From 4910bae10f057e7dbf75682062ea2c91ef608d7d Mon Sep 17 00:00:00 2001 From: rzrymiak <106121613+rzrymiak@users.noreply.github.com> Date: Mon, 18 Jul 2022 17:00:16 -0700 Subject: [PATCH 0001/1432] Update README.md Minor grammatical edit in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f23873284f..3e73934b59 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ Once you've completed Rustlings, put your new knowledge to good use! Continue pr ## Uninstalling Rustlings -If you want to remove Rustlings from your system, there's two steps. First, you'll need to remove the exercises folder that the install script created +If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created for you: ```bash From 5ff23a286185daef544c72b00f90567183898929 Mon Sep 17 00:00:00 2001 From: Mikael Frosthage Date: Tue, 26 Jul 2022 22:02:50 +0200 Subject: [PATCH 0002/1432] Improve hint for as_ref_mut --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index f07b9267c9..c3b0ca844c 100644 --- a/info.toml +++ b/info.toml @@ -1135,4 +1135,4 @@ name = "as_ref_mut" path = "exercises/conversions/as_ref_mut.rs" mode = "test" hint = """ -Add AsRef as a trait bound to the functions.""" +Add AsRef or AsMut as a trait bound to the functions.""" From 94bdb708fee17b6a87097cfbbcd697322c7a2141 Mon Sep 17 00:00:00 2001 From: Magnus Markling Date: Sun, 28 Aug 2022 15:10:29 +0200 Subject: [PATCH 0003/1432] Add quotes --- exercises/lifetimes/lifetimes1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/lifetimes/lifetimes1.rs b/exercises/lifetimes/lifetimes1.rs index 58e995c65f..0236470dbd 100644 --- a/exercises/lifetimes/lifetimes1.rs +++ b/exercises/lifetimes/lifetimes1.rs @@ -22,5 +22,5 @@ fn main() { let string2 = "xyz"; let result = longest(string1.as_str(), string2); - println!("The longest string is {}", result); + println!("The longest string is '{}'", result); } From 291da61fda51c4bbb43e48997ede007b9f3a0251 Mon Sep 17 00:00:00 2001 From: Magnus Markling Date: Sun, 28 Aug 2022 15:12:15 +0200 Subject: [PATCH 0004/1432] Add quotes --- exercises/lifetimes/lifetimes2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/lifetimes/lifetimes2.rs b/exercises/lifetimes/lifetimes2.rs index c73a28adde..b48feabcfa 100644 --- a/exercises/lifetimes/lifetimes2.rs +++ b/exercises/lifetimes/lifetimes2.rs @@ -23,5 +23,5 @@ fn main() { let string2 = String::from("xyz"); result = longest(string1.as_str(), string2.as_str()); } - println!("The longest string is {}", result); + println!("The longest string is '{}'", result); } From c97e659d782fbd26337f52941aff9e0a66fabce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?= Date: Mon, 5 Sep 2022 10:18:45 -0500 Subject: [PATCH 0005/1432] typox fix --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 060643d7ae..6feeb18690 100644 --- a/info.toml +++ b/info.toml @@ -943,7 +943,7 @@ After using drop() to move the Planets out of scope individually, the reference In the end the sun only has one reference again, to itself. See more at: https://doc.rust-lang.org/book/ch15-04-rc.html -* Unforunately Pluto is no longer considered a planet :( +* Unfortunately Pluto is no longer considered a planet :( """ [[exercises]] From 96552e07c20ef9ad6b2d64d6f853cb8ff1da1b01 Mon Sep 17 00:00:00 2001 From: liv Date: Tue, 6 Sep 2022 12:09:43 +0200 Subject: [PATCH 0006/1432] fix(quiz1): correct explanation once again --- exercises/quiz1.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index d1e76e598a..3c2f87895f 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -4,9 +4,11 @@ // - Functions // - If -// Mary is buying apples. One apple usually costs 2 Rustbucks, but if you buy -// 40 or more at once, each apple only costs 1! Write a function that calculates -// the price of an order of apples given the quantity bought. No hints this time! +// Mary is buying apples. The price of an apple is calculated as follows: +// - An apple costs 2 rustbucks. +// - 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! // I AM NOT DONE From 1d5700e58a6f55a5549dc217aa9c4d071cdf8d2e Mon Sep 17 00:00:00 2001 From: liv Date: Tue, 6 Sep 2022 12:10:53 +0200 Subject: [PATCH 0007/1432] fix(quiz1): add fourth assert --- exercises/quiz1.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 3c2f87895f..dbb5cdc9a1 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -20,9 +20,11 @@ fn verify_test() { let price1 = calculate_price_of_apples(35); let price2 = calculate_price_of_apples(40); - let price3 = calculate_price_of_apples(65); + let price3 = calculate_price_of_apples(41); + let price4 = calculate_price_of_apples(65); assert_eq!(70, price1); assert_eq!(80, price2); - assert_eq!(65, price3); + assert_eq!(41, price3); + assert_eq!(65, price4); } From c9c743967f1a8b44407d44a86ffa0421d485dd39 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:13:26 +0000 Subject: [PATCH 0008/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 8edf09bfb2..5dcace66c7 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -219,6 +219,9 @@ authors.
Gabriel Bianconi

πŸ–‹
Kody Low

πŸ–‹ + +
rzrymiak

πŸ–‹ + From f437f8e9eaa991063cf46a195a1319c11b15b412 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:13:27 +0000 Subject: [PATCH 0009/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f2033dcde9..2da6a47fef 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1542,6 +1542,15 @@ "contributions": [ "content" ] + }, + { + "login": "rzrymiak", + "name": "rzrymiak", + "avatar_url": "https://avatars.githubusercontent.com/u/106121613?v=4", + "profile": "https://github.com/rzrymiak", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 900d58d05481847bb9968a612c35504533d20648 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:14:05 +0000 Subject: [PATCH 0010/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 5dcace66c7..e038de0b5e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -221,6 +221,7 @@ authors.
rzrymiak

πŸ–‹ +
Miguel Raz GuzmΓ‘n Macedo

πŸ–‹ From 742200d14bb3552fc595aed8d2fdf80e2c6b2084 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:14:06 +0000 Subject: [PATCH 0011/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2da6a47fef..c7789419b5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1551,6 +1551,15 @@ "contributions": [ "content" ] + }, + { + "login": "miguelraz", + "name": "Miguel Raz GuzmΓ‘n Macedo", + "avatar_url": "https://avatars.githubusercontent.com/u/13056181?v=4", + "profile": "https://github.com/miguelraz", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 7b5ef323c5d3ecb6cd16ff7172e45dae0c5d2360 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:17:07 +0000 Subject: [PATCH 0012/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index e038de0b5e..29d330d283 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -222,6 +222,7 @@ authors.
rzrymiak

πŸ–‹
Miguel Raz GuzmΓ‘n Macedo

πŸ–‹ +
Magnus Markling

πŸ–‹ From 50323a3977f5e8f9a99da9413413514d077863ff Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:17:08 +0000 Subject: [PATCH 0013/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index c7789419b5..0917089a32 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1560,6 +1560,15 @@ "contributions": [ "content" ] + }, + { + "login": "memark", + "name": "Magnus Markling", + "avatar_url": "https://avatars.githubusercontent.com/u/318504?v=4", + "profile": "https://github.com/memark", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c923e7af73a91970d2e63e03babbca9cc0817551 Mon Sep 17 00:00:00 2001 From: mokou Date: Tue, 6 Sep 2022 12:22:17 +0200 Subject: [PATCH 0014/1432] chore: release 5.2.1 --- CHANGELOG.md | 14 ++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 4 ++-- src/main.rs | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b15cf996a..9925e3dbf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ + +## 5.2.1 (2022-09-06) + +#### Fixed + +- **quiz1**: Reworded the comment to actually reflect what's going on in the tests. + Also added another assert just to make sure. +- **rc1**: Fixed a typo in the hint. +- **lifetimes**: Add quotes to the `println!` output, for readability. + +#### Housekeeping + +- Fixed a typo in README.md + ## 5.2.0 (2022-08-27) diff --git a/Cargo.lock b/Cargo.lock index 2370e8dcda..948d0f42e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -459,7 +459,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rustlings" -version = "5.1.1" +version = "5.2.1" dependencies = [ "argh", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 25cd7bfefe..dadded6877 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustlings" -version = "5.2.0" +version = "5.2.1" authors = ["Liv ", "Carol (Nichols || Goulding) "] edition = "2021" diff --git a/README.md b/README.md index 39a31c961a..9b619d6f1b 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.1.1) -git clone -b 5.1.1 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.2.1) +git clone -b 5.2.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/src/main.rs b/src/main.rs index 8eebc08660..cd79d9ffca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ mod run; mod verify; // In sync with crate version -const VERSION: &str = "5.2.0"; +const VERSION: &str = "5.2.1"; #[derive(FromArgs, PartialEq, Debug)] /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code From 34ed2358855e187990636621fdd6e435308532e3 Mon Sep 17 00:00:00 2001 From: Arkid <39987510+aaarkid@users.noreply.github.com> Date: Fri, 9 Sep 2022 02:40:22 +0200 Subject: [PATCH 0015/1432] fix: Add a deref in the test code It's virtually impossible to write a the `num_sq` function to take the Box since it doesn't implement `MulAssign`. --- exercises/conversions/as_ref_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index 9f47973990..dafbdb981a 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -54,7 +54,7 @@ mod tests { #[test] fn mult_box() { let mut num: Box = Box::new(3); - num_sq(&mut num); + num_sq(&mut *num); assert_eq!(*num, 9); } } From 68fe97bbc23d33b7cb2be5439711029c2e4826c0 Mon Sep 17 00:00:00 2001 From: Tiago De Gaspari <3237254+gasparitiago@users.noreply.github.com> Date: Mon, 12 Sep 2022 10:53:59 -0300 Subject: [PATCH 0016/1432] fix(quiz2): fix comment regarding hints Change the comment on quiz2.rs, since there are no hints. --- exercises/quiz2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index d8fa954aa2..606d3c7096 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -16,7 +16,7 @@ // - 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. -// Execute `rustlings hint quiz2` or use the `hint` watch subcommand for a hint. +// No hints this time! // I AM NOT DONE From d3c0c18946b56223d5282587c3adb51959f14588 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 08:11:12 +0000 Subject: [PATCH 0017/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 433 +++++++++++++++++++++++++++-------------------------- 1 file changed, 218 insertions(+), 215 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 29d330d283..43e11b1ac1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -9,221 +9,224 @@ authors. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Carol (Nichols || Goulding)

πŸ’» πŸ–‹

QuietMisdreavus

πŸ’» πŸ–‹

Robert M Lugg

πŸ–‹

Hynek Schlawack

πŸ’»

Katharina Fey

πŸ’»

lukabavdaz

πŸ’» πŸ–‹

Erik Vesteraas

πŸ’»

delet0r

πŸ’»

Shaun Bennett

πŸ’»

Andrew Bagshaw

πŸ’»

Kyle Isom

πŸ’»

Colin Pitrat

πŸ’»

Zac Anger

πŸ’»

Matthias Geier

πŸ’»

Chris Pearce

πŸ’»

Yvan Sraka

πŸ’»

Denys Smirnov

πŸ’»

eddyp

πŸ’»

Brian Kung

πŸ’» πŸ–‹

Russell

πŸ’»

Dan Wilhelm

πŸ“–

Jesse

πŸ’» πŸ–‹

Fredrik JambrΓ©n

πŸ’»

Pete McFarlane

πŸ–‹

nkanderson

πŸ’» πŸ–‹

Ajax M

πŸ“–

Dylan Nugent

πŸ–‹

vyaslav

πŸ’» πŸ–‹

George

πŸ’»

Thomas Holloway

πŸ’» πŸ–‹

Jubilee

πŸ’»

WofWca

πŸ’»

Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧

Jens

πŸ“–

Rahat Ahmed

πŸ“–

Abdou Seck

πŸ’» πŸ–‹ πŸ‘€

Katie

πŸ’»

Socrates

πŸ“–

gnodarse

πŸ–‹

Harrison Metzger

πŸ’»

Torben Jonas

πŸ’» πŸ–‹

Paul Bissex

πŸ“–

Steven Mann

πŸ’» πŸ–‹

Mario Reder

πŸ’» πŸ–‹

skim

πŸ’»

Sanjay K

πŸ’» πŸ–‹

Rohan Jain

πŸ’»

Said Aspen

πŸ’» πŸ–‹

Ufuk Celebi

πŸ’»

lebedevsergey

πŸ“–

Aleksei Trifonov

πŸ–‹

Darren Meehan

πŸ–‹

Jihchi Lee

πŸ–‹

Christofer Bertonha

πŸ–‹

Vivek Bharath Akupatni

πŸ’» ⚠️

DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹

Rob Story

πŸ’»

Siobhan Jacobson

πŸ’»

Evan Carroll

πŸ–‹

Jawaad Mahmood

πŸ–‹

Gaurang Tandon

πŸ–‹

Stefan Kupresak

πŸ–‹

Greg Leonard

πŸ–‹

Ryan McQuen

πŸ’»

Annika

πŸ‘€

Axel Viala

πŸ’»

Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»

Caleb Webber

🚧

Peter N

🚧

seancad

🚧

Will Hayworth

πŸ–‹

Christian Zeller

πŸ–‹

Jean-Francois Chevrette

πŸ–‹ πŸ’»

John Baber-Lucero

πŸ–‹

Tal

πŸ–‹

apogeeoak

πŸ–‹ πŸ’»

Larry Garfield

πŸ–‹

circumspect

πŸ–‹

Cyrus Wyett

πŸ–‹

cadolphs

πŸ’»

Pascal H.

πŸ–‹

Rod Elias

πŸ–‹

Matt Lebl

πŸ’»

Ignacio Le Fluk

πŸ–‹

Taylor Yu

πŸ’» πŸ–‹

Patrick Hintermayer

πŸ’»

Pete Pavlovski

πŸ–‹

k12ish

πŸ–‹

Shao Yang Hong

πŸ–‹

Brandon Macer

πŸ–‹

Stoian Dan

πŸ–‹

Pi Delport

πŸ–‹

Sateesh

πŸ’» πŸ–‹

ZC

πŸ–‹

hyperparabolic

πŸ’»

arlecchino

πŸ“–

Richthofen

πŸ’»

Ivan Nerazumov

πŸ“–

lauralindzey

πŸ“–

Rakshit Sinha

πŸ–‹

Damian

πŸ–‹

Ben Armstead

πŸ’»

anuk909

πŸ–‹ πŸ’»

granddaifuku

πŸ–‹

Weilet

πŸ–‹

LIU JIE

πŸ–‹

Antoine BΓΌsch

πŸ’»

frogtd

πŸ–‹

Zhenghao Lu

πŸ–‹

Fredrik Enestad

πŸ–‹

xuesong

πŸ–‹

Michael Walsh

πŸ’»

alirezaghey

πŸ–‹

Franklin van Nes

πŸ’»

nekonako

πŸ’»

ZX

πŸ–‹

Yang Wen

πŸ–‹

Brandon High

πŸ“–

x-hgg-x

πŸ’»

Kisaragi

πŸ“–

Lucas Aries

πŸ–‹

ragreenburg

πŸ–‹

stevenfukase

πŸ–‹

J-S-Kim

πŸ–‹

Fointard

πŸ–‹

Ryan Lowe

πŸ’»

cui fliter

πŸ–‹

Ron Lusk

πŸ–‹

Bryan Lee

πŸ–‹

Nandaja Varma

πŸ“–

pwygab

πŸ’»

Lucas Grigolon Varela

πŸ–‹

Bufo

πŸ–‹

Jack Clayton

πŸ’»

Konstantin

πŸ–‹

0pling

πŸ–‹

KatanaFluorescent

πŸ’»

Drew Morris

πŸ’»

camperdue42

πŸ–‹

YsuOS

πŸ–‹

Steven Nguyen

πŸ–‹

nacairns1

πŸ–‹

Paulo Gabriel Justino Bezerra

πŸ–‹

Jason

πŸ–‹

exdx

πŸ–‹

James Zow

πŸ–‹

James Bromley

πŸ–‹

swhiteCQC

πŸ–‹

Neil Pate

πŸ–‹

wojexe

πŸ–‹

Mattia Schiavon

πŸ–‹

Eric Jolibois

πŸ–‹

Edwin Chang

πŸ–‹

Saikat Das

πŸ–‹

Jeremy Goh

πŸ–‹

Lioness100

πŸ–‹

Tristan Nicholls

πŸ–‹

Claire

πŸ–‹

Maurice Van Wassenhove

πŸ–‹

John Mendelewski

πŸ’»

Brian Fakhoury

πŸ–‹

Markus Boehme

πŸ’»

Nico Vromans

πŸ–‹

vostok92

πŸ–‹

Magnus RΓΈdseth

πŸ–‹

rubiesonthesky

πŸ–‹

Gabriel Bianconi

πŸ–‹

Kody Low

πŸ–‹

rzrymiak

πŸ–‹

Miguel Raz GuzmΓ‘n Macedo

πŸ–‹

Magnus Markling

πŸ–‹

Carol (Nichols || Goulding)

πŸ’» πŸ–‹

QuietMisdreavus

πŸ’» πŸ–‹

Robert M Lugg

πŸ–‹

Hynek Schlawack

πŸ’»

Katharina Fey

πŸ’»

lukabavdaz

πŸ’» πŸ–‹

Erik Vesteraas

πŸ’»

delet0r

πŸ’»

Shaun Bennett

πŸ’»

Andrew Bagshaw

πŸ’»

Kyle Isom

πŸ’»

Colin Pitrat

πŸ’»

Zac Anger

πŸ’»

Matthias Geier

πŸ’»

Chris Pearce

πŸ’»

Yvan Sraka

πŸ’»

Denys Smirnov

πŸ’»

eddyp

πŸ’»

Brian Kung

πŸ’» πŸ–‹

Russell

πŸ’»

Dan Wilhelm

πŸ“–

Jesse

πŸ’» πŸ–‹

Fredrik JambrΓ©n

πŸ’»

Pete McFarlane

πŸ–‹

nkanderson

πŸ’» πŸ–‹

Ajax M

πŸ“–

Dylan Nugent

πŸ–‹

vyaslav

πŸ’» πŸ–‹

George

πŸ’»

Thomas Holloway

πŸ’» πŸ–‹

Jubilee

πŸ’»

WofWca

πŸ’»

Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧

Jens

πŸ“–

Rahat Ahmed

πŸ“–

Abdou Seck

πŸ’» πŸ–‹ πŸ‘€

Katie

πŸ’»

Socrates

πŸ“–

gnodarse

πŸ–‹

Harrison Metzger

πŸ’»

Torben Jonas

πŸ’» πŸ–‹

Paul Bissex

πŸ“–

Steven Mann

πŸ’» πŸ–‹

Mario Reder

πŸ’» πŸ–‹

skim

πŸ’»

Sanjay K

πŸ’» πŸ–‹

Rohan Jain

πŸ’»

Said Aspen

πŸ’» πŸ–‹

Ufuk Celebi

πŸ’»

lebedevsergey

πŸ“–

Aleksei Trifonov

πŸ–‹

Darren Meehan

πŸ–‹

Jihchi Lee

πŸ–‹

Christofer Bertonha

πŸ–‹

Vivek Bharath Akupatni

πŸ’» ⚠️

DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹

Rob Story

πŸ’»

Siobhan Jacobson

πŸ’»

Evan Carroll

πŸ–‹

Jawaad Mahmood

πŸ–‹

Gaurang Tandon

πŸ–‹

Stefan Kupresak

πŸ–‹

Greg Leonard

πŸ–‹

Ryan McQuen

πŸ’»

Annika

πŸ‘€

Axel Viala

πŸ’»

Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»

Caleb Webber

🚧

Peter N

🚧

seancad

🚧

Will Hayworth

πŸ–‹

Christian Zeller

πŸ–‹

Jean-Francois Chevrette

πŸ–‹ πŸ’»

John Baber-Lucero

πŸ–‹

Tal

πŸ–‹

apogeeoak

πŸ–‹ πŸ’»

Larry Garfield

πŸ–‹

circumspect

πŸ–‹

Cyrus Wyett

πŸ–‹

cadolphs

πŸ’»

Pascal H.

πŸ–‹

Rod Elias

πŸ–‹

Matt Lebl

πŸ’»

Ignacio Le Fluk

πŸ–‹

Taylor Yu

πŸ’» πŸ–‹

Patrick Hintermayer

πŸ’»

Pete Pavlovski

πŸ–‹

k12ish

πŸ–‹

Shao Yang Hong

πŸ–‹

Brandon Macer

πŸ–‹

Stoian Dan

πŸ–‹

Pi Delport

πŸ–‹

Sateesh

πŸ’» πŸ–‹

ZC

πŸ–‹

hyperparabolic

πŸ’»

arlecchino

πŸ“–

Richthofen

πŸ’»

Ivan Nerazumov

πŸ“–

lauralindzey

πŸ“–

Rakshit Sinha

πŸ–‹

Damian

πŸ–‹

Ben Armstead

πŸ’»

anuk909

πŸ–‹ πŸ’»

granddaifuku

πŸ–‹

Weilet

πŸ–‹

LIU JIE

πŸ–‹

Antoine BΓΌsch

πŸ’»

frogtd

πŸ–‹

Zhenghao Lu

πŸ–‹

Fredrik Enestad

πŸ–‹

xuesong

πŸ–‹

Michael Walsh

πŸ’»

alirezaghey

πŸ–‹

Franklin van Nes

πŸ’»

nekonako

πŸ’»

ZX

πŸ–‹

Yang Wen

πŸ–‹

Brandon High

πŸ“–

x-hgg-x

πŸ’»

Kisaragi

πŸ“–

Lucas Aries

πŸ–‹

ragreenburg

πŸ–‹

stevenfukase

πŸ–‹

J-S-Kim

πŸ–‹

Fointard

πŸ–‹

Ryan Lowe

πŸ’»

cui fliter

πŸ–‹

Ron Lusk

πŸ–‹

Bryan Lee

πŸ–‹

Nandaja Varma

πŸ“–

pwygab

πŸ’»

Lucas Grigolon Varela

πŸ–‹

Bufo

πŸ–‹

Jack Clayton

πŸ’»

Konstantin

πŸ–‹

0pling

πŸ–‹

KatanaFluorescent

πŸ’»

Drew Morris

πŸ’»

camperdue42

πŸ–‹

YsuOS

πŸ–‹

Steven Nguyen

πŸ–‹

nacairns1

πŸ–‹

Paulo Gabriel Justino Bezerra

πŸ–‹

Jason

πŸ–‹

exdx

πŸ–‹

James Zow

πŸ–‹

James Bromley

πŸ–‹

swhiteCQC

πŸ–‹

Neil Pate

πŸ–‹

wojexe

πŸ–‹

Mattia Schiavon

πŸ–‹

Eric Jolibois

πŸ–‹

Edwin Chang

πŸ–‹

Saikat Das

πŸ–‹

Jeremy Goh

πŸ–‹

Lioness100

πŸ–‹

Tristan Nicholls

πŸ–‹

Claire

πŸ–‹

Maurice Van Wassenhove

πŸ–‹

John Mendelewski

πŸ’»

Brian Fakhoury

πŸ–‹

Markus Boehme

πŸ’»

Nico Vromans

πŸ–‹

vostok92

πŸ–‹

Magnus RΓΈdseth

πŸ–‹

rubiesonthesky

πŸ–‹

Gabriel Bianconi

πŸ–‹

Kody Low

πŸ–‹

rzrymiak

πŸ–‹

Miguel Raz GuzmΓ‘n Macedo

πŸ–‹

Magnus Markling

πŸ–‹

Tiago De Gaspari

πŸ–‹
From 072847943af3884ce0ae21dc594649a86be54e2e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 08:11:13 +0000 Subject: [PATCH 0018/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0917089a32..a5a6dd605e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1569,6 +1569,15 @@ "contributions": [ "content" ] + }, + { + "login": "gasparitiago", + "name": "Tiago De Gaspari", + "avatar_url": "https://avatars.githubusercontent.com/u/3237254?v=4", + "profile": "https://github.com/gasparitiago", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, @@ -1576,5 +1585,6 @@ "projectOwner": "rust-lang", "repoType": "github", "repoHost": "https://github.com", - "skipCi": true + "skipCi": true, + "commitConvention": "angular" } From 3028d1395cfa1bc2dbf49f7678d8f083e9042a56 Mon Sep 17 00:00:00 2001 From: skaunov <65976143+skaunov@users.noreply.github.com> Date: Tue, 13 Sep 2022 16:33:03 +0300 Subject: [PATCH 0019/1432] Add link to `std` in `strings3` hint Seems like it's the first place, where `std` is introduced in Rustlings, and it's a good place to facilitate docs discovery for user. As _the book_ seems to have no Rustlings-sized solutions for this exercise. --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 6feeb18690..e80d8a9cf7 100644 --- a/info.toml +++ b/info.toml @@ -449,7 +449,7 @@ path = "exercises/strings/strings3.rs" mode = "test" hint = """ There's tons of useful standard library functions for strings. Let's try and use some of -them! +them: ! For the compose_me method: You can either use the `format!` macro, or convert the string slice into an owned string, which you can then freely extend.""" From 3375b67b2b72a25960aa9e37612f8f1d3d2c05b2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 15 Sep 2022 09:29:14 +0000 Subject: [PATCH 0020/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 43e11b1ac1..9f63f5dd6c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -225,6 +225,7 @@ authors.
Miguel Raz GuzmΓ‘n Macedo

πŸ–‹
Magnus Markling

πŸ–‹
Tiago De Gaspari

πŸ–‹ +
skaunov

πŸ–‹ From 37a734421992deab8e15286245e4429919d3a93d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 15 Sep 2022 09:29:15 +0000 Subject: [PATCH 0021/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index a5a6dd605e..4fd4b1d5be 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1578,6 +1578,15 @@ "contributions": [ "content" ] + }, + { + "login": "skaunov", + "name": "skaunov", + "avatar_url": "https://avatars.githubusercontent.com/u/65976143?v=4", + "profile": "https://github.com/skaunov", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c0b3194a55da75c6404944e01f032c2a92ea5b0c Mon Sep 17 00:00:00 2001 From: skaunov <65976143+skaunov@users.noreply.github.com> Date: Sat, 17 Sep 2022 00:26:05 +0300 Subject: [PATCH 0022/1432] Correct a link in `threads1` `hint` It didn't work without last character --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index e80d8a9cf7..263b23eb71 100644 --- a/info.toml +++ b/info.toml @@ -969,7 +969,7 @@ https://doc.rust-lang.org/std/thread/fn.spawn.html A challenge with multi-threaded applications is that the main thread can finish before the spawned threads are completed. -https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handle +https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles Collect the JoinHandles and wait for them to finish. https://doc.rust-lang.org/std/thread/struct.JoinHandle.html From efdebd48b6239b29d69d684fae575ca738cacf41 Mon Sep 17 00:00:00 2001 From: Cal Jacobson Date: Sun, 25 Sep 2022 15:22:15 -0500 Subject: [PATCH 0023/1432] improve hint for iterators1 --- info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/info.toml b/info.toml index e80d8a9cf7..acc9fc3e26 100644 --- a/info.toml +++ b/info.toml @@ -818,9 +818,9 @@ Step 1: We need to apply something to the collection `my_fav_fruits` before we start to go through it. What could that be? Take a look at the struct definition for a vector for inspiration: https://doc.rust-lang.org/std/vec/struct.Vec.html. -Step 2 & step 2.1: +Step 2 & step 3: Very similar to the lines above and below. You've got this! -Step 3: +Step 4: An iterator goes through all elements in a collection, but what if we've run out of elements? What should we expect here? If you're stuck, take a look at https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. From 1660f1647eb16692f5dd1367395416c9b261848d Mon Sep 17 00:00:00 2001 From: Tostapunk Date: Wed, 28 Sep 2022 23:40:00 +0200 Subject: [PATCH 0024/1432] fix: Fix typo in errors5 hint fixes #1205 --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index e80d8a9cf7..ecf307d7c8 100644 --- a/info.toml +++ b/info.toml @@ -627,7 +627,7 @@ propagated using `?` operators. How do we declare a return type from `main()` th Under the hood, the `?` operator calls `From::from` on the error value to convert it to a boxed trait object, a `Box`. This boxed trait object is polymorphic, and since all -errors implement the `error:Error` trait, we can capture lots of different errors in one "Box" +errors implement the `error::Error` trait, we can capture lots of different errors in one "Box" object. Check out this section of the book: From 58cba835f2d79665a11017483d5df7c62c5c1607 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 08:45:06 +0000 Subject: [PATCH 0025/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 434 +++++++++++++++++++++++++++-------------------------- 1 file changed, 219 insertions(+), 215 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 9f63f5dd6c..bfc8b99a68 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -11,223 +11,227 @@ authors. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Carol (Nichols || Goulding)

πŸ’» πŸ–‹

QuietMisdreavus

πŸ’» πŸ–‹

Robert M Lugg

πŸ–‹

Hynek Schlawack

πŸ’»

Katharina Fey

πŸ’»

lukabavdaz

πŸ’» πŸ–‹

Erik Vesteraas

πŸ’»

delet0r

πŸ’»

Shaun Bennett

πŸ’»

Andrew Bagshaw

πŸ’»

Kyle Isom

πŸ’»

Colin Pitrat

πŸ’»

Zac Anger

πŸ’»

Matthias Geier

πŸ’»

Chris Pearce

πŸ’»

Yvan Sraka

πŸ’»

Denys Smirnov

πŸ’»

eddyp

πŸ’»

Brian Kung

πŸ’» πŸ–‹

Russell

πŸ’»

Dan Wilhelm

πŸ“–

Jesse

πŸ’» πŸ–‹

Fredrik JambrΓ©n

πŸ’»

Pete McFarlane

πŸ–‹

nkanderson

πŸ’» πŸ–‹

Ajax M

πŸ“–

Dylan Nugent

πŸ–‹

vyaslav

πŸ’» πŸ–‹

George

πŸ’»

Thomas Holloway

πŸ’» πŸ–‹

Jubilee

πŸ’»

WofWca

πŸ’»

Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧

Jens

πŸ“–

Rahat Ahmed

πŸ“–

Abdou Seck

πŸ’» πŸ–‹ πŸ‘€

Katie

πŸ’»

Socrates

πŸ“–

gnodarse

πŸ–‹

Harrison Metzger

πŸ’»

Torben Jonas

πŸ’» πŸ–‹

Paul Bissex

πŸ“–

Steven Mann

πŸ’» πŸ–‹

Mario Reder

πŸ’» πŸ–‹

skim

πŸ’»

Sanjay K

πŸ’» πŸ–‹

Rohan Jain

πŸ’»

Said Aspen

πŸ’» πŸ–‹

Ufuk Celebi

πŸ’»

lebedevsergey

πŸ“–

Aleksei Trifonov

πŸ–‹

Darren Meehan

πŸ–‹

Jihchi Lee

πŸ–‹

Christofer Bertonha

πŸ–‹

Vivek Bharath Akupatni

πŸ’» ⚠️

DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹

Rob Story

πŸ’»

Siobhan Jacobson

πŸ’»

Evan Carroll

πŸ–‹

Jawaad Mahmood

πŸ–‹

Gaurang Tandon

πŸ–‹

Stefan Kupresak

πŸ–‹

Greg Leonard

πŸ–‹

Ryan McQuen

πŸ’»

Annika

πŸ‘€

Axel Viala

πŸ’»

Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»

Caleb Webber

🚧

Peter N

🚧

seancad

🚧

Will Hayworth

πŸ–‹

Christian Zeller

πŸ–‹

Jean-Francois Chevrette

πŸ–‹ πŸ’»

John Baber-Lucero

πŸ–‹

Tal

πŸ–‹

apogeeoak

πŸ–‹ πŸ’»

Larry Garfield

πŸ–‹

circumspect

πŸ–‹

Cyrus Wyett

πŸ–‹

cadolphs

πŸ’»

Pascal H.

πŸ–‹

Rod Elias

πŸ–‹

Matt Lebl

πŸ’»

Ignacio Le Fluk

πŸ–‹

Taylor Yu

πŸ’» πŸ–‹

Patrick Hintermayer

πŸ’»

Pete Pavlovski

πŸ–‹

k12ish

πŸ–‹

Shao Yang Hong

πŸ–‹

Brandon Macer

πŸ–‹

Stoian Dan

πŸ–‹

Pi Delport

πŸ–‹

Sateesh

πŸ’» πŸ–‹

ZC

πŸ–‹

hyperparabolic

πŸ’»

arlecchino

πŸ“–

Richthofen

πŸ’»

Ivan Nerazumov

πŸ“–

lauralindzey

πŸ“–

Rakshit Sinha

πŸ–‹

Damian

πŸ–‹

Ben Armstead

πŸ’»

anuk909

πŸ–‹ πŸ’»

granddaifuku

πŸ–‹

Weilet

πŸ–‹

LIU JIE

πŸ–‹

Antoine BΓΌsch

πŸ’»

frogtd

πŸ–‹

Zhenghao Lu

πŸ–‹

Fredrik Enestad

πŸ–‹

xuesong

πŸ–‹

Michael Walsh

πŸ’»

alirezaghey

πŸ–‹

Franklin van Nes

πŸ’»

nekonako

πŸ’»

ZX

πŸ–‹

Yang Wen

πŸ–‹

Brandon High

πŸ“–

x-hgg-x

πŸ’»

Kisaragi

πŸ“–

Lucas Aries

πŸ–‹

ragreenburg

πŸ–‹

stevenfukase

πŸ–‹

J-S-Kim

πŸ–‹

Fointard

πŸ–‹

Ryan Lowe

πŸ’»

cui fliter

πŸ–‹

Ron Lusk

πŸ–‹

Bryan Lee

πŸ–‹

Nandaja Varma

πŸ“–

pwygab

πŸ’»

Lucas Grigolon Varela

πŸ–‹

Bufo

πŸ–‹

Jack Clayton

πŸ’»

Konstantin

πŸ–‹

0pling

πŸ–‹

KatanaFluorescent

πŸ’»

Drew Morris

πŸ’»

camperdue42

πŸ–‹

YsuOS

πŸ–‹

Steven Nguyen

πŸ–‹

nacairns1

πŸ–‹

Paulo Gabriel Justino Bezerra

πŸ–‹

Jason

πŸ–‹

exdx

πŸ–‹

James Zow

πŸ–‹

James Bromley

πŸ–‹

swhiteCQC

πŸ–‹

Neil Pate

πŸ–‹

wojexe

πŸ–‹

Mattia Schiavon

πŸ–‹

Eric Jolibois

πŸ–‹

Edwin Chang

πŸ–‹

Saikat Das

πŸ–‹

Jeremy Goh

πŸ–‹

Lioness100

πŸ–‹

Tristan Nicholls

πŸ–‹

Claire

πŸ–‹

Maurice Van Wassenhove

πŸ–‹

John Mendelewski

πŸ’»

Brian Fakhoury

πŸ–‹

Markus Boehme

πŸ’»

Nico Vromans

πŸ–‹

vostok92

πŸ–‹

Magnus RΓΈdseth

πŸ–‹

rubiesonthesky

πŸ–‹

Gabriel Bianconi

πŸ–‹

Kody Low

πŸ–‹

rzrymiak

πŸ–‹

Miguel Raz GuzmΓ‘n Macedo

πŸ–‹

Magnus Markling

πŸ–‹

Tiago De Gaspari

πŸ–‹

skaunov

πŸ–‹
Carol (Nichols || Goulding)
Carol (Nichols || Goulding)

πŸ’» πŸ–‹
QuietMisdreavus
QuietMisdreavus

πŸ’» πŸ–‹
Robert M Lugg
Robert M Lugg

πŸ–‹
Hynek Schlawack
Hynek Schlawack

πŸ’»
Katharina Fey
Katharina Fey

πŸ’»
lukabavdaz
lukabavdaz

πŸ’» πŸ–‹
Erik Vesteraas
Erik Vesteraas

πŸ’»
delet0r
delet0r

πŸ’»
Shaun Bennett
Shaun Bennett

πŸ’»
Andrew Bagshaw
Andrew Bagshaw

πŸ’»
Kyle Isom
Kyle Isom

πŸ’»
Colin Pitrat
Colin Pitrat

πŸ’»
Zac Anger
Zac Anger

πŸ’»
Matthias Geier
Matthias Geier

πŸ’»
Chris Pearce
Chris Pearce

πŸ’»
Yvan Sraka
Yvan Sraka

πŸ’»
Denys Smirnov
Denys Smirnov

πŸ’»
eddyp
eddyp

πŸ’»
Brian Kung
Brian Kung

πŸ’» πŸ–‹
Russell
Russell

πŸ’»
Dan Wilhelm
Dan Wilhelm

πŸ“–
Jesse
Jesse

πŸ’» πŸ–‹
Fredrik JambrΓ©n
Fredrik JambrΓ©n

πŸ’»
Pete McFarlane
Pete McFarlane

πŸ–‹
nkanderson
nkanderson

πŸ’» πŸ–‹
Ajax M
Ajax M

πŸ“–
Dylan Nugent
Dylan Nugent

πŸ–‹
vyaslav
vyaslav

πŸ’» πŸ–‹
George
George

πŸ’»
Thomas Holloway
Thomas Holloway

πŸ’» πŸ–‹
Jubilee
Jubilee

πŸ’»
WofWca
WofWca

πŸ’»
Roberto Vidal
Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧
Jens
Jens

πŸ“–
Rahat Ahmed
Rahat Ahmed

πŸ“–
Abdou Seck
Abdou Seck

πŸ’» πŸ–‹ πŸ‘€
Katie
Katie

πŸ’»
Socrates
Socrates

πŸ“–
gnodarse
gnodarse

πŸ–‹
Harrison Metzger
Harrison Metzger

πŸ’»
Torben Jonas
Torben Jonas

πŸ’» πŸ–‹
Paul Bissex
Paul Bissex

πŸ“–
Steven Mann
Steven Mann

πŸ’» πŸ–‹
Mario Reder
Mario Reder

πŸ’» πŸ–‹
skim
skim

πŸ’»
Sanjay K
Sanjay K

πŸ’» πŸ–‹
Rohan Jain
Rohan Jain

πŸ’»
Said Aspen
Said Aspen

πŸ’» πŸ–‹
Ufuk Celebi
Ufuk Celebi

πŸ’»
lebedevsergey
lebedevsergey

πŸ“–
Aleksei Trifonov
Aleksei Trifonov

πŸ–‹
Darren Meehan
Darren Meehan

πŸ–‹
Jihchi Lee
Jihchi Lee

πŸ–‹
Christofer Bertonha
Christofer Bertonha

πŸ–‹
Vivek Bharath Akupatni
Vivek Bharath Akupatni

πŸ’» ⚠️
DΓ­dac SementΓ© FernΓ‘ndez
DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹
Rob Story
Rob Story

πŸ’»
Siobhan Jacobson
Siobhan Jacobson

πŸ’»
Evan Carroll
Evan Carroll

πŸ–‹
Jawaad Mahmood
Jawaad Mahmood

πŸ–‹
Gaurang Tandon
Gaurang Tandon

πŸ–‹
Stefan Kupresak
Stefan Kupresak

πŸ–‹
Greg Leonard
Greg Leonard

πŸ–‹
Ryan McQuen
Ryan McQuen

πŸ’»
Annika
Annika

πŸ‘€
Axel Viala
Axel Viala

πŸ’»
Mohammed Sazid Al Rashid
Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»
Caleb Webber
Caleb Webber

🚧
Peter N
Peter N

🚧
seancad
seancad

🚧
Will Hayworth
Will Hayworth

πŸ–‹
Christian Zeller
Christian Zeller

πŸ–‹
Jean-Francois Chevrette
Jean-Francois Chevrette

πŸ–‹ πŸ’»
John Baber-Lucero
John Baber-Lucero

πŸ–‹
Tal
Tal

πŸ–‹
apogeeoak
apogeeoak

πŸ–‹ πŸ’»
Larry Garfield
Larry Garfield

πŸ–‹
circumspect
circumspect

πŸ–‹
Cyrus Wyett
Cyrus Wyett

πŸ–‹
cadolphs
cadolphs

πŸ’»
Pascal H.
Pascal H.

πŸ–‹
Rod Elias
Rod Elias

πŸ–‹
Matt Lebl
Matt Lebl

πŸ’»
Ignacio Le Fluk
Ignacio Le Fluk

πŸ–‹
Taylor Yu
Taylor Yu

πŸ’» πŸ–‹
Patrick Hintermayer
Patrick Hintermayer

πŸ’»
Pete Pavlovski
Pete Pavlovski

πŸ–‹
k12ish
k12ish

πŸ–‹
Shao Yang Hong
Shao Yang Hong

πŸ–‹
Brandon Macer
Brandon Macer

πŸ–‹
Stoian Dan
Stoian Dan

πŸ–‹
Pi Delport
Pi Delport

πŸ–‹
Sateesh
Sateesh

πŸ’» πŸ–‹
ZC
ZC

πŸ–‹
hyperparabolic
hyperparabolic

πŸ’»
arlecchino
arlecchino

πŸ“–
Richthofen
Richthofen

πŸ’»
Ivan Nerazumov
Ivan Nerazumov

πŸ“–
lauralindzey
lauralindzey

πŸ“–
Rakshit Sinha
Rakshit Sinha

πŸ–‹
Damian
Damian

πŸ–‹
Ben Armstead
Ben Armstead

πŸ’»
anuk909
anuk909

πŸ–‹ πŸ’»
granddaifuku
granddaifuku

πŸ–‹
Weilet
Weilet

πŸ–‹
LIU JIE
LIU JIE

πŸ–‹
Antoine BΓΌsch
Antoine BΓΌsch

πŸ’»
frogtd
frogtd

πŸ–‹
Zhenghao Lu
Zhenghao Lu

πŸ–‹
Fredrik Enestad
Fredrik Enestad

πŸ–‹
xuesong
xuesong

πŸ–‹
Michael Walsh
Michael Walsh

πŸ’»
alirezaghey
alirezaghey

πŸ–‹
Franklin van Nes
Franklin van Nes

πŸ’»
nekonako
nekonako

πŸ’»
ZX
ZX

πŸ–‹
Yang Wen
Yang Wen

πŸ–‹
Brandon High
Brandon High

πŸ“–
x-hgg-x
x-hgg-x

πŸ’»
Kisaragi
Kisaragi

πŸ“–
Lucas Aries
Lucas Aries

πŸ–‹
ragreenburg
ragreenburg

πŸ–‹
stevenfukase
stevenfukase

πŸ–‹
J-S-Kim
J-S-Kim

πŸ–‹
Fointard
Fointard

πŸ–‹
Ryan Lowe
Ryan Lowe

πŸ’»
cui fliter
cui fliter

πŸ–‹
Ron Lusk
Ron Lusk

πŸ–‹
Bryan Lee
Bryan Lee

πŸ–‹
Nandaja Varma
Nandaja Varma

πŸ“–
pwygab
pwygab

πŸ’»
Lucas Grigolon Varela
Lucas Grigolon Varela

πŸ–‹
Bufo
Bufo

πŸ–‹
Jack Clayton
Jack Clayton

πŸ’»
Konstantin
Konstantin

πŸ–‹
0pling
0pling

πŸ–‹
KatanaFluorescent
KatanaFluorescent

πŸ’»
Drew Morris
Drew Morris

πŸ’»
camperdue42
camperdue42

πŸ–‹
YsuOS
YsuOS

πŸ–‹
Steven Nguyen
Steven Nguyen

πŸ–‹
nacairns1
nacairns1

πŸ–‹
Paulo Gabriel Justino Bezerra
Paulo Gabriel Justino Bezerra

πŸ–‹
Jason
Jason

πŸ–‹
exdx
exdx

πŸ–‹
James Zow
James Zow

πŸ–‹
James Bromley
James Bromley

πŸ–‹
swhiteCQC
swhiteCQC

πŸ–‹
Neil Pate
Neil Pate

πŸ–‹
wojexe
wojexe

πŸ–‹
Mattia Schiavon
Mattia Schiavon

πŸ–‹
Eric Jolibois
Eric Jolibois

πŸ–‹
Edwin Chang
Edwin Chang

πŸ–‹
Saikat Das
Saikat Das

πŸ–‹
Jeremy Goh
Jeremy Goh

πŸ–‹
Lioness100
Lioness100

πŸ–‹
Tristan Nicholls
Tristan Nicholls

πŸ–‹
Claire
Claire

πŸ–‹
Maurice Van Wassenhove
Maurice Van Wassenhove

πŸ–‹
John Mendelewski
John Mendelewski

πŸ’»
Brian Fakhoury
Brian Fakhoury

πŸ–‹
Markus Boehme
Markus Boehme

πŸ’»
Nico Vromans
Nico Vromans

πŸ–‹
vostok92
vostok92

πŸ–‹
Magnus RΓΈdseth
Magnus RΓΈdseth

πŸ–‹
rubiesonthesky
rubiesonthesky

πŸ–‹
Gabriel Bianconi
Gabriel Bianconi

πŸ–‹
Kody Low
Kody Low

πŸ–‹
rzrymiak
rzrymiak

πŸ–‹
Miguel Raz GuzmΓ‘n Macedo
Miguel Raz GuzmΓ‘n Macedo

πŸ–‹
Magnus Markling
Magnus Markling

πŸ–‹
Tiago De Gaspari
Tiago De Gaspari

πŸ–‹
skaunov
skaunov

πŸ–‹
Cal Jacobson
Cal Jacobson

πŸ–‹
From a59e305665f0cb52f29cc77f76cf09f6f3acae03 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 08:45:07 +0000 Subject: [PATCH 0026/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4fd4b1d5be..76dd3ad79a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1587,6 +1587,15 @@ "contributions": [ "content" ] + }, + { + "login": "cj81499", + "name": "Cal Jacobson", + "avatar_url": "https://avatars.githubusercontent.com/u/9152032?v=4", + "profile": "http://caljacobson.dev", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From b4c7507b4d979c5c14870045e148afb6cec83103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Faug=C3=A8re?= Date: Fri, 30 Sep 2022 15:50:45 +0200 Subject: [PATCH 0027/1432] feat: Add VSCode extension recommendation --- .gitignore | 3 ++- .vscode/extensions.json | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .vscode/extensions.json diff --git a/.gitignore b/.gitignore index 534453bca1..c14f9227aa 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ exercises/clippy/Cargo.toml exercises/clippy/Cargo.lock rust-project.json .idea -.vscode +.vscode/* +!.vscode/extensions.json *.iml *.o diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..b85de74971 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "rust-lang.rust-analyzer" + ] +} From 4749768734fd14e38eea555034f3586c11280933 Mon Sep 17 00:00:00 2001 From: Duchoud Nicolas <34117620+duchonic@users.noreply.github.com> Date: Tue, 4 Oct 2022 11:36:18 +0200 Subject: [PATCH 0028/1432] additional test for fees --- exercises/structs/structs3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs index 0b3615f4cb..4c2a66107b 100644 --- a/exercises/structs/structs3.rs +++ b/exercises/structs/structs3.rs @@ -78,5 +78,6 @@ mod tests { let package = Package::new(sender_country, recipient_country, 1500); assert_eq!(package.get_fees(cents_per_gram), 4500); + assert_eq!(package.get_fees(cents_per_gram*2), 9000); } } From 76392d81fa2689d22ccf8dc0cf774e8d345cb80d Mon Sep 17 00:00:00 2001 From: Duchoud Nicolas <34117620+duchonic@users.noreply.github.com> Date: Tue, 4 Oct 2022 11:43:23 +0200 Subject: [PATCH 0029/1432] Added spaces around * --- exercises/structs/structs3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs index 4c2a66107b..3536a4573e 100644 --- a/exercises/structs/structs3.rs +++ b/exercises/structs/structs3.rs @@ -78,6 +78,6 @@ mod tests { let package = Package::new(sender_country, recipient_country, 1500); assert_eq!(package.get_fees(cents_per_gram), 4500); - assert_eq!(package.get_fees(cents_per_gram*2), 9000); + assert_eq!(package.get_fees(cents_per_gram * 2), 9000); } } From 1e1b689775b01c0b5fb7d63d26b2d29898d4ab87 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:29:27 +0000 Subject: [PATCH 0030/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index bfc8b99a68..abd0d18534 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -227,11 +227,9 @@ authors. Tiago De Gaspari
Tiago De Gaspari

πŸ–‹ skaunov
skaunov

πŸ–‹ Cal Jacobson
Cal Jacobson

πŸ–‹ + Duchoud Nicolas
Duchoud Nicolas

πŸ–‹ - - - From 88a767f39a643f0240bc8e928327ffcbdaa0bad4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:29:28 +0000 Subject: [PATCH 0031/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 76dd3ad79a..6deddfca79 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1596,6 +1596,15 @@ "contributions": [ "content" ] + }, + { + "login": "duchonic", + "name": "Duchoud Nicolas", + "avatar_url": "https://avatars.githubusercontent.com/u/34117620?v=4", + "profile": "https://github.com/duchonic", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From bfdc1991a5244610992fd3633b36750d539ccabb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:39:59 +0000 Subject: [PATCH 0032/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index abd0d18534..704021ab81 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -228,6 +228,7 @@ authors. skaunov
skaunov

πŸ–‹ Cal Jacobson
Cal Jacobson

πŸ–‹ Duchoud Nicolas
Duchoud Nicolas

πŸ–‹ + GaΓ«tan FaugΓ¨re
Gaëtan Faugère

πŸ”§ From d00f7783db431ac918e2d56a54def3e6e7d7beb2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:40:00 +0000 Subject: [PATCH 0033/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 6deddfca79..7374951b88 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1605,6 +1605,15 @@ "contributions": [ "content" ] + }, + { + "login": "gfaugere", + "name": "GaΓ«tan FaugΓ¨re", + "avatar_url": "https://avatars.githubusercontent.com/u/11901979?v=4", + "profile": "https://github.com/gfaugere", + "contributions": [ + "tool" + ] } ], "contributorsPerLine": 8, From da6178bdc6b8a88d2bfdac82e0281f45b177a299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20=C5=BBur?= Date: Tue, 11 Oct 2022 10:43:32 +0200 Subject: [PATCH 0034/1432] Removed unnecessary use statement --- exercises/options/options2.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/exercises/options/options2.rs b/exercises/options/options2.rs index b112047181..4e36443fd7 100644 --- a/exercises/options/options2.rs +++ b/exercises/options/options2.rs @@ -5,8 +5,6 @@ #[cfg(test)] mod tests { - use super::*; - #[test] fn simple_option() { let target = "rustlings"; From c157c53983d67d209e7ffc63896f9a9030f5154c Mon Sep 17 00:00:00 2001 From: bhbuehler <25541343+bhbuehler@users.noreply.github.com> Date: Tue, 11 Oct 2022 12:05:37 -0500 Subject: [PATCH 0035/1432] docs(options1): fix and clarify 24 hour time instruction --- exercises/options/options1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/options/options1.rs b/exercises/options/options1.rs index d1735c2f76..1149af0863 100644 --- a/exercises/options/options1.rs +++ b/exercises/options/options1.rs @@ -8,8 +8,8 @@ // all, so there'll be no more left :( // TODO: Return an Option! fn maybe_icecream(time_of_day: u16) -> Option { - // We use the 24-hour system here, so 10PM is a value of 22 - // The Option output should gracefully handle cases where time_of_day > 24. + // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a value of 0 + // The Option output should gracefully handle cases where time_of_day > 23. ??? } From 08df6224af229429598c7da027e3738781bb750b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 12 Oct 2022 14:51:21 +0000 Subject: [PATCH 0036/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 704021ab81..8dd7116391 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -230,6 +230,9 @@ authors. Duchoud Nicolas
Duchoud Nicolas

πŸ–‹ GaΓ«tan FaugΓ¨re
Gaëtan Faugère

πŸ”§ + + bhbuehler
bhbuehler

πŸ–‹ + From a50dcc97280644740939e64140bc313be7e5cb8a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 12 Oct 2022 14:51:22 +0000 Subject: [PATCH 0037/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7374951b88..5235b3896a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1614,6 +1614,15 @@ "contributions": [ "tool" ] + }, + { + "login": "bhbuehler", + "name": "bhbuehler", + "avatar_url": "https://avatars.githubusercontent.com/u/25541343?v=4", + "profile": "https://github.com/bhbuehler", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 2940ad059d7f0fa1dbcbed2a6c522ba3cbfd0ec7 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 12 Oct 2022 16:30:52 -0400 Subject: [PATCH 0038/1432] Apply uninlined-format-args clippy lint This lint should also be applied to the excersies, but I am not certain how to run it for all non-crate individual files. To re-run: ``` rustup run nightly cargo clippy --fix -- -A clippy::all -W clippy::uninlined_format_args ``` --- src/exercise.rs | 2 +- src/main.rs | 22 +++++++++++----------- src/run.rs | 4 ++-- src/verify.rs | 14 +++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 4be3a2ccdd..c0dae34ea9 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -20,7 +20,7 @@ fn temp_file() -> String { .filter(|c| c.is_alphanumeric()) .collect(); - format!("./temp_{}_{}", process::id(), thread_id) + format!("./temp_{}_{thread_id}", process::id()) } // The mode of the exercise. diff --git a/src/main.rs b/src/main.rs index cd79d9ffca..3e78333ac3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,12 +121,12 @@ fn main() { let args: Args = argh::from_env(); if args.version { - println!("v{}", VERSION); + println!("v{VERSION}"); std::process::exit(0); } if args.nested.is_none() { - println!("\n{}\n", WELCOME); + println!("\n{WELCOME}\n"); } if !Path::new("info.toml").exists() { @@ -150,7 +150,7 @@ fn main() { let verbose = args.nocapture; let command = args.nested.unwrap_or_else(|| { - println!("{}\n", DEFAULT_OUT); + println!("{DEFAULT_OUT}\n"); std::process::exit(0); }); match command { @@ -179,11 +179,11 @@ fn main() { }; if solve_cond && (filter_cond || subargs.filter.is_none()) { let line = if subargs.paths { - format!("{}\n", fname) + format!("{fname}\n") } else if subargs.names { format!("{}\n", e.name) } else { - format!("{:<17}\t{:<46}\t{:<7}\n", e.name, fname, status) + format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name) }; // Somehow using println! leads to the binary panicking // when its output is piped. @@ -266,7 +266,7 @@ fn main() { "{emoji} All exercises completed! {emoji}", emoji = Emoji("πŸŽ‰", "β˜…") ); - println!("\n{}\n", FENISH_LINE); + println!("\n{FENISH_LINE}\n"); } Ok(WatchStatus::Unfinished) => { println!("We hope you're enjoying learning about Rust!"); @@ -289,7 +289,7 @@ fn spawn_watch_shell( let input = input.trim(); if input == "hint" { if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { - println!("{}", hint); + println!("{hint}"); } } else if input == "clear" { println!("\x1B[2J\x1B[1;1H"); @@ -306,10 +306,10 @@ fn spawn_watch_shell( println!("Watch mode automatically re-evaluates the current exercise"); println!("when you edit a file's contents.") } else { - println!("unknown command: {}", input); + println!("unknown command: {input}"); } } - Err(error) => println!("error reading command: {}", error), + Err(error) => println!("error reading command: {error}"), } }); } @@ -329,7 +329,7 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { .iter() .find(|e| e.name == name) .unwrap_or_else(|| { - println!("No exercise found for '{}'!", name); + println!("No exercise found for '{name}'!"); std::process::exit(1) }) } @@ -392,7 +392,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result { Err(RecvTimeoutError::Timeout) => { // the timeout expired, just check the `should_quit` variable below then loop again } - Err(e) => println!("watch error: {:?}", e), + Err(e) => println!("watch error: {e:?}"), } // Check if we need to exit if should_quit.load(Ordering::SeqCst) { diff --git a/src/run.rs b/src/run.rs index 826f00a638..1e2e56cfad 100644 --- a/src/run.rs +++ b/src/run.rs @@ -35,7 +35,7 @@ pub fn reset(exercise: &Exercise) -> Result<(), ()> { // This is strictly for non-test binaries, so output is displayed fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {}...", exercise)); + progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.enable_steady_tick(100); let compilation_result = exercise.compile(); @@ -52,7 +52,7 @@ fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { } }; - progress_bar.set_message(format!("Running {}...", exercise)); + progress_bar.set_message(format!("Running {exercise}...")); let result = compilation.run(); progress_bar.finish_and_clear(); diff --git a/src/verify.rs b/src/verify.rs index 6f877831dc..95ccbe50ba 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -48,7 +48,7 @@ pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> { // Invoke the rust compiler without running the resulting binary fn compile_only(exercise: &Exercise) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {}...", exercise)); + progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.enable_steady_tick(100); let _ = compile(exercise, &progress_bar)?; @@ -60,12 +60,12 @@ fn compile_only(exercise: &Exercise) -> Result { // Compile the given Exercise and run the resulting binary in an interactive mode fn compile_and_run_interactively(exercise: &Exercise) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {}...", exercise)); + progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.enable_steady_tick(100); let compilation = compile(exercise, &progress_bar)?; - progress_bar.set_message(format!("Running {}...", exercise)); + progress_bar.set_message(format!("Running {exercise}...")); let result = compilation.run(); progress_bar.finish_and_clear(); @@ -86,7 +86,7 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result { // the output if verbose is set to true fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Testing {}...", exercise)); + progress_bar.set_message(format!("Testing {exercise}...")); progress_bar.enable_steady_tick(100); let compilation = compile(exercise, &progress_bar)?; @@ -165,16 +165,16 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option) -> println!(); if no_emoji { - println!("~*~ {} ~*~", success_msg) + println!("~*~ {success_msg} ~*~") } else { - println!("πŸŽ‰ πŸŽ‰ {} πŸŽ‰ πŸŽ‰", success_msg) + println!("πŸŽ‰ πŸŽ‰ {success_msg} πŸŽ‰ πŸŽ‰") } println!(); if let Some(output) = prompt_output { println!("Output:"); println!("{}", separator()); - println!("{}", output); + println!("{output}"); println!("{}", separator()); println!(); } From 68388e5d4f73ca7717eff0e668aa89f5c3a2124e Mon Sep 17 00:00:00 2001 From: azzamsa Date: Mon, 3 Oct 2022 19:56:46 +0700 Subject: [PATCH 0039/1432] feat(verify): add progress percentage in watch mode --- src/main.rs | 2 +- src/verify.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index cd79d9ffca..8a17a35f56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -202,7 +202,7 @@ fn main() { }); let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0; println!( - "Progress: You completed {} / {} exercises ({:.2} %).", + "Progress: You completed {} / {} exercises ({:.1} %).", exercises_done, exercises.len(), percentage_progress diff --git a/src/verify.rs b/src/verify.rs index 6f877831dc..595990d2f2 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -16,7 +16,7 @@ pub fn verify<'a>( let (num_done, total) = progress; let bar = ProgressBar::new(total as u64); bar.set_style(ProgressStyle::default_bar() - .template("Progress: [{bar:60.green/red}] {pos}/{len}") + .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") .progress_chars("#>-") ); bar.set_position(num_done as u64); @@ -29,6 +29,8 @@ pub fn verify<'a>( if !compile_result.unwrap_or(false) { return Err(exercise); } + let percentage = num_done as f32 / total as f32 * 100.0; + bar.set_message(format!("({:.1} %)", percentage)); bar.inc(1); } Ok(()) From c96fbc71809e913cacde12a5859404490a2f783a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Oct 2022 09:26:50 +0000 Subject: [PATCH 0040/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 8dd7116391..3e7f916d7d 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -232,6 +232,7 @@ authors. bhbuehler
bhbuehler

πŸ–‹ + Yuri Astrakhan
Yuri Astrakhan

πŸ’» From 8b251a202f09b24fa1c009e32b69bfa82d4ca75e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Oct 2022 09:26:51 +0000 Subject: [PATCH 0041/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 5235b3896a..eaaf62448f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1623,6 +1623,15 @@ "contributions": [ "content" ] + }, + { + "login": "nyurik", + "name": "Yuri Astrakhan", + "avatar_url": "https://avatars.githubusercontent.com/u/1641515?v=4", + "profile": "https://github.com/nyurik", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From eea039b025c22e0103cea3cb980c985d6e0ff9b6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Oct 2022 09:30:47 +0000 Subject: [PATCH 0042/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 3e7f916d7d..642659b224 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -233,6 +233,7 @@ authors. bhbuehler
bhbuehler

πŸ–‹ Yuri Astrakhan
Yuri Astrakhan

πŸ’» + azzamsa
azzamsa

πŸ’» From 3cb9825033f1d77e51a769db0300a62b525dbb1b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Oct 2022 09:30:48 +0000 Subject: [PATCH 0043/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index eaaf62448f..208d52ef76 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1632,6 +1632,15 @@ "contributions": [ "code" ] + }, + { + "login": "azzamsa", + "name": "azzamsa", + "avatar_url": "https://avatars.githubusercontent.com/u/17734314?v=4", + "profile": "http://azzamsa.com", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From ccd73c0a815bbf5bdb1d215e0e0417f5ea216e68 Mon Sep 17 00:00:00 2001 From: Matthew Van Schellebeeck Date: Sun, 16 Oct 2022 08:18:56 -0400 Subject: [PATCH 0044/1432] style: explicitly use Arc::clone --- exercises/threads/threads2.rs | 2 +- exercises/threads/threads3.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/threads/threads2.rs b/exercises/threads/threads2.rs index d0f8578f02..ada3d14a60 100644 --- a/exercises/threads/threads2.rs +++ b/exercises/threads/threads2.rs @@ -17,7 +17,7 @@ fn main() { let status = Arc::new(JobStatus { jobs_completed: 0 }); let mut handles = vec![]; for _ in 0..10 { - let status_shared = status.clone(); + let status_shared = Arc::clone(&status); let handle = thread::spawn(move || { thread::sleep(Duration::from_millis(250)); // TODO: You must take an action before you update a shared value diff --git a/exercises/threads/threads3.rs b/exercises/threads/threads3.rs index 27e9908829..9e9f285aa7 100644 --- a/exercises/threads/threads3.rs +++ b/exercises/threads/threads3.rs @@ -26,8 +26,8 @@ impl Queue { fn send_tx(q: Queue, tx: mpsc::Sender) -> () { let qc = Arc::new(q); - let qc1 = qc.clone(); - let qc2 = qc.clone(); + let qc1 = Arc::clone(&qc); + let qc2 = Arc::clone(&qc); thread::spawn(move || { for val in &qc1.first_half { From 5e6207bb90919d71a68c852f1c46eb6f23b09c10 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 09:38:01 +0000 Subject: [PATCH 0045/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 642659b224..0a21049d7d 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -234,6 +234,7 @@ authors. bhbuehler
bhbuehler

πŸ–‹ Yuri Astrakhan
Yuri Astrakhan

πŸ’» azzamsa
azzamsa

πŸ’» + mvanschellebeeck
mvanschellebeeck

πŸ–‹ From 61c086b2429d7a7ea5d7aa98ddeaa6870b564a9b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 09:38:02 +0000 Subject: [PATCH 0046/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 208d52ef76..f46e1adea1 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1641,6 +1641,15 @@ "contributions": [ "code" ] + }, + { + "login": "mvanschellebeeck", + "name": "mvanschellebeeck", + "avatar_url": "https://avatars.githubusercontent.com/u/17671052?v=4", + "profile": "https://github.com/mvanschellebeeck", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From a20452489720a59f4b080c6c241d8df1d0e06e75 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 09:39:49 +0000 Subject: [PATCH 0047/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 0a21049d7d..b994508ccc 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -235,6 +235,7 @@ authors. Yuri Astrakhan
Yuri Astrakhan

πŸ’» azzamsa
azzamsa

πŸ’» mvanschellebeeck
mvanschellebeeck

πŸ–‹ + Arkid
Arkid

πŸ–‹ From ee841311714ebb8baa1fe46e00281d00f38bf3ad Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 09:39:50 +0000 Subject: [PATCH 0048/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f46e1adea1..6e9ece9990 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1650,6 +1650,15 @@ "contributions": [ "content" ] + }, + { + "login": "aaarkid", + "name": "Arkid", + "avatar_url": "https://avatars.githubusercontent.com/u/39987510?v=4", + "profile": "https://github.com/aaarkid", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 623161e50dd8dc1960452c7e5bd7d66bfbab483f Mon Sep 17 00:00:00 2001 From: Arkid <39987510+aaarkid@users.noreply.github.com> Date: Fri, 21 Oct 2022 02:45:31 +0200 Subject: [PATCH 0049/1432] fix: Revert deref change Revert the addition of a deref in PR #1192 by me, which should not be there. Apologies for the inconvenience caused. --- exercises/conversions/as_ref_mut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index dafbdb981a..c9eed7d015 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -17,7 +17,7 @@ fn char_counter(arg: T) -> usize { arg.as_ref().chars().count() } -// Squares a number using AsMut. Add the trait bound as is appropriate and +// Squares a number using as_mut(). Add the trait bound as is appropriate and // implement the function body. fn num_sq(arg: &mut T) { ??? @@ -54,7 +54,7 @@ mod tests { #[test] fn mult_box() { let mut num: Box = Box::new(3); - num_sq(&mut *num); + num_sq(&mut num); assert_eq!(*num, 9); } } From da995b24ebbc210b5fe22eef3c52fbdde89a9d01 Mon Sep 17 00:00:00 2001 From: Tom Kunc Date: Mon, 24 Oct 2022 13:57:44 +1100 Subject: [PATCH 0050/1432] feat(macros-readme): Add link to MacroKata --- exercises/macros/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exercises/macros/README.md b/exercises/macros/README.md index 31a941b7c5..e34bc3a8e8 100644 --- a/exercises/macros/README.md +++ b/exercises/macros/README.md @@ -4,6 +4,10 @@ Rust's macro system is very powerful, but also kind of difficult to wrap your head around. We're not going to teach you how to write your own fully-featured macros. Instead, we'll show you how to use and create them. +If you'd like to learn more about writing your own macros, the +[macrokata](https://github.com/tfpk/macrokata) project has a similar style +of exercises to Rustlings, but is all about learning to write Macros. + ## Further information - [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html) From 8e0f5bf1256e1485ea9f1d2ed81ad1059c21f2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20B=C3=B6hme?= Date: Tue, 25 Jan 2022 11:47:16 +0100 Subject: [PATCH 0051/1432] feat: Add flake.nix for nix users Co-authored-by: Winter --- README.md | 13 ++++++++++++ flake.lock | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 53 +++++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 6 ++++++ 4 files changed, 132 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 shell.nix diff --git a/README.md b/README.md index 9b619d6f1b..29f3e5deb6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,19 @@ curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | This will install Rustlings and give you access to the `rustlings` command. Run it to get started! +### Nix +Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. + +```bash +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.2.1) +git clone -b 5.2.1 --depth 1 https://github.com/rust-lang/rustlings +cd rustlings +# if nix version > 2.3 +nix develop +# if nix version <= 2.3 +nix-shell +``` + ## Windows In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`: diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..ceb62c6d33 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1650374568, + "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1666629043, + "narHash": "sha256-Yoq6Ut2F3Ol73yO9hG93x6ts5c4F5BhKTbcF3DtBEAw=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "b39fd6e4edef83cb4a135ebef98751ce23becc33", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..15c82b7743 --- /dev/null +++ b/flake.nix @@ -0,0 +1,53 @@ +{ + description = "Small exercises to get you used to reading and writing Rust code"; + + inputs = { + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + flake-utils.url = "github:numtide/flake-utils"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + }; + + outputs = { self, flake-utils, nixpkgs, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + rustlings = + pkgs.rustPlatform.buildRustPackage { + name = "rustlings"; + version = "5.2.1"; + + src = with pkgs.lib; cleanSourceWith { + src = self; + # a function that returns a bool determining if the path should be included in the cleaned source + filter = path: type: + let + # filename + baseName = builtins.baseNameOf (toString path); + # path from root directory + path' = builtins.replaceStrings [ "${self}/" ] [ "" ] path; + # checks if path is in the directory + inDirectory = directory: hasPrefix directory path'; + in + inDirectory "src" || + inDirectory "tests" || + hasPrefix "Cargo" baseName || + baseName == "info.toml"; + }; + + cargoLock.lockFile = ./Cargo.lock; + }; + in + { + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + cargo + rustc + rust-analyzer + rustlings + ]; + }; + }); +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000..fa2a56c769 --- /dev/null +++ b/shell.nix @@ -0,0 +1,6 @@ +(import (let lock = builtins.fromJSON (builtins.readFile ./flake.lock); +in fetchTarball { + url = + "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; +}) { src = ./.; }).shellNix From a2134d70c6968ec4bc37e4e42a8f09a6e27b7962 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 08:49:52 +0000 Subject: [PATCH 0052/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index b994508ccc..84f29b43f6 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -236,6 +236,7 @@ authors. azzamsa
azzamsa

πŸ’» mvanschellebeeck
mvanschellebeeck

πŸ–‹ Arkid
Arkid

πŸ–‹ + Tom Kunc
Tom Kunc

πŸ–‹ From a0b10af1a355da8684e942ccc1d7e0d4cef1089d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 08:49:53 +0000 Subject: [PATCH 0053/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 6e9ece9990..523202d303 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1659,6 +1659,15 @@ "contributions": [ "content" ] + }, + { + "login": "tfpk", + "name": "Tom Kunc", + "avatar_url": "https://avatars.githubusercontent.com/u/10906982?v=4", + "profile": "http://tfpk.dev", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From d01ce8304e018b815cbd4488034f2339c749b6b1 Mon Sep 17 00:00:00 2001 From: mfurak Date: Sun, 6 Nov 2022 20:28:34 +0100 Subject: [PATCH 0054/1432] style: format errors5 with rustfmt --- exercises/error_handling/errors5.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs index 2ba8f9039d..6da06ef371 100644 --- a/exercises/error_handling/errors5.rs +++ b/exercises/error_handling/errors5.rs @@ -7,7 +7,7 @@ // For now, think of the `Box` type as an "I want anything that does ???" type, which, given // Rust's usual standards for runtime safety, should strike you as somewhat lenient! -// In short, this particular use case for boxes is for when you want to own a value and you care only that it is a +// In short, this particular use case for boxes is for when you want to own a value and you care only that it is a // type which implements a particular trait. To do so, The Box is declared as of type Box where Trait is the trait // the compiler looks for on any value used in that context. For this exercise, that context is the potential errors // which can be returned in a Result. @@ -46,7 +46,7 @@ impl PositiveNonzeroInteger { match value { x if x < 0 => Err(CreationError::Negative), x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)) + x => Ok(PositiveNonzeroInteger(x as u64)), } } } From 152193b459085c2bdfe41f65df7ea04a032d2a4c Mon Sep 17 00:00:00 2001 From: mfurak Date: Sun, 6 Nov 2022 20:42:17 +0100 Subject: [PATCH 0055/1432] style: format errors6 with rustfmt --- exercises/error_handling/errors6.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/exercises/error_handling/errors6.rs b/exercises/error_handling/errors6.rs index 1306fb03e1..8097b4902a 100644 --- a/exercises/error_handling/errors6.rs +++ b/exercises/error_handling/errors6.rs @@ -16,7 +16,7 @@ use std::num::ParseIntError; #[derive(PartialEq, Debug)] enum ParsePosNonzeroError { Creation(CreationError), - ParseInt(ParseIntError) + ParseInt(ParseIntError), } impl ParsePosNonzeroError { @@ -27,14 +27,11 @@ impl ParsePosNonzeroError { // fn from_parseint... } -fn parse_pos_nonzero(s: &str) - -> Result -{ +fn parse_pos_nonzero(s: &str) -> Result { // TODO: change this to return an appropriate error instead of panicking // when `parse()` returns an error. let x: i64 = s.parse().unwrap(); - PositiveNonzeroInteger::new(x) - .map_err(ParsePosNonzeroError::from_creation) + PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation) } // Don't change anything below this line. @@ -53,7 +50,7 @@ impl PositiveNonzeroInteger { match value { x if x < 0 => Err(CreationError::Negative), x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)) + x => Ok(PositiveNonzeroInteger(x as u64)), } } } From 6413f5b5d4c56b0a07931f75ecba5ea57694b8d9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 7 Nov 2022 14:14:20 +0000 Subject: [PATCH 0056/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 84f29b43f6..379e986504 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -237,6 +237,7 @@ authors. mvanschellebeeck
mvanschellebeeck

πŸ–‹ Arkid
Arkid

πŸ–‹ Tom Kunc
Tom Kunc

πŸ–‹ + Marek FurΓ‘k
Marek FurΓ‘k

πŸ–‹ From 70f9f08d2cc9a1b0dc40ef1ab3f393417551c5aa Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 7 Nov 2022 14:14:21 +0000 Subject: [PATCH 0057/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 523202d303..4caffea79a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1668,6 +1668,15 @@ "contributions": [ "content" ] + }, + { + "login": "mfurak", + "name": "Marek FurΓ‘k", + "avatar_url": "https://avatars.githubusercontent.com/u/38523093?v=4", + "profile": "https://github.com/mfurak", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 2e1630c712892f83cb1cc3adfb034f9e358e7a5e Mon Sep 17 00:00:00 2001 From: mokou Date: Fri, 11 Nov 2022 16:12:09 +0100 Subject: [PATCH 0058/1432] chore: style fixes --- Cargo.toml | 5 ++++- README.md | 2 +- info.toml | 18 +++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dadded6877..3f5b253db9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "rustlings" version = "5.2.1" -authors = ["Liv ", "Carol (Nichols || Goulding) "] +authors = [ + "Liv ", + "Carol (Nichols || Goulding) ", +] edition = "2021" [dependencies] diff --git a/README.md b/README.md index 9b619d6f1b..4e0bbf0bc4 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ After every couple of sections, there will be a quiz that'll test your knowledge ## Enabling `rust-analyzer` -Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise. +Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise. ## Continuing On diff --git a/info.toml b/info.toml index a840f9ba97..5a177506e6 100644 --- a/info.toml +++ b/info.toml @@ -416,8 +416,8 @@ path = "exercises/enums/enums3.rs" mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. -and then create a match expression in `process()`. -Note that you need to deconstruct some message variants +and then create a match expression in `process()`. +Note that you need to deconstruct some message variants in the match expression to get value in the variant.""" # STRINGS @@ -476,7 +476,7 @@ name = "modules2" path = "exercises/modules/modules2.rs" mode = "compile" hint = """ -The delicious_snacks module is trying to present an external interface that is +The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and associated constants). Complete the `use` statements to fit the uses in main and find the one keyword missing for both constants.""" @@ -623,12 +623,12 @@ path = "exercises/error_handling/errors5.rs" mode = "compile" hint = """ There are two different possible `Result` types produced within `main()`, which are -propagated using `?` operators. How do we declare a return type from `main()` that allows both? +propagated using `?` operators. How do we declare a return type from `main()` that allows both? Under the hood, the `?` operator calls `From::from` on the error value to convert it to a boxed trait object, a `Box`. This boxed trait object is polymorphic, and since all errors implement the `error::Error` trait, we can capture lots of different errors in one "Box" -object. +object. Check out this section of the book: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator @@ -862,7 +862,7 @@ case is a vector of integers and the failure case is a DivisionError. The list_of_results function needs to return a vector of results. -See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how +See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how the `FromIterator` trait is used in `collect()`. This trait is REALLY powerful! It can make the solution to this exercise infinitely easier.""" @@ -964,10 +964,10 @@ name = "threads1" path = "exercises/threads/threads1.rs" mode = "compile" hint = """ -`JoinHandle` is a struct that is returned from a spawned thread: +`JoinHandle` is a struct that is returned from a spawned thread: https://doc.rust-lang.org/std/thread/fn.spawn.html -A challenge with multi-threaded applications is that the main thread can +A challenge with multi-threaded applications is that the main thread can finish before the spawned threads are completed. https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles @@ -1077,7 +1077,7 @@ mathematical constants in the rust standard library. https://doc.rust-lang.org/stable/std/f32/consts/index.html We may be tempted to use our own approximations for certain mathematical constants, -but clippy recognizes those imprecise mathematical constants as a source of +but clippy recognizes those imprecise mathematical constants as a source of potential error. See the suggestions of the clippy warning in compile output and use the appropriate replacement constant from std::f32::consts...""" From ef3ef82057d2e8d499d7bcaa204aeb0edea8a3c7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:44:31 +0000 Subject: [PATCH 0059/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 379e986504..515f28daa7 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -238,6 +238,7 @@ authors. Arkid
Arkid

πŸ–‹ Tom Kunc
Tom Kunc

πŸ–‹ Marek FurΓ‘k
Marek FurΓ‘k

πŸ–‹ + Winter
Winter

πŸ’» From 78e2685d5fd7902c0be9b441846841a9f9cd57c4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:44:32 +0000 Subject: [PATCH 0060/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4caffea79a..7d5b26da64 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1677,6 +1677,15 @@ "contributions": [ "content" ] + }, + { + "login": "winterqt", + "name": "Winter", + "avatar_url": "https://avatars.githubusercontent.com/u/78392041?v=4", + "profile": "https://winter.cafe", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 43c049df753b3a26b4d018a553ef53ffb63efa33 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:45:16 +0000 Subject: [PATCH 0061/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 515f28daa7..b0a5b64cfd 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -240,6 +240,9 @@ authors. Marek FurΓ‘k
Marek FurΓ‘k

πŸ–‹ Winter
Winter

πŸ’» + + Moritz BΓΆhme
Moritz BΓΆhme

πŸ’» + From 28d78e288b3f267865c1c18a465cb3c6b0ff1645 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:45:16 +0000 Subject: [PATCH 0062/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7d5b26da64..bb0214999e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1686,6 +1686,15 @@ "contributions": [ "code" ] + }, + { + "login": "MoritzBoehme", + "name": "Moritz BΓΆhme", + "avatar_url": "https://avatars.githubusercontent.com/u/42215704?v=4", + "profile": "https://moritzboeh.me", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 2ceb59a11d7e2c8aa31d3bd7e1c7cb2c94d61300 Mon Sep 17 00:00:00 2001 From: craymel <71062756+craymel@users.noreply.github.com> Date: Fri, 18 Nov 2022 23:38:27 +1100 Subject: [PATCH 0063/1432] fix: Update structs3 hint --- info.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/info.toml b/info.toml index 5a177506e6..df3820d21e 100644 --- a/info.toml +++ b/info.toml @@ -386,11 +386,9 @@ name = "structs3" path = "exercises/structs/structs3.rs" mode = "test" hint = """ -The new method needs to panic if the weight is physically impossible :), how do we do that in Rust? - For is_international: What makes a package international? Seems related to the places it goes through right? -For calculate_transport_fees: Bigger is more expensive usually, we don't have size, but something may fit the bill here :) +For get_fees: This method takes an additional argument, is there a field in the Package struct that this relates to? Have a look in The Book, to find out more about method implementations: https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" From e8ef4fe5d612e554c2efa55835d351bc7f24f76e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 18 Nov 2022 23:35:51 +0000 Subject: [PATCH 0064/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index b0a5b64cfd..44d8a0ff96 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -242,6 +242,7 @@ authors. Moritz BΓΆhme
Moritz BΓΆhme

πŸ’» + craymel
craymel

πŸ–‹ From a8033e0193b39effead5e280fb5ee71a91fde8a1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 18 Nov 2022 23:35:52 +0000 Subject: [PATCH 0065/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index bb0214999e..9e2f307fec 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1695,6 +1695,15 @@ "contributions": [ "code" ] + }, + { + "login": "craymel", + "name": "craymel", + "avatar_url": "https://avatars.githubusercontent.com/u/71062756?v=4", + "profile": "https://github.com/craymel", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From be0b7e084ed5713d74c7f1bacdfd563fb2145a95 Mon Sep 17 00:00:00 2001 From: TK Buristrakul Date: Thu, 24 Nov 2022 19:20:59 +0000 Subject: [PATCH 0066/1432] chore: minor change in comment --- exercises/quiz2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 606d3c7096..715788b808 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -42,7 +42,7 @@ mod my_module { #[cfg(test)] mod tests { - // TODO: What do we have to import to have `transformer` in scope? + // TODO: What do we need to import to have `transformer` in scope? use ???; use super::Command; From a315f2fefb963c2facfd80efe336b8be3b8f6bfa Mon Sep 17 00:00:00 2001 From: TK Buristrakul Date: Thu, 24 Nov 2022 19:39:54 +0000 Subject: [PATCH 0067/1432] chore: added more descriptive TODOs --- exercises/conversions/as_ref_mut.rs | 13 +++++++------ exercises/options/options1.rs | 2 +- exercises/traits/traits1.rs | 2 +- exercises/traits/traits2.rs | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index c9eed7d015..e6a9d11472 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -5,21 +5,22 @@ // I AM NOT DONE -// Obtain the number of bytes (not characters) in the given argument -// Add the AsRef trait appropriately as a trait bound +// Obtain the number of bytes (not characters) in the given argument. +// TODO: Add the AsRef trait appropriately as a trait bound. fn byte_counter(arg: T) -> usize { arg.as_ref().as_bytes().len() } -// Obtain the number of characters (not bytes) in the given argument -// Add the AsRef trait appropriately as a trait bound +// Obtain the number of characters (not bytes) in the given argument. +// TODO: Add the AsRef trait appropriately as a trait bound. fn char_counter(arg: T) -> usize { arg.as_ref().chars().count() } -// Squares a number using as_mut(). Add the trait bound as is appropriate and -// implement the function body. +// Squares a number using as_mut(). +// TODO: Add the appropriate trait bound. fn num_sq(arg: &mut T) { + // TODO: Implement the function body. ??? } diff --git a/exercises/options/options1.rs b/exercises/options/options1.rs index 1149af0863..1f891b0eb2 100644 --- a/exercises/options/options1.rs +++ b/exercises/options/options1.rs @@ -6,10 +6,10 @@ // This function returns how much icecream there is left in the fridge. // If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them // all, so there'll be no more left :( -// TODO: Return an Option! fn maybe_icecream(time_of_day: u16) -> Option { // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a value of 0 // The Option output should gracefully handle cases where time_of_day > 23. + // TODO: Complete the function body - remember to return an Option! ??? } diff --git a/exercises/traits/traits1.rs b/exercises/traits/traits1.rs index 5b9d8d5064..43500b8664 100644 --- a/exercises/traits/traits1.rs +++ b/exercises/traits/traits1.rs @@ -16,7 +16,7 @@ trait AppendBar { } impl AppendBar for String { - //Add your code here + // TODO: Implement `AppendBar` for type `String`. } fn main() { diff --git a/exercises/traits/traits2.rs b/exercises/traits/traits2.rs index 708bb19a00..99dc1cbc4d 100644 --- a/exercises/traits/traits2.rs +++ b/exercises/traits/traits2.rs @@ -17,7 +17,7 @@ trait AppendBar { fn append_bar(self) -> Self; } -//TODO: Add your code here +// TODO: Implement trait `AppendBar` for a vector of strings. #[cfg(test)] mod tests { From db53dbc12615888ddd021025379fbab8e00e5067 Mon Sep 17 00:00:00 2001 From: TK Buristrakul Date: Thu, 24 Nov 2022 19:41:25 +0000 Subject: [PATCH 0068/1432] chore: tidied up unmatched backticks --- exercises/traits/traits1.rs | 2 +- exercises/traits/traits2.rs | 2 +- info.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/traits/traits1.rs b/exercises/traits/traits1.rs index 43500b8664..f5320a5a15 100644 --- a/exercises/traits/traits1.rs +++ b/exercises/traits/traits1.rs @@ -2,7 +2,7 @@ // Time to implement some traits! // // Your task is to implement the trait -// `AppendBar' for the type `String'. +// `AppendBar` for the type `String`. // // The trait AppendBar has only one function, // which appends "Bar" to any object diff --git a/exercises/traits/traits2.rs b/exercises/traits/traits2.rs index 99dc1cbc4d..288b498363 100644 --- a/exercises/traits/traits2.rs +++ b/exercises/traits/traits2.rs @@ -1,7 +1,7 @@ // traits2.rs // // Your task is to implement the trait -// `AppendBar' for a vector of strings. +// `AppendBar` for a vector of strings. // // To implement this trait, consider for // a moment what it means to 'append "Bar"' diff --git a/info.toml b/info.toml index df3820d21e..4b87819303 100644 --- a/info.toml +++ b/info.toml @@ -695,7 +695,7 @@ name = "traits2" path = "exercises/traits/traits2.rs" mode = "test" hint = """ -Notice how the trait takes ownership of 'self',and returns `Self'. +Notice how the trait takes ownership of 'self',and returns `Self`. Try mutating the incoming string vector. Have a look at the tests to see what the result should look like! From f94f365e146fa37b66e5c2e6b392d282f8c049f1 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Thu, 1 Dec 2022 14:05:10 +0100 Subject: [PATCH 0069/1432] dev: add basic devcontainer settings file Theses settings files are the base needed to declare and bootstrap development environment on codespaces Signed-off-by: Emmanuel Roullit --- .devcontainer/devcontainer.json | 17 +++++++++++++++++ .devcontainer/setup.sh | 4 ++++ 2 files changed, 21 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/setup.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..0fd90cc94b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "waitFor": "onCreateCommand", + "onCreateCommand": ".devcontainer/setup.sh", + "updateContentCommand": "cargo build", + "postCreateCommand": "", + "postAttachCommand": { + "server": "rustlings watch" + }, + "customizations": { + "vscode": { + "extensions": [ + "rust-lang.rust-analyzer" + ] + } + } +} diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 0000000000..e50bde3438 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,4 @@ +#!/bin/bash +curl https://sh.rustup.rs -sSf | sh -s -- -y +rustup install stable +bash install.sh From 4972bede48001fb4ae6838a98c2bfce2399d7293 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Thu, 1 Dec 2022 14:30:03 +0100 Subject: [PATCH 0070/1432] dev: source cargo env files to find rustup Signed-off-by: Emmanuel Roullit --- .devcontainer/setup.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index e50bde3438..0e090a8678 100755 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -1,4 +1,7 @@ #!/bin/bash curl https://sh.rustup.rs -sSf | sh -s -- -y + +# Update current shell environment variables after install to find rustup +. "$HOME/.cargo/env" rustup install stable bash install.sh From b653d4848a52701d2240f130ab74c158dd5d7069 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Thu, 1 Dec 2022 16:15:13 +0100 Subject: [PATCH 0071/1432] doc: add hint on how to create codespace Signed-off-by: Emmanuel Roullit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 38972a4bcf..f1adae877d 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ If you get a permission denied message, you might have to exclude the directory [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) +Open up Rustlings in [Codespaces](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace-for-a-repository#creating-a-codespace-for-a-repository) + ## Manually Basically: Clone the repository at the latest tag, run `cargo install --path .`. From 71873e676f665b57c6d6362739d8283ec5441f49 Mon Sep 17 00:00:00 2001 From: Tyson Liddell Date: Fri, 9 Dec 2022 20:42:39 +0000 Subject: [PATCH 0072/1432] fix: Remove superfluous &self indirection --- exercises/enums/enums2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/enums/enums2.rs b/exercises/enums/enums2.rs index 18479f874d..167a6b2e91 100644 --- a/exercises/enums/enums2.rs +++ b/exercises/enums/enums2.rs @@ -10,7 +10,7 @@ enum Message { impl Message { fn call(&self) { - println!("{:?}", &self); + println!("{:?}", self); } } From 3d693634b5dbb284a4c4712b45215005287bb92c Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 10 Dec 2022 13:23:15 +0100 Subject: [PATCH 0073/1432] fix nix build on Darwin --- flake.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flake.nix b/flake.nix index 15c82b7743..4cfe7d9a14 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,10 @@ name = "rustlings"; version = "5.2.1"; + buildInputs = with pkgs; lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.CoreServices + ]; + src = with pkgs.lib; cleanSourceWith { src = self; # a function that returns a bool determining if the path should be included in the cleaned source From 1ce671528e40eab7f5d09f6579537a297858d284 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 10 Dec 2022 13:57:26 +0100 Subject: [PATCH 0074/1432] add missing RUST_SRC_PATH --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 4cfe7d9a14..3fabe0fef7 100644 --- a/flake.nix +++ b/flake.nix @@ -46,6 +46,8 @@ in { devShell = pkgs.mkShell { + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + buildInputs = with pkgs; [ cargo rustc From b2df015fe6c76203d55ee1c916b4f3a27b327d67 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 10 Dec 2022 14:05:44 +0100 Subject: [PATCH 0075/1432] when generating lsp config use RUST_SRC_PATH if set --- src/project.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/project.rs b/src/project.rs index 0df00b9a2d..a6e3acfa2b 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,5 +1,6 @@ use glob::glob; use serde::{Deserialize, Serialize}; +use std::env; use std::error::Error; use std::process::Command; @@ -64,6 +65,12 @@ impl RustAnalyzerProject { /// Use `rustc` to determine the default toolchain pub fn get_sysroot_src(&mut self) -> Result<(), Box> { + // check if RUST_SRC_PATH is set + if let Ok(path) = env::var("RUST_SRC_PATH") { + self.sysroot_src = path; + return Ok(()); + } + let toolchain = Command::new("rustc") .arg("--print") .arg("sysroot") From e519b5079e93f4555d712771343fa159232cbf96 Mon Sep 17 00:00:00 2001 From: William Webb Date: Tue, 20 Dec 2022 21:17:32 -0600 Subject: [PATCH 0076/1432] fix(hashmaps3): fix typo in todo hint --- exercises/hashmaps/hashmaps3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/hashmaps/hashmaps3.rs index 18dd44c914..ad3baa688a 100644 --- a/exercises/hashmaps/hashmaps3.rs +++ b/exercises/hashmaps/hashmaps3.rs @@ -37,7 +37,7 @@ fn build_scores_table(results: String) -> HashMap { let team_2_score: u8 = v[3].parse().unwrap(); // TODO: Populate the scores table with details extracted from the // current line. Keep in mind that goals scored by team_1 - // will be number of goals conceded from team_2, and similarly + // will be the number of goals conceded from team_2, and similarly // goals scored by team_2 will be the number of goals conceded by // team_1. } From e0eef0e1901b80b5401a8c61346ab32eb54561cd Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 23 Dec 2022 16:16:24 +0100 Subject: [PATCH 0077/1432] docs: add note about powershell compat Closes #1299. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38972a4bcf..44ca4bdd6a 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Then, you can run: Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 ``` -To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. +To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors. If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus. From 130e57ec60519c269d6477eef0ad681f1ac96d5c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 23 Dec 2022 15:44:14 +0000 Subject: [PATCH 0078/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 44d8a0ff96..2fc637f2f3 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -243,6 +243,7 @@ authors. Moritz BΓΆhme
Moritz BΓΆhme

πŸ’» craymel
craymel

πŸ–‹ + TK Buristrakul
TK Buristrakul

πŸ–‹ From 5e583770f7922b45889cd5e252be0fb87578c088 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 23 Dec 2022 15:44:15 +0000 Subject: [PATCH 0079/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9e2f307fec..9414114070 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1704,6 +1704,15 @@ "contributions": [ "content" ] + }, + { + "login": "tkburis", + "name": "TK Buristrakul", + "avatar_url": "https://avatars.githubusercontent.com/u/20501289?v=4", + "profile": "https://github.com/tkburis", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 40b1b079150e8554b595374d3b72a84108109cc5 Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 23 Dec 2022 16:47:48 +0100 Subject: [PATCH 0080/1432] fix(enums3): add extra tuple comment --- exercises/enums/enums3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index 55acf6bc9d..54fd6f6071 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -52,7 +52,7 @@ mod tests { position: Point { x: 0, y: 0 }, color: (0, 0, 0), }; - state.process(Message::ChangeColor((255, 0, 255))); + state.process(Message::ChangeColor((255, 0, 255))); // Remember: The extra parentheses mark a tuple type. state.process(Message::Echo(String::from("hello world"))); state.process(Message::Move(Point { x: 10, y: 15 })); state.process(Message::Quit); From 9ad884aadb0758baf2c14b949c79d51adcf9b8fb Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 23 Dec 2022 16:53:03 +0100 Subject: [PATCH 0081/1432] chore: bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 8 ++++---- flake.nix | 2 +- src/main.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 948d0f42e7..49f20b6641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -459,7 +459,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rustlings" -version = "5.2.1" +version = "5.3.0" dependencies = [ "argh", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 3f5b253db9..c2c54fd2ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustlings" -version = "5.2.1" +version = "5.3.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", diff --git a/README.md b/README.md index 44ca4bdd6a..956bb6d0d5 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.2.1) -git clone -b 5.2.1 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.3.0) +git clone -b 5.3.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings # if nix version > 2.3 nix develop @@ -70,8 +70,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.2.1) -git clone -b 5.2.1 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.3.0) +git clone -b 5.3.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/flake.nix b/flake.nix index 15c82b7743..a6703199d8 100644 --- a/flake.nix +++ b/flake.nix @@ -17,7 +17,7 @@ rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; - version = "5.2.1"; + version = "5.3.0"; src = with pkgs.lib; cleanSourceWith { src = self; diff --git a/src/main.rs b/src/main.rs index bf8503d0d2..6dc18e8be1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ mod run; mod verify; // In sync with crate version -const VERSION: &str = "5.2.1"; +const VERSION: &str = "5.3.0"; #[derive(FromArgs, PartialEq, Debug)] /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code From 32b234c9f0de2f63f905d461639d9f7ed94776bc Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 23 Dec 2022 17:09:04 +0100 Subject: [PATCH 0082/1432] chore: update changelog --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9925e3dbf6..c351fce81f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ + +## 5.3.0 (2022-12-23) + +#### Added + +- **cli**: Added a percentage display in watch mode +- Added a `flake.nix` for Nix users + +#### Changed + +- **structs3**: Added an additional test +- **macros**: Added a link to MacroKata in the README + +#### Fixed + +- **strings3**: Added a link to `std` in the hint +- **threads1**: Corrected a hint link +- **iterators1**: Clarified hint steps +- **errors5**: Fix a typo in the hint +- **options1**: Clarified on the usage of the 24-hour system +- **threads2, threads3**: Explicitly use `Arc::clone` +- **structs3**: Clarifed the hint +- **quiz2, as_ref_mut, options1, traits1, traits2**: Clarified hints +- **traits1, traits2, cli**: Tidied up unmatching backticks +- **enums2**: Removed unneccessary indirection of self +- **enums3**: Added an extra tuple comment + +#### Housekeeping + +- Added a VSCode extension recommendation +- Applied some Clippy and rustfmt formatting +- Added a note on Windows PowerShell and other shell compatibility + ## 5.2.1 (2022-09-06) From 430371795133488f9daa1b7d390997fb3d3a7e7b Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Sun, 25 Dec 2022 17:57:28 +0100 Subject: [PATCH 0083/1432] chore: "rust" -> "Rust" in exercise hints --- info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/info.toml b/info.toml index 4b87819303..8356f6ab42 100644 --- a/info.toml +++ b/info.toml @@ -665,7 +665,7 @@ name = "generics1" path = "exercises/generics/generics1.rs" mode = "compile" hint = """ -Vectors in rust make use of generics to create dynamically sized arrays of any type. +Vectors in Rust make use of generics to create dynamically sized arrays of any type. You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] @@ -1071,7 +1071,7 @@ path = "exercises/clippy/clippy1.rs" mode = "clippy" hint = """ Rust stores the highest precision version of any long or inifinite precision -mathematical constants in the rust standard library. +mathematical constants in the Rust standard library. https://doc.rust-lang.org/stable/std/f32/consts/index.html We may be tempted to use our own approximations for certain mathematical constants, From 7e4ce386816a380e66dca482c57349cd1a049aeb Mon Sep 17 00:00:00 2001 From: platformer Date: Mon, 26 Dec 2022 02:25:43 -0600 Subject: [PATCH 0084/1432] fix(threads1): make program panic if threads are not joined closes #1298 --- exercises/threads/threads1.rs | 25 ++++++++++++++++--------- info.toml | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index e59f4ce490..d6376db24e 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -1,31 +1,38 @@ // threads1.rs // Execute `rustlings hint threads1` or use the `hint` watch subcommand for a hint. -// This program should wait until all the spawned threads have finished before exiting. + +// 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. // I AM NOT DONE use std::thread; -use std::time::Duration; - +use std::time::{Duration, Instant}; fn main() { - let mut handles = vec![]; for i in 0..10 { - thread::spawn(move || { + handles.push(thread::spawn(move || { + let start = Instant::now(); thread::sleep(Duration::from_millis(250)); println!("thread {} is complete", i); - }); + start.elapsed().as_millis() + })); } - let mut completed_threads = 0; + let mut results: Vec = vec![]; for handle in handles { // TODO: a struct is returned from thread::spawn, can you use it? - completed_threads += 1; } - if completed_threads != 10 { + if results.len() != 10 { panic!("Oh no! All the spawned threads did not finish!"); } + println!(); + for (i, result) in results.into_iter().enumerate() { + println!("thread {} took {}ms", i, result); + } } diff --git a/info.toml b/info.toml index 4b87819303..dd8b78ab0e 100644 --- a/info.toml +++ b/info.toml @@ -969,7 +969,7 @@ A challenge with multi-threaded applications is that the main thread can finish before the spawned threads are completed. https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles -Collect the JoinHandles and wait for them to finish. +Use the JoinHandles to wait for each thread to finish and collect their results. https://doc.rust-lang.org/std/thread/struct.JoinHandle.html """ From 2f821aa30da3640060741de552e5e6fd27d35778 Mon Sep 17 00:00:00 2001 From: HerschelW Date: Fri, 30 Dec 2022 08:14:13 -0600 Subject: [PATCH 0085/1432] chore: update enums3.rs addressing extra parentheses usage with tuples --- exercises/enums/enums3.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index 54fd6f6071..a2a9d58684 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -38,6 +38,7 @@ impl State { fn process(&mut self, message: Message) { // TODO: create a match expression to process the different message variants + // Remember: When passing a tuple as a function argument, you'll need extra parentheses: fn function((t, u, p, l, e)) } } @@ -52,7 +53,7 @@ mod tests { position: Point { x: 0, y: 0 }, color: (0, 0, 0), }; - state.process(Message::ChangeColor((255, 0, 255))); // Remember: The extra parentheses mark a tuple type. + state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Echo(String::from("hello world"))); state.process(Message::Move(Point { x: 10, y: 15 })); state.process(Message::Quit); From 25fc7814cc2507e472a3ce670303370dc01df06f Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 30 Dec 2022 15:52:49 +0000 Subject: [PATCH 0086/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 2fc637f2f3..160eed5717 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -244,6 +244,7 @@ authors. Moritz BΓΆhme
Moritz BΓΆhme

πŸ’» craymel
craymel

πŸ–‹ TK Buristrakul
TK Buristrakul

πŸ–‹ + Kent Worthington
Kent Worthington

πŸ–‹ From 6367697d926a0421528400fa394f3d7ecc45cf87 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 30 Dec 2022 15:52:50 +0000 Subject: [PATCH 0087/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9414114070..97d38f5be4 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1713,6 +1713,15 @@ "contributions": [ "content" ] + }, + { + "login": "HerschelW", + "name": "Kent Worthington", + "avatar_url": "https://avatars.githubusercontent.com/u/17935816?v=4", + "profile": "https://github.com/HerschelW", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From cb79be921d54b2a631c50d78df43bed0988a8f62 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:32:38 +0100 Subject: [PATCH 0088/1432] reordered smart pointer exercises Switched rc and arc so rc comes first, since arc is, like the name implies, atomic rc. This gives the exercises a more logical progression. Moved all smart pointer exercises (box, rc, arc, cow) under threads into their own section. Threads are used in the smart pointer exercises, so they should be introduced first. --- info.toml | 122 +++++++++++++++++++++++++++--------------------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/info.toml b/info.toml index 8356f6ab42..7512aee3ec 100644 --- a/info.toml +++ b/info.toml @@ -895,66 +895,6 @@ The fold method can be useful in the count_collection_iterator function. For a further challenge, consult the documentation for Iterator to find a different method that could make your code more compact than using fold.""" -[[exercises]] -name = "box1" -path = "exercises/standard_library_types/box1.rs" -mode = "test" -hint = """ -Step 1 -The compiler's message should help: since we cannot store the value of the actual type -when working with recursive types, we need to store a reference (pointer) to its value. -We should, therefore, place our `List` inside a `Box`. More details in the book here: -https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2 -Creating an empty list should be fairly straightforward (hint: peek at the assertions). -For a non-empty list keep in mind that we want to use our Cons "list builder". -Although the current list is one of integers (i32), feel free to change the definition -and try other types! -""" - -[[exercises]] -name = "arc1" -path = "exercises/standard_library_types/arc1.rs" -mode = "compile" -hint = """ -Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order -to avoid creating a copy of `numbers`, you'll need to create `child_numbers` -inside the loop but still in the main thread. - -`child_numbers` should be a clone of the Arc of the numbers instead of a -thread-local copy of the numbers. - -This is a simple exercise if you understand the underlying concepts, but if this -is too much of a struggle, consider reading through all of Chapter 16 in the book: -https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html -""" - -[[exercises]] -name = "rc1" -path = "exercises/standard_library_types/rc1.rs" -mode = "compile" -hint = """ -This is a straightforward exercise to use the Rc type. Each Planet has -ownership of the Sun, and uses Rc::clone() to increment the reference count of the Sun. -After using drop() to move the Planets out of scope individually, the reference count goes down. -In the end the sun only has one reference again, to itself. See more at: -https://doc.rust-lang.org/book/ch15-04-rc.html - -* Unfortunately Pluto is no longer considered a planet :( -""" - -[[exercises]] -name = "cow1" -path = "exercises/standard_library_types/cow1.rs" -mode = "compile" -hint = """ -Since the vector is already owned, the `Cow` type doesn't need to clone it. - -Checkout https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation -on the `Cow` type. -""" - # THREADS [[exercises]] @@ -1016,6 +956,68 @@ of the original sending end. See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. """ +# SMART POINTERS + +[[exercises]] +name = "box1" +path = "exercises/standard_library_types/box1.rs" +mode = "test" +hint = """ +Step 1 +The compiler's message should help: since we cannot store the value of the actual type +when working with recursive types, we need to store a reference (pointer) to its value. +We should, therefore, place our `List` inside a `Box`. More details in the book here: +https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes + +Step 2 +Creating an empty list should be fairly straightforward (hint: peek at the assertions). +For a non-empty list keep in mind that we want to use our Cons "list builder". +Although the current list is one of integers (i32), feel free to change the definition +and try other types! +""" + +[[exercises]] +name = "rc1" +path = "exercises/standard_library_types/rc1.rs" +mode = "compile" +hint = """ +This is a straightforward exercise to use the Rc type. Each Planet has +ownership of the Sun, and uses Rc::clone() to increment the reference count of the Sun. +After using drop() to move the Planets out of scope individually, the reference count goes down. +In the end the sun only has one reference again, to itself. See more at: +https://doc.rust-lang.org/book/ch15-04-rc.html + +* Unfortunately Pluto is no longer considered a planet :( +""" + +[[exercises]] +name = "arc1" +path = "exercises/standard_library_types/arc1.rs" +mode = "compile" +hint = """ +Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order +to avoid creating a copy of `numbers`, you'll need to create `child_numbers` +inside the loop but still in the main thread. + +`child_numbers` should be a clone of the Arc of the numbers instead of a +thread-local copy of the numbers. + +This is a simple exercise if you understand the underlying concepts, but if this +is too much of a struggle, consider reading through all of Chapter 16 in the book: +https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html +""" + +[[exercises]] +name = "cow1" +path = "exercises/standard_library_types/cow1.rs" +mode = "compile" +hint = """ +Since the vector is already owned, the `Cow` type doesn't need to clone it. + +Checkout https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation +on the `Cow` type. +""" + # MACROS [[exercises]] From 05592acf4053262c7cffe824076322da8b13e6e2 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:44:47 +0100 Subject: [PATCH 0089/1432] move arc to smart_pointers --- exercises/{standard_library_types => smart_pointers}/arc1.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => smart_pointers}/arc1.rs (100%) diff --git a/exercises/standard_library_types/arc1.rs b/exercises/smart_pointers/arc1.rs similarity index 100% rename from exercises/standard_library_types/arc1.rs rename to exercises/smart_pointers/arc1.rs From c3bab88fda6311b34bc3f8091c2e3cf442148d6f Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:51:27 +0100 Subject: [PATCH 0090/1432] moved box to smart_pointers --- exercises/{standard_library_types => smart_pointers}/box1.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => smart_pointers}/box1.rs (100%) diff --git a/exercises/standard_library_types/box1.rs b/exercises/smart_pointers/box1.rs similarity index 100% rename from exercises/standard_library_types/box1.rs rename to exercises/smart_pointers/box1.rs From e8c4aab643062da2a44b764198a5077cc1bd97a0 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:52:05 +0100 Subject: [PATCH 0091/1432] moved cow to smart_pointers --- exercises/{standard_library_types => smart_pointers}/cow1.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => smart_pointers}/cow1.rs (100%) diff --git a/exercises/standard_library_types/cow1.rs b/exercises/smart_pointers/cow1.rs similarity index 100% rename from exercises/standard_library_types/cow1.rs rename to exercises/smart_pointers/cow1.rs From a8fd315e099e4b5c24b450eb6ba6ec3cccd96854 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:52:47 +0100 Subject: [PATCH 0092/1432] moved rc to smart_pointers --- exercises/{standard_library_types => smart_pointers}/rc1.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => smart_pointers}/rc1.rs (100%) diff --git a/exercises/standard_library_types/rc1.rs b/exercises/smart_pointers/rc1.rs similarity index 100% rename from exercises/standard_library_types/rc1.rs rename to exercises/smart_pointers/rc1.rs From a0c5a892d3f1a5b4fa1d2bba41fb1dca145460f3 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:58:04 +0100 Subject: [PATCH 0093/1432] refactoring standard_library_types as iterators --- exercises/iterators/README.md | 8 ++++++++ exercises/standard_library_types/README.md | 10 ---------- 2 files changed, 8 insertions(+), 10 deletions(-) create mode 100644 exercises/iterators/README.md delete mode 100644 exercises/standard_library_types/README.md diff --git a/exercises/iterators/README.md b/exercises/iterators/README.md new file mode 100644 index 0000000000..0e8b671e44 --- /dev/null +++ b/exercises/iterators/README.md @@ -0,0 +1,8 @@ +# Iterators + +This section will teach you about Iterators. + +## Further information + +- [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html) +- [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/) diff --git a/exercises/standard_library_types/README.md b/exercises/standard_library_types/README.md deleted file mode 100644 index 809d61feb6..0000000000 --- a/exercises/standard_library_types/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Standard library types - -This section will teach you about Box, Shared-State Concurrency and Iterators. - -## Further information - -- [Using Box to Point to Data on the Heap](https://doc.rust-lang.org/book/ch15-01-box.html) -- [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html) -- [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html) -- [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/) From e9dc52c2d3abb60c532634ffbda7f435b4c3d140 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:58:57 +0100 Subject: [PATCH 0094/1432] moved iterator exercises --- exercises/{standard_library_types => iterators}/iterators1.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => iterators}/iterators1.rs (100%) diff --git a/exercises/standard_library_types/iterators1.rs b/exercises/iterators/iterators1.rs similarity index 100% rename from exercises/standard_library_types/iterators1.rs rename to exercises/iterators/iterators1.rs From 5b0d587c223cff097d824fbd5f8a5003f88036a0 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 01:59:35 +0100 Subject: [PATCH 0095/1432] moved iterator exercises --- exercises/{standard_library_types => iterators}/iterators2.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => iterators}/iterators2.rs (100%) diff --git a/exercises/standard_library_types/iterators2.rs b/exercises/iterators/iterators2.rs similarity index 100% rename from exercises/standard_library_types/iterators2.rs rename to exercises/iterators/iterators2.rs From 0f02a9b9af80e8b62709d2c50b0fcdf5f7478ea7 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:00:36 +0100 Subject: [PATCH 0096/1432] moved iterator exercises --- exercises/{standard_library_types => iterators}/iterators3.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => iterators}/iterators3.rs (100%) diff --git a/exercises/standard_library_types/iterators3.rs b/exercises/iterators/iterators3.rs similarity index 100% rename from exercises/standard_library_types/iterators3.rs rename to exercises/iterators/iterators3.rs From e3e298cfa21f671f32280f576c3092c62dc81b2a Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:02:15 +0100 Subject: [PATCH 0097/1432] moved iterator exercises --- exercises/{standard_library_types => iterators}/iterators4.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => iterators}/iterators4.rs (100%) diff --git a/exercises/standard_library_types/iterators4.rs b/exercises/iterators/iterators4.rs similarity index 100% rename from exercises/standard_library_types/iterators4.rs rename to exercises/iterators/iterators4.rs From 8405a61b07583e6d09b8811c253a8e83aa972c46 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:02:49 +0100 Subject: [PATCH 0098/1432] moved iterator exercises --- exercises/{standard_library_types => iterators}/iterators5.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename exercises/{standard_library_types => iterators}/iterators5.rs (100%) diff --git a/exercises/standard_library_types/iterators5.rs b/exercises/iterators/iterators5.rs similarity index 100% rename from exercises/standard_library_types/iterators5.rs rename to exercises/iterators/iterators5.rs From 3fad2a9c8364c6994fa5de0ed58612ddceaf2f25 Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:17:23 +0100 Subject: [PATCH 0099/1432] gave smart_pointers its own README.md --- exercises/smart_pointers/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 exercises/smart_pointers/README.md diff --git a/exercises/smart_pointers/README.md b/exercises/smart_pointers/README.md new file mode 100644 index 0000000000..3893e78d5a --- /dev/null +++ b/exercises/smart_pointers/README.md @@ -0,0 +1,11 @@ +## Smart Pointers +In Rust, smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities. +Smart pointers in Rust often own the data they point to, while references only borrow data. + +## Further Information + +- [Smart Pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html) +- [Using Box to Point to Data on the Heap](https://doc.rust-lang.org/book/ch15-01-box.html) +- [Rc\, the Reference Counted Smart Pointer](https://doc.rust-lang.org/book/ch15-04-rc.html) +- [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html) +- [Cow Documentation](https://doc.rust-lang.org/std/borrow/enum.Cow.html) From 66eaaf7b6e5f7a5fe0ec0472c7ef9610b332bb2f Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:17:53 +0100 Subject: [PATCH 0100/1432] fixed formatting --- exercises/smart_pointers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/smart_pointers/README.md b/exercises/smart_pointers/README.md index 3893e78d5a..c517ae31a0 100644 --- a/exercises/smart_pointers/README.md +++ b/exercises/smart_pointers/README.md @@ -1,4 +1,4 @@ -## Smart Pointers +# Smart Pointers In Rust, smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities. Smart pointers in Rust often own the data they point to, while references only borrow data. From b2b6e6900fbe8b63a7d965240715ad56fa81403a Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:29:45 +0100 Subject: [PATCH 0101/1432] reformatted exercise->chapter mapping Added and removed rows according to changes to exercise order and grouping. --- exercises/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/exercises/README.md b/exercises/README.md index e52137caaa..26fcdf8fed 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -7,7 +7,7 @@ | if | Β§3.5 | | primitive_types | Β§3.2, Β§4.3 | | vecs | Β§8.1 | -| move_semantics | Β§4.1, Β§4.2 | +| move_semantics | Β§4.1-2 | | structs | Β§5.1, Β§5.3 | | enums | Β§6, Β§18.3 | | strings | Β§8.2 | @@ -19,8 +19,9 @@ | traits | Β§10.2 | | tests | Β§11.1 | | lifetimes | Β§10.3 | -| standard_library_types | Β§13.2, Β§15.1, Β§16.3 | -| threads | Β§16.1, Β§16.2, Β§16.3 | +| iterators | Β§13.2-4 | +| threads | Β§16.1-3 | +| smart_pointers | Β§15, Β§16.3 | | macros | Β§19.6 | | clippy | n/a | | conversions | n/a | From 9860976af92ea9578980587b57f992073f6ff5ea Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:34:58 +0100 Subject: [PATCH 0102/1432] added existing chapter for clippy to mapping It's real! https://doc.rust-lang.org/book/appendix-04-useful-development-tools.html --- exercises/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/README.md b/exercises/README.md index 26fcdf8fed..c7effa95b6 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -23,5 +23,5 @@ | threads | Β§16.1-3 | | smart_pointers | Β§15, Β§16.3 | | macros | Β§19.6 | -| clippy | n/a | +| clippy | Β§21.4 | | conversions | n/a | From a5429d59f9495535f5113a4236905d86df1657ff Mon Sep 17 00:00:00 2001 From: seporterfield <107010978+seporterfield@users.noreply.github.com> Date: Sun, 1 Jan 2023 02:46:40 +0100 Subject: [PATCH 0103/1432] updated file paths for iterators, smart_pointers --- info.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/info.toml b/info.toml index 7512aee3ec..1135c91482 100644 --- a/info.toml +++ b/info.toml @@ -809,7 +809,7 @@ If you use a lifetime annotation in a struct's fields, where else does it need t [[exercises]] name = "iterators1" -path = "exercises/standard_library_types/iterators1.rs" +path = "exercises/iterators/iterators1.rs" mode = "compile" hint = """ Step 1: @@ -826,7 +826,7 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. [[exercises]] name = "iterators2" -path = "exercises/standard_library_types/iterators2.rs" +path = "exercises/iterators/iterators2.rs" mode = "test" hint = """ Step 1 @@ -847,7 +847,7 @@ and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" -path = "exercises/standard_library_types/iterators3.rs" +path = "exercises/iterators/iterators3.rs" mode = "test" hint = """ The divide function needs to return the correct error when even division is not @@ -866,7 +866,7 @@ can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" -path = "exercises/standard_library_types/iterators4.rs" +path = "exercises/iterators/iterators4.rs" mode = "test" hint = """ In an imperative language, you might write a for loop that updates @@ -878,7 +878,7 @@ Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" -path = "exercises/standard_library_types/iterators5.rs" +path = "exercises/iterators/iterators5.rs" mode = "test" hint = """ The documentation for the std::iter::Iterator trait contains numerous methods @@ -960,7 +960,7 @@ See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. [[exercises]] name = "box1" -path = "exercises/standard_library_types/box1.rs" +path = "exercises/smart_pointers/box1.rs" mode = "test" hint = """ Step 1 @@ -978,7 +978,7 @@ and try other types! [[exercises]] name = "rc1" -path = "exercises/standard_library_types/rc1.rs" +path = "exercises/smart_pointers/rc1.rs" mode = "compile" hint = """ This is a straightforward exercise to use the Rc type. Each Planet has @@ -992,7 +992,7 @@ https://doc.rust-lang.org/book/ch15-04-rc.html [[exercises]] name = "arc1" -path = "exercises/standard_library_types/arc1.rs" +path = "exercises/smart_pointers/arc1.rs" mode = "compile" hint = """ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order @@ -1009,7 +1009,7 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" -path = "exercises/standard_library_types/cow1.rs" +path = "exercises/smart_pointers/cow1.rs" mode = "compile" hint = """ Since the vector is already owned, the `Cow` type doesn't need to clone it. From 3001f1ae02951e9194c0ed07755b1f150165dac2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 2 Jan 2023 09:52:14 +0000 Subject: [PATCH 0104/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 160eed5717..5f9dc6d354 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -245,6 +245,7 @@ authors. craymel
craymel

πŸ–‹ TK Buristrakul
TK Buristrakul

πŸ–‹ Kent Worthington
Kent Worthington

πŸ–‹ + seporterfield
seporterfield

πŸ–‹ From cb5371f86f36f941e5b1c2d473b536889f23c559 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 2 Jan 2023 09:52:15 +0000 Subject: [PATCH 0105/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 97d38f5be4..904d199411 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1722,6 +1722,15 @@ "contributions": [ "content" ] + }, + { + "login": "seporterfield", + "name": "seporterfield", + "avatar_url": "https://avatars.githubusercontent.com/u/107010978?v=4", + "profile": "https://github.com/seporterfield", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From e6bc13ff04502066540db427780c442fe9690295 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Tue, 3 Jan 2023 08:26:12 +0100 Subject: [PATCH 0106/1432] added common cargoBuildInputs to all flake outputs --- flake.nix | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index 3fabe0fef7..4fec6accde 100644 --- a/flake.nix +++ b/flake.nix @@ -14,14 +14,17 @@ flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; + + cargoBuildInputs = with pkgs; lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.CoreServices + ]; + rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; version = "5.2.1"; - buildInputs = with pkgs; lib.optionals stdenv.isDarwin [ - darwin.apple_sdk.frameworks.CoreServices - ]; + buildInputs = cargoBuildInputs; src = with pkgs.lib; cleanSourceWith { src = self; @@ -53,7 +56,9 @@ rustc rust-analyzer rustlings - ]; + rustfmt + clippy + ] ++ cargoBuildInputs; }; }); } From 7ed047436469b27174294ac47d623f2acfed38cb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:21:40 +0000 Subject: [PATCH 0107/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 5f9dc6d354..6d8a05e61f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -246,6 +246,7 @@ authors. TK Buristrakul
TK Buristrakul

πŸ–‹ Kent Worthington
Kent Worthington

πŸ–‹ seporterfield
seporterfield

πŸ–‹ + David Barroso
David Barroso

πŸš‡ From 2be582fb2794c7245b649b8c2821a42462d8c0c4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:21:41 +0000 Subject: [PATCH 0108/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 904d199411..b5b4ffdb2b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1731,6 +1731,15 @@ "contributions": [ "content" ] + }, + { + "login": "dbarrosop", + "name": "David Barroso", + "avatar_url": "https://avatars.githubusercontent.com/u/6246622?v=4", + "profile": "https://www.linkedin.com/in/dbarrosop", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From 1b9d4bbf723b9fc6a4f8fdd7bf10d6b39b3f2f62 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 3 Jan 2023 14:14:07 +0100 Subject: [PATCH 0109/1432] Fix typo in method name --- src/main.rs | 2 +- src/project.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6dc18e8be1..7e9156fb42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -239,7 +239,7 @@ fn main() { .get_sysroot_src() .expect("Couldn't find toolchain path, do you have `rustc` installed?"); project - .exercies_to_json() + .exercises_to_json() .expect("Couldn't parse rustlings exercises files"); if project.crates.is_empty() { diff --git a/src/project.rs b/src/project.rs index 0df00b9a2d..f093cdf5fd 100644 --- a/src/project.rs +++ b/src/project.rs @@ -54,7 +54,7 @@ impl RustAnalyzerProject { /// Parse the exercises folder for .rs files, any matches will create /// a new `crate` in rust-project.json which allows rust-analyzer to /// treat it like a normal binary - pub fn exercies_to_json(&mut self) -> Result<(), Box> { + pub fn exercises_to_json(&mut self) -> Result<(), Box> { for e in glob("./exercises/**/*")? { let path = e?.to_string_lossy().to_string(); self.path_to_json(path); From ceb03cfb2c8343c6b323764b448a7f643f470c61 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 13:19:32 +0000 Subject: [PATCH 0110/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 6d8a05e61f..06b568185e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -247,6 +247,7 @@ authors. Kent Worthington
Kent Worthington

πŸ–‹ seporterfield
seporterfield

πŸ–‹ David Barroso
David Barroso

πŸš‡ + Tobias Klauser
Tobias Klauser

πŸ’» From b985b1d6eaa3b0e696cf1d7c378abf47c37098fb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 13:19:33 +0000 Subject: [PATCH 0111/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b5b4ffdb2b..66056aa77b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1740,6 +1740,15 @@ "contributions": [ "infra" ] + }, + { + "login": "tklauser", + "name": "Tobias Klauser", + "avatar_url": "https://avatars.githubusercontent.com/u/539708?v=4", + "profile": "https://distanz.ch", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 57834e9f8c4a005ba210b69932e62506ea12a998 Mon Sep 17 00:00:00 2001 From: Jarrod Sanders <50600614+kawaiiPlat@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:22:52 -0500 Subject: [PATCH 0112/1432] Minor Grammar --- exercises/quiz2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 715788b808..5c42dae083 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -6,7 +6,7 @@ // - Modules // - Enums -// Let's build a little machine in form of a function. +// Let's build a little machine in the form of a function. // As input, we're going to give a list of strings and commands. These commands // determine what action is going to be applied to the string. It can either be: // - Uppercase the string From 951826e6b59ac930393c88c74f89c35fabb9fe8f Mon Sep 17 00:00:00 2001 From: himanshu soni Date: Tue, 3 Jan 2023 23:54:01 +0930 Subject: [PATCH 0113/1432] fix(verify): progress bar proportion now updates with the number of files verified --- src/verify.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/verify.rs b/src/verify.rs index 97471b8f38..cf319e473c 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -13,13 +13,15 @@ pub fn verify<'a>( progress: (usize, usize), verbose: bool, ) -> Result<(), &'a Exercise> { - let (num_done, total) = progress; + let (mut num_done, total) = progress; let bar = ProgressBar::new(total as u64); bar.set_style(ProgressStyle::default_bar() .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") .progress_chars("#>-") ); bar.set_position(num_done as u64); + bar.set_message(format!("({:.1} %)", 0.)); + for exercise in exercises { let compile_result = match exercise.mode { Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose), @@ -29,9 +31,10 @@ pub fn verify<'a>( if !compile_result.unwrap_or(false) { return Err(exercise); } + num_done += 1; let percentage = num_done as f32 / total as f32 * 100.0; - bar.set_message(format!("({:.1} %)", percentage)); bar.inc(1); + bar.set_message(format!("({:.1} %)", percentage)); } Ok(()) } From 8e36256eac3eeb289906a4f51d2a742d8ba45037 Mon Sep 17 00:00:00 2001 From: TenzinRabgy Date: Wed, 4 Jan 2023 04:49:51 -0500 Subject: [PATCH 0114/1432] chore(watch): decrease watch delay closes #1215 --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 7e9156fb42..59adb418c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -350,7 +350,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result { let (tx, rx) = channel(); let should_quit = Arc::new(AtomicBool::new(false)); - let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?; + let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(1))?; watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?; clear_screen(); From 87221720494d21958eb7e6e4abc4ee1c35f3f6e2 Mon Sep 17 00:00:00 2001 From: 0xMySt1c <0xMySt1c@tuta.io> Date: Wed, 4 Jan 2023 14:59:18 -0500 Subject: [PATCH 0115/1432] update rust language extension to rust-analyzer --- .gitpod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.yml b/.gitpod.yml index 73cb802da9..0691933560 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -4,4 +4,4 @@ tasks: vscode: extensions: - - rust-lang.rust@0.7.8 + - rust-lang.rust-analyzer@0.3.1348 From 33e0c73df76f5355a25660f19d6b977490621b39 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 14:21:44 +0000 Subject: [PATCH 0116/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 06b568185e..486f9b5b8a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -248,6 +248,7 @@ authors. seporterfield
seporterfield

πŸ–‹ David Barroso
David Barroso

πŸš‡ Tobias Klauser
Tobias Klauser

πŸ’» + 0xMySt1c
0xMySt1c

πŸ”§ From f67a2b25356beeed9e12f970fb90b03cd08b8ee7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 14:21:45 +0000 Subject: [PATCH 0117/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 66056aa77b..a466f16028 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1749,6 +1749,15 @@ "contributions": [ "code" ] + }, + { + "login": "0xMySt1c", + "name": "0xMySt1c", + "avatar_url": "https://avatars.githubusercontent.com/u/101825630?v=4", + "profile": "https://github.com/0xMySt1c", + "contributions": [ + "tool" + ] } ], "contributorsPerLine": 8, From 4179317f370718cc5ebfa8504d3423ef81572268 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 14:28:01 +0000 Subject: [PATCH 0118/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 486f9b5b8a..7d3d4f9d9c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -250,6 +250,9 @@ authors. Tobias Klauser
Tobias Klauser

πŸ’» 0xMySt1c
0xMySt1c

πŸ”§ + + Ten
Ten

πŸ’» + From 6c170c2c21a25ccfbc4687e6cfec824177d456bf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 14:28:02 +0000 Subject: [PATCH 0119/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index a466f16028..659c289b72 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1758,6 +1758,15 @@ "contributions": [ "tool" ] + }, + { + "login": "AxolotlTears", + "name": "Ten", + "avatar_url": "https://avatars.githubusercontent.com/u/87157047?v=4", + "profile": "https://github.com/AxolotlTears", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 7c4a3a2af6dde8ab77fde8433496148f94e5ec90 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 5 Jan 2023 17:14:49 +0100 Subject: [PATCH 0120/1432] Fix typo in clippy1 hint --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 1135c91482..66f78abb0e 100644 --- a/info.toml +++ b/info.toml @@ -1072,7 +1072,7 @@ name = "clippy1" path = "exercises/clippy/clippy1.rs" mode = "clippy" hint = """ -Rust stores the highest precision version of any long or inifinite precision +Rust stores the highest precision version of any long or infinite precision mathematical constants in the Rust standard library. https://doc.rust-lang.org/stable/std/f32/consts/index.html From 6b04848d413b09ee7925b280c38f57fdb35166a3 Mon Sep 17 00:00:00 2001 From: h4x5p4c3 Date: Wed, 11 Jan 2023 00:23:21 +0530 Subject: [PATCH 0121/1432] minor changes to hint --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 66f78abb0e..7418943b75 100644 --- a/info.toml +++ b/info.toml @@ -815,7 +815,7 @@ hint = """ Step 1: We need to apply something to the collection `my_fav_fruits` before we start to go through it. What could that be? Take a look at the struct definition for a vector for inspiration: -https://doc.rust-lang.org/std/vec/struct.Vec.html. +https://doc.rust-lang.org/std/vec/struct.Vec.html Step 2 & step 3: Very similar to the lines above and below. You've got this! Step 4: From 3ed1e16c78fd7d3d04a6be8ddbfb7899e76569a4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:23:07 +0000 Subject: [PATCH 0122/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 387 +++++++++++++++++++++++++++-------------------------- 1 file changed, 194 insertions(+), 193 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 7d3d4f9d9c..43216f1f77 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -11,247 +11,248 @@ authors. - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - + +
Carol (Nichols || Goulding)
Carol (Nichols || Goulding)

πŸ’» πŸ–‹
QuietMisdreavus
QuietMisdreavus

πŸ’» πŸ–‹
Robert M Lugg
Robert M Lugg

πŸ–‹
Hynek Schlawack
Hynek Schlawack

πŸ’»
Katharina Fey
Katharina Fey

πŸ’»
lukabavdaz
lukabavdaz

πŸ’» πŸ–‹
Erik Vesteraas
Erik Vesteraas

πŸ’»
delet0r
delet0r

πŸ’»
Carol (Nichols || Goulding)
Carol (Nichols || Goulding)

πŸ’» πŸ–‹
QuietMisdreavus
QuietMisdreavus

πŸ’» πŸ–‹
Robert M Lugg
Robert M Lugg

πŸ–‹
Hynek Schlawack
Hynek Schlawack

πŸ’»
Katharina Fey
Katharina Fey

πŸ’»
lukabavdaz
lukabavdaz

πŸ’» πŸ–‹
Erik Vesteraas
Erik Vesteraas

πŸ’»
delet0r
delet0r

πŸ’»
Shaun Bennett
Shaun Bennett

πŸ’»
Andrew Bagshaw
Andrew Bagshaw

πŸ’»
Kyle Isom
Kyle Isom

πŸ’»
Colin Pitrat
Colin Pitrat

πŸ’»
Zac Anger
Zac Anger

πŸ’»
Matthias Geier
Matthias Geier

πŸ’»
Chris Pearce
Chris Pearce

πŸ’»
Yvan Sraka
Yvan Sraka

πŸ’»
Shaun Bennett
Shaun Bennett

πŸ’»
Andrew Bagshaw
Andrew Bagshaw

πŸ’»
Kyle Isom
Kyle Isom

πŸ’»
Colin Pitrat
Colin Pitrat

πŸ’»
Zac Anger
Zac Anger

πŸ’»
Matthias Geier
Matthias Geier

πŸ’»
Chris Pearce
Chris Pearce

πŸ’»
Yvan Sraka
Yvan Sraka

πŸ’»
Denys Smirnov
Denys Smirnov

πŸ’»
eddyp
eddyp

πŸ’»
Brian Kung
Brian Kung

πŸ’» πŸ–‹
Russell
Russell

πŸ’»
Dan Wilhelm
Dan Wilhelm

πŸ“–
Jesse
Jesse

πŸ’» πŸ–‹
Fredrik JambrΓ©n
Fredrik JambrΓ©n

πŸ’»
Pete McFarlane
Pete McFarlane

πŸ–‹
Denys Smirnov
Denys Smirnov

πŸ’»
eddyp
eddyp

πŸ’»
Brian Kung
Brian Kung

πŸ’» πŸ–‹
Russell
Russell

πŸ’»
Dan Wilhelm
Dan Wilhelm

πŸ“–
Jesse
Jesse

πŸ’» πŸ–‹
Fredrik JambrΓ©n
Fredrik JambrΓ©n

πŸ’»
Pete McFarlane
Pete McFarlane

πŸ–‹
nkanderson
nkanderson

πŸ’» πŸ–‹
Ajax M
Ajax M

πŸ“–
Dylan Nugent
Dylan Nugent

πŸ–‹
vyaslav
vyaslav

πŸ’» πŸ–‹
George
George

πŸ’»
Thomas Holloway
Thomas Holloway

πŸ’» πŸ–‹
Jubilee
Jubilee

πŸ’»
WofWca
WofWca

πŸ’»
nkanderson
nkanderson

πŸ’» πŸ–‹
Ajax M
Ajax M

πŸ“–
Dylan Nugent
Dylan Nugent

πŸ–‹
vyaslav
vyaslav

πŸ’» πŸ–‹
George
George

πŸ’»
Thomas Holloway
Thomas Holloway

πŸ’» πŸ–‹
Jubilee
Jubilee

πŸ’»
WofWca
WofWca

πŸ’»
Roberto Vidal
Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧
Jens
Jens

πŸ“–
Rahat Ahmed
Rahat Ahmed

πŸ“–
Abdou Seck
Abdou Seck

πŸ’» πŸ–‹ πŸ‘€
Katie
Katie

πŸ’»
Socrates
Socrates

πŸ“–
gnodarse
gnodarse

πŸ–‹
Harrison Metzger
Harrison Metzger

πŸ’»
Roberto Vidal
Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧
Jens
Jens

πŸ“–
Rahat Ahmed
Rahat Ahmed

πŸ“–
Abdou Seck
Abdou Seck

πŸ’» πŸ–‹ πŸ‘€
Katie
Katie

πŸ’»
Socrates
Socrates

πŸ“–
gnodarse
gnodarse

πŸ–‹
Harrison Metzger
Harrison Metzger

πŸ’»
Torben Jonas
Torben Jonas

πŸ’» πŸ–‹
Paul Bissex
Paul Bissex

πŸ“–
Steven Mann
Steven Mann

πŸ’» πŸ–‹
Mario Reder
Mario Reder

πŸ’» πŸ–‹
skim
skim

πŸ’»
Sanjay K
Sanjay K

πŸ’» πŸ–‹
Rohan Jain
Rohan Jain

πŸ’»
Said Aspen
Said Aspen

πŸ’» πŸ–‹
Torben Jonas
Torben Jonas

πŸ’» πŸ–‹
Paul Bissex
Paul Bissex

πŸ“–
Steven Mann
Steven Mann

πŸ’» πŸ–‹
Mario Reder
Mario Reder

πŸ’» πŸ–‹
skim
skim

πŸ’»
Sanjay K
Sanjay K

πŸ’» πŸ–‹
Rohan Jain
Rohan Jain

πŸ’»
Said Aspen
Said Aspen

πŸ’» πŸ–‹
Ufuk Celebi
Ufuk Celebi

πŸ’»
lebedevsergey
lebedevsergey

πŸ“–
Aleksei Trifonov
Aleksei Trifonov

πŸ–‹
Darren Meehan
Darren Meehan

πŸ–‹
Jihchi Lee
Jihchi Lee

πŸ–‹
Christofer Bertonha
Christofer Bertonha

πŸ–‹
Vivek Bharath Akupatni
Vivek Bharath Akupatni

πŸ’» ⚠️
DΓ­dac SementΓ© FernΓ‘ndez
DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹
Ufuk Celebi
Ufuk Celebi

πŸ’»
lebedevsergey
lebedevsergey

πŸ“–
Aleksei Trifonov
Aleksei Trifonov

πŸ–‹
Darren Meehan
Darren Meehan

πŸ–‹
Jihchi Lee
Jihchi Lee

πŸ–‹
Christofer Bertonha
Christofer Bertonha

πŸ–‹
Vivek Bharath Akupatni
Vivek Bharath Akupatni

πŸ’» ⚠️
DΓ­dac SementΓ© FernΓ‘ndez
DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹
Rob Story
Rob Story

πŸ’»
Siobhan Jacobson
Siobhan Jacobson

πŸ’»
Evan Carroll
Evan Carroll

πŸ–‹
Jawaad Mahmood
Jawaad Mahmood

πŸ–‹
Gaurang Tandon
Gaurang Tandon

πŸ–‹
Stefan Kupresak
Stefan Kupresak

πŸ–‹
Greg Leonard
Greg Leonard

πŸ–‹
Ryan McQuen
Ryan McQuen

πŸ’»
Rob Story
Rob Story

πŸ’»
Siobhan Jacobson
Siobhan Jacobson

πŸ’»
Evan Carroll
Evan Carroll

πŸ–‹
Jawaad Mahmood
Jawaad Mahmood

πŸ–‹
Gaurang Tandon
Gaurang Tandon

πŸ–‹
Stefan Kupresak
Stefan Kupresak

πŸ–‹
Greg Leonard
Greg Leonard

πŸ–‹
Ryan McQuen
Ryan McQuen

πŸ’»
Annika
Annika

πŸ‘€
Axel Viala
Axel Viala

πŸ’»
Mohammed Sazid Al Rashid
Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»
Caleb Webber
Caleb Webber

🚧
Peter N
Peter N

🚧
seancad
seancad

🚧
Will Hayworth
Will Hayworth

πŸ–‹
Christian Zeller
Christian Zeller

πŸ–‹
Annika
Annika

πŸ‘€
Axel Viala
Axel Viala

πŸ’»
Mohammed Sazid Al Rashid
Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»
Caleb Webber
Caleb Webber

🚧
Peter N
Peter N

🚧
seancad
seancad

🚧
Will Hayworth
Will Hayworth

πŸ–‹
Christian Zeller
Christian Zeller

πŸ–‹
Jean-Francois Chevrette
Jean-Francois Chevrette

πŸ–‹ πŸ’»
John Baber-Lucero
John Baber-Lucero

πŸ–‹
Tal
Tal

πŸ–‹
apogeeoak
apogeeoak

πŸ–‹ πŸ’»
Larry Garfield
Larry Garfield

πŸ–‹
circumspect
circumspect

πŸ–‹
Cyrus Wyett
Cyrus Wyett

πŸ–‹
cadolphs
cadolphs

πŸ’»
Jean-Francois Chevrette
Jean-Francois Chevrette

πŸ–‹ πŸ’»
John Baber-Lucero
John Baber-Lucero

πŸ–‹
Tal
Tal

πŸ–‹
apogeeoak
apogeeoak

πŸ–‹ πŸ’»
Larry Garfield
Larry Garfield

πŸ–‹
circumspect
circumspect

πŸ–‹
Cyrus Wyett
Cyrus Wyett

πŸ–‹
cadolphs
cadolphs

πŸ’»
Pascal H.
Pascal H.

πŸ–‹
Rod Elias
Rod Elias

πŸ–‹
Matt Lebl
Matt Lebl

πŸ’»
Ignacio Le Fluk
Ignacio Le Fluk

πŸ–‹
Taylor Yu
Taylor Yu

πŸ’» πŸ–‹
Patrick Hintermayer
Patrick Hintermayer

πŸ’»
Pete Pavlovski
Pete Pavlovski

πŸ–‹
k12ish
k12ish

πŸ–‹
Pascal H.
Pascal H.

πŸ–‹
Rod Elias
Rod Elias

πŸ–‹
Matt Lebl
Matt Lebl

πŸ’»
Ignacio Le Fluk
Ignacio Le Fluk

πŸ–‹
Taylor Yu
Taylor Yu

πŸ’» πŸ–‹
Patrick Hintermayer
Patrick Hintermayer

πŸ’»
Pete Pavlovski
Pete Pavlovski

πŸ–‹
k12ish
k12ish

πŸ–‹
Shao Yang Hong
Shao Yang Hong

πŸ–‹
Brandon Macer
Brandon Macer

πŸ–‹
Stoian Dan
Stoian Dan

πŸ–‹
Pi Delport
Pi Delport

πŸ–‹
Sateesh
Sateesh

πŸ’» πŸ–‹
ZC
ZC

πŸ–‹
hyperparabolic
hyperparabolic

πŸ’»
arlecchino
arlecchino

πŸ“–
Shao Yang Hong
Shao Yang Hong

πŸ–‹
Brandon Macer
Brandon Macer

πŸ–‹
Stoian Dan
Stoian Dan

πŸ–‹
Pi Delport
Pi Delport

πŸ–‹
Sateesh
Sateesh

πŸ’» πŸ–‹
ZC
ZC

πŸ–‹
hyperparabolic
hyperparabolic

πŸ’»
arlecchino
arlecchino

πŸ“–
Richthofen
Richthofen

πŸ’»
Ivan Nerazumov
Ivan Nerazumov

πŸ“–
lauralindzey
lauralindzey

πŸ“–
Rakshit Sinha
Rakshit Sinha

πŸ–‹
Damian
Damian

πŸ–‹
Ben Armstead
Ben Armstead

πŸ’»
anuk909
anuk909

πŸ–‹ πŸ’»
granddaifuku
granddaifuku

πŸ–‹
Richthofen
Richthofen

πŸ’»
Ivan Nerazumov
Ivan Nerazumov

πŸ“–
lauralindzey
lauralindzey

πŸ“–
Rakshit Sinha
Rakshit Sinha

πŸ–‹
Damian
Damian

πŸ–‹
Ben Armstead
Ben Armstead

πŸ’»
anuk909
anuk909

πŸ–‹ πŸ’»
granddaifuku
granddaifuku

πŸ–‹
Weilet
Weilet

πŸ–‹
LIU JIE
LIU JIE

πŸ–‹
Antoine BΓΌsch
Antoine BΓΌsch

πŸ’»
frogtd
frogtd

πŸ–‹
Zhenghao Lu
Zhenghao Lu

πŸ–‹
Fredrik Enestad
Fredrik Enestad

πŸ–‹
xuesong
xuesong

πŸ–‹
Michael Walsh
Michael Walsh

πŸ’»
Weilet
Weilet

πŸ–‹
LIU JIE
LIU JIE

πŸ–‹
Antoine BΓΌsch
Antoine BΓΌsch

πŸ’»
frogtd
frogtd

πŸ–‹
Zhenghao Lu
Zhenghao Lu

πŸ–‹
Fredrik Enestad
Fredrik Enestad

πŸ–‹
xuesong
xuesong

πŸ–‹
Michael Walsh
Michael Walsh

πŸ’»
alirezaghey
alirezaghey

πŸ–‹
Franklin van Nes
Franklin van Nes

πŸ’»
nekonako
nekonako

πŸ’»
ZX
ZX

πŸ–‹
Yang Wen
Yang Wen

πŸ–‹
Brandon High
Brandon High

πŸ“–
x-hgg-x
x-hgg-x

πŸ’»
Kisaragi
Kisaragi

πŸ“–
alirezaghey
alirezaghey

πŸ–‹
Franklin van Nes
Franklin van Nes

πŸ’»
nekonako
nekonako

πŸ’»
ZX
ZX

πŸ–‹
Yang Wen
Yang Wen

πŸ–‹
Brandon High
Brandon High

πŸ“–
x-hgg-x
x-hgg-x

πŸ’»
Kisaragi
Kisaragi

πŸ“–
Lucas Aries
Lucas Aries

πŸ–‹
ragreenburg
ragreenburg

πŸ–‹
stevenfukase
stevenfukase

πŸ–‹
J-S-Kim
J-S-Kim

πŸ–‹
Fointard
Fointard

πŸ–‹
Ryan Lowe
Ryan Lowe

πŸ’»
cui fliter
cui fliter

πŸ–‹
Ron Lusk
Ron Lusk

πŸ–‹
Lucas Aries
Lucas Aries

πŸ–‹
ragreenburg
ragreenburg

πŸ–‹
stevenfukase
stevenfukase

πŸ–‹
J-S-Kim
J-S-Kim

πŸ–‹
Fointard
Fointard

πŸ–‹
Ryan Lowe
Ryan Lowe

πŸ’»
cui fliter
cui fliter

πŸ–‹
Ron Lusk
Ron Lusk

πŸ–‹
Bryan Lee
Bryan Lee

πŸ–‹
Nandaja Varma
Nandaja Varma

πŸ“–
pwygab
pwygab

πŸ’»
Lucas Grigolon Varela
Lucas Grigolon Varela

πŸ–‹
Bufo
Bufo

πŸ–‹
Jack Clayton
Jack Clayton

πŸ’»
Konstantin
Konstantin

πŸ–‹
0pling
0pling

πŸ–‹
Bryan Lee
Bryan Lee

πŸ–‹
Nandaja Varma
Nandaja Varma

πŸ“–
pwygab
pwygab

πŸ’»
Lucas Grigolon Varela
Lucas Grigolon Varela

πŸ–‹
Bufo
Bufo

πŸ–‹
Jack Clayton
Jack Clayton

πŸ’»
Konstantin
Konstantin

πŸ–‹
0pling
0pling

πŸ–‹
KatanaFluorescent
KatanaFluorescent

πŸ’»
Drew Morris
Drew Morris

πŸ’»
camperdue42
camperdue42

πŸ–‹
YsuOS
YsuOS

πŸ–‹
Steven Nguyen
Steven Nguyen

πŸ–‹
nacairns1
nacairns1

πŸ–‹
Paulo Gabriel Justino Bezerra
Paulo Gabriel Justino Bezerra

πŸ–‹
Jason
Jason

πŸ–‹
KatanaFluorescent
KatanaFluorescent

πŸ’»
Drew Morris
Drew Morris

πŸ’»
camperdue42
camperdue42

πŸ–‹
YsuOS
YsuOS

πŸ–‹
Steven Nguyen
Steven Nguyen

πŸ–‹
nacairns1
nacairns1

πŸ–‹
Paulo Gabriel Justino Bezerra
Paulo Gabriel Justino Bezerra

πŸ–‹
Jason
Jason

πŸ–‹
exdx
exdx

πŸ–‹
James Zow
James Zow

πŸ–‹
James Bromley
James Bromley

πŸ–‹
swhiteCQC
swhiteCQC

πŸ–‹
Neil Pate
Neil Pate

πŸ–‹
wojexe
wojexe

πŸ–‹
Mattia Schiavon
Mattia Schiavon

πŸ–‹
Eric Jolibois
Eric Jolibois

πŸ–‹
exdx
exdx

πŸ–‹
James Zow
James Zow

πŸ–‹
James Bromley
James Bromley

πŸ–‹
swhiteCQC
swhiteCQC

πŸ–‹
Neil Pate
Neil Pate

πŸ–‹
wojexe
wojexe

πŸ–‹
Mattia Schiavon
Mattia Schiavon

πŸ–‹
Eric Jolibois
Eric Jolibois

πŸ–‹
Edwin Chang
Edwin Chang

πŸ–‹
Saikat Das
Saikat Das

πŸ–‹
Jeremy Goh
Jeremy Goh

πŸ–‹
Lioness100
Lioness100

πŸ–‹
Tristan Nicholls
Tristan Nicholls

πŸ–‹
Claire
Claire

πŸ–‹
Maurice Van Wassenhove
Maurice Van Wassenhove

πŸ–‹
John Mendelewski
John Mendelewski

πŸ’»
Edwin Chang
Edwin Chang

πŸ–‹
Saikat Das
Saikat Das

πŸ–‹
Jeremy Goh
Jeremy Goh

πŸ–‹
Lioness100
Lioness100

πŸ–‹
Tristan Nicholls
Tristan Nicholls

πŸ–‹
Claire
Claire

πŸ–‹
Maurice Van Wassenhove
Maurice Van Wassenhove

πŸ–‹
John Mendelewski
John Mendelewski

πŸ’»
Brian Fakhoury
Brian Fakhoury

πŸ–‹
Markus Boehme
Markus Boehme

πŸ’»
Nico Vromans
Nico Vromans

πŸ–‹
vostok92
vostok92

πŸ–‹
Magnus RΓΈdseth
Magnus RΓΈdseth

πŸ–‹
rubiesonthesky
rubiesonthesky

πŸ–‹
Gabriel Bianconi
Gabriel Bianconi

πŸ–‹
Kody Low
Kody Low

πŸ–‹
Brian Fakhoury
Brian Fakhoury

πŸ–‹
Markus Boehme
Markus Boehme

πŸ’»
Nico Vromans
Nico Vromans

πŸ–‹
vostok92
vostok92

πŸ–‹
Magnus RΓΈdseth
Magnus RΓΈdseth

πŸ–‹
rubiesonthesky
rubiesonthesky

πŸ–‹
Gabriel Bianconi
Gabriel Bianconi

πŸ–‹
Kody Low
Kody Low

πŸ–‹
rzrymiak
rzrymiak

πŸ–‹
Miguel Raz GuzmΓ‘n Macedo
Miguel Raz GuzmΓ‘n Macedo

πŸ–‹
Magnus Markling
Magnus Markling

πŸ–‹
Tiago De Gaspari
Tiago De Gaspari

πŸ–‹
skaunov
skaunov

πŸ–‹
Cal Jacobson
Cal Jacobson

πŸ–‹
Duchoud Nicolas
Duchoud Nicolas

πŸ–‹
Gaëtan Faugère
Gaëtan Faugère

πŸ”§
rzrymiak
rzrymiak

πŸ–‹
Miguel Raz GuzmΓ‘n Macedo
Miguel Raz GuzmΓ‘n Macedo

πŸ–‹
Magnus Markling
Magnus Markling

πŸ–‹
Tiago De Gaspari
Tiago De Gaspari

πŸ–‹
skaunov
skaunov

πŸ–‹
Cal Jacobson
Cal Jacobson

πŸ–‹
Duchoud Nicolas
Duchoud Nicolas

πŸ–‹
Gaëtan Faugère
Gaëtan Faugère

πŸ”§
bhbuehler
bhbuehler

πŸ–‹
Yuri Astrakhan
Yuri Astrakhan

πŸ’»
azzamsa
azzamsa

πŸ’»
mvanschellebeeck
mvanschellebeeck

πŸ–‹
Arkid
Arkid

πŸ–‹
Tom Kunc
Tom Kunc

πŸ–‹
Marek FurΓ‘k
Marek FurΓ‘k

πŸ–‹
Winter
Winter

πŸ’»
bhbuehler
bhbuehler

πŸ–‹
Yuri Astrakhan
Yuri Astrakhan

πŸ’»
azzamsa
azzamsa

πŸ’»
mvanschellebeeck
mvanschellebeeck

πŸ–‹
Arkid
Arkid

πŸ–‹
Tom Kunc
Tom Kunc

πŸ–‹
Marek FurΓ‘k
Marek FurΓ‘k

πŸ–‹
Winter
Winter

πŸ’»
Moritz BΓΆhme
Moritz BΓΆhme

πŸ’»
craymel
craymel

πŸ–‹
TK Buristrakul
TK Buristrakul

πŸ–‹
Kent Worthington
Kent Worthington

πŸ–‹
seporterfield
seporterfield

πŸ–‹
David Barroso
David Barroso

πŸš‡
Tobias Klauser
Tobias Klauser

πŸ’»
0xMySt1c
0xMySt1c

πŸ”§
Moritz BΓΆhme
Moritz BΓΆhme

πŸ’»
craymel
craymel

πŸ–‹
TK Buristrakul
TK Buristrakul

πŸ–‹
Kent Worthington
Kent Worthington

πŸ–‹
seporterfield
seporterfield

πŸ–‹
David Barroso
David Barroso

πŸš‡
Tobias Klauser
Tobias Klauser

πŸ’»
0xMySt1c
0xMySt1c

πŸ”§
Ten
Ten

πŸ’»
Ten
Ten

πŸ’»
jones martin
jones martin

πŸ–‹
From ac839c267b901217af8eaf6f37791d53df35fe26 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:23:08 +0000 Subject: [PATCH 0123/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 659c289b72..cf99dc2404 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1767,6 +1767,15 @@ "contributions": [ "code" ] + }, + { + "login": "h4x5p4c3", + "name": "jones martin", + "avatar_url": "https://avatars.githubusercontent.com/u/66133688?v=4", + "profile": "http://h4x5p4c3.xyz", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From d05a817f3b90ef0275dbdc0ec19f6514434be163 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:25:35 +0000 Subject: [PATCH 0124/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 43216f1f77..fa51aa5b86 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -253,6 +253,7 @@ authors. Ten
Ten

πŸ’» jones martin
jones martin

πŸ–‹ + cloppingemu
cloppingemu

πŸ’» From d58c97bd21d54195c33c9743a7f7532c4c7ef8e7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:25:36 +0000 Subject: [PATCH 0125/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index cf99dc2404..2613d26841 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1776,6 +1776,15 @@ "contributions": [ "content" ] + }, + { + "login": "cloppingemu", + "name": "cloppingemu", + "avatar_url": "https://avatars.githubusercontent.com/u/12227963?v=4", + "profile": "https://github.com/cloppingemu", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 4771a7b9e70a05820bd418c155ecee5beacb2771 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Mon, 23 Jan 2023 12:16:51 +0800 Subject: [PATCH 0126/1432] chore: fix prompt error Missed a blank line, which causes the prompt incorrect like below: ```rust You can keep working on this exercise, or jump into the next one by removing the `I AM NOT DONE` comment: 6 | // Make this code compile by using the proper Rc primitives to express that the sun has multiple owners. 7 | 8 | // I AM NOT DONE 9 | use std::rc::Rc; ``` --- exercises/smart_pointers/rc1.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/smart_pointers/rc1.rs b/exercises/smart_pointers/rc1.rs index 9b907fde8c..d62f36192f 100644 --- a/exercises/smart_pointers/rc1.rs +++ b/exercises/smart_pointers/rc1.rs @@ -6,6 +6,7 @@ // Make this code compile by using the proper Rc primitives to express that the sun has multiple owners. // I AM NOT DONE + use std::rc::Rc; #[derive(Debug)] From a1b29648696216c5d281f6b7d28c630629255913 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 26 Jan 2023 19:55:13 +0000 Subject: [PATCH 0127/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index fa51aa5b86..94c93d258c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -254,6 +254,7 @@ authors. Ten
Ten

πŸ’» jones martin
jones martin

πŸ–‹ cloppingemu
cloppingemu

πŸ’» + Kevin Wan
Kevin Wan

πŸ–‹ From c9526043080b7e5d49841bd6ee6792ed1948a7ce Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 26 Jan 2023 19:55:14 +0000 Subject: [PATCH 0128/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2613d26841..7864aab1e0 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1785,6 +1785,15 @@ "contributions": [ "code" ] + }, + { + "login": "kevwan", + "name": "Kevin Wan", + "avatar_url": "https://avatars.githubusercontent.com/u/1918356?v=4", + "profile": "http://github.com/zeromicro/go-zero", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 1721ddc231b197b6a4ac44c05ab4f558b91f8098 Mon Sep 17 00:00:00 2001 From: wjwrh Date: Sun, 5 Feb 2023 14:10:23 +0800 Subject: [PATCH 0129/1432] Fix the problem of different edition between rustc and rust-analyzer --- src/exercise.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index c0dae34ea9..2cde4e1592 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use std::process::{self, Command}; const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; +const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE"; const CONTEXT: usize = 2; const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/clippy/Cargo.toml"; @@ -111,10 +112,12 @@ impl Exercise { Mode::Compile => Command::new("rustc") .args(&[self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) + .args(RUSTC_EDITION_ARGS) .output(), Mode::Test => Command::new("rustc") .args(&["--test", self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) + .args(RUSTC_EDITION_ARGS) .output(), Mode::Clippy => { let cargo_toml = format!( @@ -140,6 +143,7 @@ path = "{}.rs""#, Command::new("rustc") .args(&[self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) + .args(RUSTC_EDITION_ARGS) .output() .expect("Failed to compile!"); // Due to an issue with Clippy, a cargo clean is required to catch all lints. @@ -154,7 +158,7 @@ path = "{}.rs""#, Command::new("cargo") .args(&["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(RUSTC_COLOR_ARGS) - .args(&["--", "-D", "warnings","-D","clippy::float_cmp"]) + .args(&["--", "-D", "warnings", "-D", "clippy::float_cmp"]) .output() } } From ff44be9dcd8b248962bcd1479e134ac093af6f08 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 10:51:49 +0000 Subject: [PATCH 0130/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 94c93d258c..e978f1accb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -255,6 +255,7 @@ authors. jones martin
jones martin

πŸ–‹ cloppingemu
cloppingemu

πŸ’» Kevin Wan
Kevin Wan

πŸ–‹ + Ruby
Ruby

πŸ’» From 72fff8d51a1c362a2ca7902b4aab3d6e9a1460a4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 10:51:50 +0000 Subject: [PATCH 0131/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7864aab1e0..049d0f5728 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1794,6 +1794,15 @@ "contributions": [ "content" ] + }, + { + "login": "wjwrh", + "name": "Ruby", + "avatar_url": "https://avatars.githubusercontent.com/u/43495006?v=4", + "profile": "http://kurowasaruby.cn", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 4bdd3c036bb6e532cb90b28a0502ccfda3ec4f2b Mon Sep 17 00:00:00 2001 From: Alexander Gill Date: Sat, 11 Feb 2023 23:18:34 +0000 Subject: [PATCH 0132/1432] fix(installation): loop to `max_len-1` avoid `i` going outside range of array indices --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index f3b3f33df7..06f94645c4 100755 --- a/install.sh +++ b/install.sh @@ -99,7 +99,7 @@ function vercomp() { done fi - for i in `seq 0 $max_len` + for i in `seq 0 $((max_len-1))` do # Fill empty fields with zeros in v1 if [ -z "${v1[$i]}" ] From 0b119339eb15f92bd6b4fe111f0678f01e972e3b Mon Sep 17 00:00:00 2001 From: Alexander Gill Date: Sat, 11 Feb 2023 23:30:35 +0000 Subject: [PATCH 0133/1432] fix(installation): bump `MinRustVersion` to 1.58 this is the earliest minor version that compiled without errors --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 06f94645c4..1929691097 100755 --- a/install.sh +++ b/install.sh @@ -124,7 +124,7 @@ function vercomp() { } RustVersion=$(rustc --version | cut -d " " -f 2) -MinRustVersion=1.56 +MinRustVersion=1.58 vercomp "$RustVersion" $MinRustVersion || ec=$? if [ ${ec:-0} -eq 2 ] then From acd2164c8523236c8b30f68f3120712e05f8d1a7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:43:07 +0000 Subject: [PATCH 0134/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index e978f1accb..b5a071d5c5 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -256,6 +256,7 @@ authors. cloppingemu
cloppingemu

πŸ’» Kevin Wan
Kevin Wan

πŸ–‹ Ruby
Ruby

πŸ’» + Alexander Gill
Alexander Gill

πŸ’» From 88a92b311c5dff95b7b78f73674f2d42c668ee4e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:43:08 +0000 Subject: [PATCH 0135/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 049d0f5728..f06f81c609 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1803,6 +1803,15 @@ "contributions": [ "code" ] + }, + { + "login": "alexandergill", + "name": "Alexander Gill", + "avatar_url": "https://avatars.githubusercontent.com/u/7033716?v=4", + "profile": "https://github.com/alexandergill", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 957522a4926afafd2ea577791d1f21d8532b1bca Mon Sep 17 00:00:00 2001 From: liv Date: Sun, 12 Feb 2023 15:47:58 +0100 Subject: [PATCH 0136/1432] feat(intro1): add note on rust-analyzer usage --- exercises/intro/intro1.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/exercises/intro/intro1.rs b/exercises/intro/intro1.rs index 45c5acbaa6..cfc55c306f 100644 --- a/exercises/intro/intro1.rs +++ b/exercises/intro/intro1.rs @@ -26,5 +26,12 @@ fn main() { println!("solve the exercises. Good luck!"); println!(); println!("The source for this exercise is in `exercises/intro/intro1.rs`. Have a look!"); - println!("Going forward, the source of the exercises will always be in the success/failure output."); + println!( + "Going forward, the source of the exercises will always be in the success/failure output." + ); + println!(); + println!( + "If you want to use rust-analyzer, Rust's LSP implementation, make sure your editor is set" + ); + println!("up, and then run `rustlings lsp` before continuing.") } From e8683274ff4f452f4ad2a680285eb4ff1553d479 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:51:59 +0000 Subject: [PATCH 0137/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index b5a071d5c5..88a04bdaf2 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -257,6 +257,7 @@ authors. Kevin Wan
Kevin Wan

πŸ–‹ Ruby
Ruby

πŸ’» Alexander Gill
Alexander Gill

πŸ’» + Jarrod Sanders
Jarrod Sanders

πŸ–‹ From 2aad5360d466e1b3412d171938a1ae016d9bb7b2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:52:00 +0000 Subject: [PATCH 0138/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f06f81c609..b6c182f91e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1812,6 +1812,15 @@ "contributions": [ "code" ] + }, + { + "login": "kawaiiPlat", + "name": "Jarrod Sanders", + "avatar_url": "https://avatars.githubusercontent.com/u/50600614?v=4", + "profile": "https://www.linkedin.com/in/jarrod-sanders/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 18843cf62e012cf129e51dcaa5be034caa93e59f Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 15:50:17 +0000 Subject: [PATCH 0139/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 88a04bdaf2..4648d6df45 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -258,6 +258,7 @@ authors. Ruby
Ruby

πŸ’» Alexander Gill
Alexander Gill

πŸ’» Jarrod Sanders
Jarrod Sanders

πŸ–‹ + Andrew Sen
Andrew Sen

πŸ–‹ From f3773660b04e7afb266474edbd99b537331d616d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 15:50:18 +0000 Subject: [PATCH 0140/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b6c182f91e..b44d8d1abd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1821,6 +1821,15 @@ "contributions": [ "content" ] + }, + { + "login": "platformer", + "name": "Andrew Sen", + "avatar_url": "https://avatars.githubusercontent.com/u/40146328?v=4", + "profile": "https://github.com/platformer", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From d3ed9ce2f6df372bb1b3d368ad53a794e3cd1ce9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 15:54:56 +0000 Subject: [PATCH 0141/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4648d6df45..85cd1774cb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -260,6 +260,9 @@ authors. Jarrod Sanders
Jarrod Sanders

πŸ–‹ Andrew Sen
Andrew Sen

πŸ–‹ + + Grzegorz Ε»ur
Grzegorz Ε»ur

πŸ–‹ + From bf0fa8ee64971383737327dbe7cde56ce3757e4d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 12 Feb 2023 15:54:57 +0000 Subject: [PATCH 0142/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b44d8d1abd..5db992ac5e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1830,6 +1830,15 @@ "contributions": [ "content" ] + }, + { + "login": "grzegorz-zur", + "name": "Grzegorz Ε»ur", + "avatar_url": "https://avatars.githubusercontent.com/u/5297583?v=4", + "profile": "https://grzegorz-zur.com/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From fc9fb536ca8e2fd0121e0f52719bc838506f68fa Mon Sep 17 00:00:00 2001 From: liv Date: Sun, 12 Feb 2023 18:01:48 +0100 Subject: [PATCH 0143/1432] release: 5.4.0 --- CHANGELOG.md | 563 ++++++++++++++++++++++++++++----------------------- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 9 +- flake.nix | 2 +- src/main.rs | 2 +- 6 files changed, 314 insertions(+), 266 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c351fce81f..63e448b9e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,35 @@ + + +## 5.4.0 (2023-02-12) + +#### Changed + +- Reordered exercises + - Unwrapped `standard_library_types` into `iterators` and `smart_pointers` + - Moved smart pointer exercises behind threads + - Ordered `rc1` before `arc1` +- **intro1**: Added a note on `rustlings lsp` +- **threads1**: Panic if threads are not joined +- **cli**: + - Made progress bar update proportional to amount of files verified + - Decreased `watch` delay from 2 to 1 second + +#### Fixed + +- Capitalized "Rust" in exercise hints +- **enums3**: Removed superfluous tuple brackets +- **quiz2, clippy1, iterators1**: Fixed a typo +- **rc1**: Fixed a prompt error +- **cli**: + - Fixed a typo in a method name + - Specified the edition in `rustc` commands + +#### Housekeeping + +- Bumped min Rust version to 1.58 in installation script + + ## 5.3.0 (2022-12-23) #### Added @@ -32,6 +63,7 @@ - Added a note on Windows PowerShell and other shell compatibility + ## 5.2.1 (2022-09-06) #### Fixed @@ -46,6 +78,7 @@ - Fixed a typo in README.md + ## 5.2.0 (2022-08-27) #### Added @@ -63,6 +96,7 @@ the tests + ## 5.1.1 (2022-08-17) #### Bug Fixes @@ -70,6 +104,7 @@ - Fixed an incorrect assertion in options1 + ## 5.1.0 (2022-08-16) #### Features @@ -105,6 +140,7 @@ - Added a link to our Zulip in the readme file + ## 5.0.0 (2022-07-16) #### Features @@ -162,7 +198,7 @@ - **from_str**: Added a hint comment about string error message conversion with `Box`. - **try_from_into**: Fixed the function name in comment. - + #### Removed - Removed the legacy LSP feature that was using `mod.rs` files. @@ -178,6 +214,7 @@ - Added a GitHub actions config so that tests run on every PR/commit. + ## 4.8.0 (2022-07-01) #### Features @@ -199,6 +236,7 @@ - Removed the deprecated Rust GitPod extension. + ## 4.7.1 (2022-04-20) #### Features @@ -220,422 +258,424 @@ Git log. -## 4.7.0 (2022-04-14) +## 4.7.0 (2022-04-14) #### Features -* Add move_semantics6.rs exercise (#908) ([3f0e1303](https://github.com/rust-lang/rustlings/commit/3f0e1303e0b3bf3fecc0baced3c8b8a37f83c184)) -* **intro:** Add intro section. ([21c9f441](https://github.com/rust-lang/rustlings/commit/21c9f44168394e08338fd470b5f49b1fd235986f)) -* Include exercises folder in the project structure behind a feature, enabling rust-analyzer to work (#917) ([179a75a6](https://github.com/rust-lang/rustlings/commit/179a75a68d03ac9518dec2297fb17f91a4fc506b)) +- Add move_semantics6.rs exercise (#908) ([3f0e1303](https://github.com/rust-lang/rustlings/commit/3f0e1303e0b3bf3fecc0baced3c8b8a37f83c184)) +- **intro:** Add intro section. ([21c9f441](https://github.com/rust-lang/rustlings/commit/21c9f44168394e08338fd470b5f49b1fd235986f)) +- Include exercises folder in the project structure behind a feature, enabling rust-analyzer to work (#917) ([179a75a6](https://github.com/rust-lang/rustlings/commit/179a75a68d03ac9518dec2297fb17f91a4fc506b)) #### Bug Fixes -* Fix a few spelling mistakes ([1c0fe3cb](https://github.com/rust-lang/rustlings/commit/1c0fe3cbcca85f90b3985985b8e265ee872a2ab2)) -* **cli:** - * Move long text strings into constants. ([f78c4802](https://github.com/rust-lang/rustlings/commit/f78c48020830d7900dd8d81f355606581670446d)) - * Replace `filter_map()` with `find_map()` ([9b27e8d](https://github.com/rust-lang/rustlings/commit/9b27e8d993ca20232fe38a412750c3f845a83b65)) -* **clippy1:** - * Set clippy::float_cmp lint to deny (#907) ([71a06044](https://github.com/rust-lang/rustlings/commit/71a06044e6a96ff756dc31d7b0ed665ae4badb57)) - * Updated code to test correctness clippy lint with approx_constant lint rule ([f2650de3](https://github.com/rust-lang/rustlings/commit/f2650de369810867d2763e935ac0963c32ec420e)) -* **errors1:** - * Add a comment to make the purpose more clear (#486) ([cbcde345](https://github.com/rust-lang/rustlings/commit/cbcde345409c3e550112e449242848eaa3391bb6)) - * Don't modify tests (#958) ([60bb7cc](https://github.com/rust-lang/rustlings/commit/60bb7cc3931d21d3986ad52b2b302e632a93831c)) -* **errors6:** Remove existing answer code ([43d0623](https://github.com/rust-lang/rustlings/commit/43d0623086edbc46fe896ba59c7afa22c3da9f7a)) -* **functions5:** Remove wrong new line and small English improvements (#885) ([8ef4869b](https://github.com/rust-lang/rustlings/commit/8ef4869b264094e5a9b50452b4534823a9df19c3)) -* **install:** protect path with whitespaces using quotes and stop at the first error ([d114847f](https://github.com/rust-lang/rustlings/commit/d114847f256c5f571c0b4c87e04b04bce3435509)) -* **intro1:** Add compiler error explanation. ([9b8de655](https://github.com/rust-lang/rustlings/commit/9b8de65525a5576b78cf0c8e4098cdd34296338f)) -* **iterators1:** reorder TODO steps ([0bd7a063](https://github.com/rust-lang/rustlings/commit/0bd7a0631a17a9d69af5746795a30efc9cf64e6e)) -* **move_semantics2:** Add comment ([89650f80](https://github.com/rust-lang/rustlings/commit/89650f808af23a32c9a2c6d46592b77547a6a464)) -* **move_semantics5:** correct typo (#857) ([46c28d5c](https://github.com/rust-lang/rustlings/commit/46c28d5cef3d8446b5a356b19d8dbc725f91a3a0)) -* **quiz1:** update to say quiz covers "If" ([1622e8c1](https://github.com/rust-lang/rustlings/commit/1622e8c198d89739765c915203efff0091bdeb78)) -* **structs3:** - * Add a hint for panic (#608) ([4f7ff5d9](https://github.com/rust-lang/rustlings/commit/4f7ff5d9c7b2d8b045194c1a9469d37e30257c4a)) - * remove redundant 'return' (#852) ([bf33829d](https://github.com/rust-lang/rustlings/commit/bf33829da240375d086f96267fc2e02fa6b07001)) - * Assigned value to `cents_per_gram` in test ([d1ee2da](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532)) -* **structs3.rs:** assigned value to cents_per_gram in test ([d1ee2daf](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532)) -* **traits1:** rename test functions to snake case (#854) ([1663a16e](https://github.com/rust-lang/rustlings/commit/1663a16eade6ca646b6ed061735f7982434d530d)) +- Fix a few spelling mistakes ([1c0fe3cb](https://github.com/rust-lang/rustlings/commit/1c0fe3cbcca85f90b3985985b8e265ee872a2ab2)) +- **cli:** + - Move long text strings into constants. ([f78c4802](https://github.com/rust-lang/rustlings/commit/f78c48020830d7900dd8d81f355606581670446d)) + - Replace `filter_map()` with `find_map()` ([9b27e8d](https://github.com/rust-lang/rustlings/commit/9b27e8d993ca20232fe38a412750c3f845a83b65)) +- **clippy1:** + - Set clippy::float_cmp lint to deny (#907) ([71a06044](https://github.com/rust-lang/rustlings/commit/71a06044e6a96ff756dc31d7b0ed665ae4badb57)) + - Updated code to test correctness clippy lint with approx_constant lint rule ([f2650de3](https://github.com/rust-lang/rustlings/commit/f2650de369810867d2763e935ac0963c32ec420e)) +- **errors1:** + - Add a comment to make the purpose more clear (#486) ([cbcde345](https://github.com/rust-lang/rustlings/commit/cbcde345409c3e550112e449242848eaa3391bb6)) + - Don't modify tests (#958) ([60bb7cc](https://github.com/rust-lang/rustlings/commit/60bb7cc3931d21d3986ad52b2b302e632a93831c)) +- **errors6:** Remove existing answer code ([43d0623](https://github.com/rust-lang/rustlings/commit/43d0623086edbc46fe896ba59c7afa22c3da9f7a)) +- **functions5:** Remove wrong new line and small English improvements (#885) ([8ef4869b](https://github.com/rust-lang/rustlings/commit/8ef4869b264094e5a9b50452b4534823a9df19c3)) +- **install:** protect path with whitespaces using quotes and stop at the first error ([d114847f](https://github.com/rust-lang/rustlings/commit/d114847f256c5f571c0b4c87e04b04bce3435509)) +- **intro1:** Add compiler error explanation. ([9b8de655](https://github.com/rust-lang/rustlings/commit/9b8de65525a5576b78cf0c8e4098cdd34296338f)) +- **iterators1:** reorder TODO steps ([0bd7a063](https://github.com/rust-lang/rustlings/commit/0bd7a0631a17a9d69af5746795a30efc9cf64e6e)) +- **move_semantics2:** Add comment ([89650f80](https://github.com/rust-lang/rustlings/commit/89650f808af23a32c9a2c6d46592b77547a6a464)) +- **move_semantics5:** correct typo (#857) ([46c28d5c](https://github.com/rust-lang/rustlings/commit/46c28d5cef3d8446b5a356b19d8dbc725f91a3a0)) +- **quiz1:** update to say quiz covers "If" ([1622e8c1](https://github.com/rust-lang/rustlings/commit/1622e8c198d89739765c915203efff0091bdeb78)) +- **structs3:** + - Add a hint for panic (#608) ([4f7ff5d9](https://github.com/rust-lang/rustlings/commit/4f7ff5d9c7b2d8b045194c1a9469d37e30257c4a)) + - remove redundant 'return' (#852) ([bf33829d](https://github.com/rust-lang/rustlings/commit/bf33829da240375d086f96267fc2e02fa6b07001)) + - Assigned value to `cents_per_gram` in test ([d1ee2da](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532)) +- **structs3.rs:** assigned value to cents_per_gram in test ([d1ee2daf](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532)) +- **traits1:** rename test functions to snake case (#854) ([1663a16e](https://github.com/rust-lang/rustlings/commit/1663a16eade6ca646b6ed061735f7982434d530d)) #### Documentation improvements -* Add hints on how to get GCC installed (#741) ([bc56861](https://github.com/rust-lang/rustlings/commit/bc5686174463ad6f4f6b824b0e9b97c3039d4886)) -* Fix some code blocks that were not highlighted ([17f9d74](https://github.com/rust-lang/rustlings/commit/17f9d7429ccd133a72e815fb5618e0ce79560929)) - +- Add hints on how to get GCC installed (#741) ([bc56861](https://github.com/rust-lang/rustlings/commit/bc5686174463ad6f4f6b824b0e9b97c3039d4886)) +- Fix some code blocks that were not highlighted ([17f9d74](https://github.com/rust-lang/rustlings/commit/17f9d7429ccd133a72e815fb5618e0ce79560929)) -## 4.6.0 (2021-09-25) +## 4.6.0 (2021-09-25) #### Features -* add advanced_errs2 ([abd6b70c](https://github.com/rust-lang/rustlings/commit/abd6b70c72dc6426752ff41f09160b839e5c449e)) -* add advanced_errs1 ([882d535b](https://github.com/rust-lang/rustlings/commit/882d535ba8628d5e0b37e8664b3e2f26260b2671)) -* Add a farewell message when quitting `watch` ([1caef0b4](https://github.com/rust-lang/rustlings/commit/1caef0b43494c8b8cdd6c9260147e70d510f1aca)) -* add more watch commands ([a7dc080b](https://github.com/rust-lang/rustlings/commit/a7dc080b95e49146fbaafe6922a6de2f8cb1582a), closes [#842](https://github.com/rust-lang/rustlings/issues/842)) -* **modules:** update exercises, add modules3 (#822) ([dfd2fab4](https://github.com/rust-lang/rustlings/commit/dfd2fab4f33d1bf59e2e5ee03123c0c9a67a9481)) -* **quiz1:** add default function name in comment (#838) ([0a11bad7](https://github.com/rust-lang/rustlings/commit/0a11bad71402b5403143d642f439f57931278c07)) +- add advanced_errs2 ([abd6b70c](https://github.com/rust-lang/rustlings/commit/abd6b70c72dc6426752ff41f09160b839e5c449e)) +- add advanced_errs1 ([882d535b](https://github.com/rust-lang/rustlings/commit/882d535ba8628d5e0b37e8664b3e2f26260b2671)) +- Add a farewell message when quitting `watch` ([1caef0b4](https://github.com/rust-lang/rustlings/commit/1caef0b43494c8b8cdd6c9260147e70d510f1aca)) +- add more watch commands ([a7dc080b](https://github.com/rust-lang/rustlings/commit/a7dc080b95e49146fbaafe6922a6de2f8cb1582a), closes [#842](https://github.com/rust-lang/rustlings/issues/842)) +- **modules:** update exercises, add modules3 (#822) ([dfd2fab4](https://github.com/rust-lang/rustlings/commit/dfd2fab4f33d1bf59e2e5ee03123c0c9a67a9481)) +- **quiz1:** add default function name in comment (#838) ([0a11bad7](https://github.com/rust-lang/rustlings/commit/0a11bad71402b5403143d642f439f57931278c07)) #### Bug Fixes -* Correct small typo in exercises/conversions/from_str.rs ([86cc8529](https://github.com/rust-lang/rustlings/commit/86cc85295ae36948963ae52882e285d7e3e29323)) -* **cli:** typo in exercise.rs (#848) ([06d5c097](https://github.com/rust-lang/rustlings/commit/06d5c0973a3dffa3c6c6f70acb775d4c6630323c)) -* **from_str, try_from_into:** custom error types ([2dc93cad](https://github.com/rust-lang/rustlings/commit/2dc93caddad43821743e4903d89b355df58d7a49)) -* **modules2:** fix typo (#835) ([1c3beb0a](https://github.com/rust-lang/rustlings/commit/1c3beb0a59178c950dc05fe8ee2346b017429ae0)) -* **move_semantics5:** - * change &mut *y to &mut x (#814) ([d75759e8](https://github.com/rust-lang/rustlings/commit/d75759e829fdcd64ef071cf4b6eae2a011a7718b)) - * Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1)) -* **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a)) - - +- Correct small typo in exercises/conversions/from_str.rs ([86cc8529](https://github.com/rust-lang/rustlings/commit/86cc85295ae36948963ae52882e285d7e3e29323)) +- **cli:** typo in exercise.rs (#848) ([06d5c097](https://github.com/rust-lang/rustlings/commit/06d5c0973a3dffa3c6c6f70acb775d4c6630323c)) +- **from_str, try_from_into:** custom error types ([2dc93cad](https://github.com/rust-lang/rustlings/commit/2dc93caddad43821743e4903d89b355df58d7a49)) +- **modules2:** fix typo (#835) ([1c3beb0a](https://github.com/rust-lang/rustlings/commit/1c3beb0a59178c950dc05fe8ee2346b017429ae0)) +- **move_semantics5:** + - change &mut \*y to &mut x (#814) ([d75759e8](https://github.com/rust-lang/rustlings/commit/d75759e829fdcd64ef071cf4b6eae2a011a7718b)) + - Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1)) +- **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a)) -## 4.5.0 (2021-07-07) +## 4.5.0 (2021-07-07) #### Features -* Add move_semantics5 exercise. (#746) ([399ab328](https://github.com/rust-lang/rustlings/commit/399ab328d8d407265c09563aa4ef4534b2503ff2)) -* **cli:** Add "next" to run the next unsolved exercise. (#785) ([d20e413a](https://github.com/rust-lang/rustlings/commit/d20e413a68772cd493561f2651cf244e822b7ca5)) +- Add move_semantics5 exercise. (#746) ([399ab328](https://github.com/rust-lang/rustlings/commit/399ab328d8d407265c09563aa4ef4534b2503ff2)) +- **cli:** Add "next" to run the next unsolved exercise. (#785) ([d20e413a](https://github.com/rust-lang/rustlings/commit/d20e413a68772cd493561f2651cf244e822b7ca5)) #### Bug Fixes -* rename result1 to errors4 ([50ab289d](https://github.com/rust-lang/rustlings/commit/50ab289da6b9eb19a7486c341b00048c516b88c0)) -* move_semantics5 hints ([1b858285](https://github.com/rust-lang/rustlings/commit/1b85828548f46f58b622b5e0c00f8c989f928807)) -* remove trailing whitespaces from iterators1 ([4d4fa774](https://github.com/rust-lang/rustlings/commit/4d4fa77459392acd3581c6068aa8be9a02de12fc)) -* add hints to generics1 and generics2 exercises ([31457940](https://github.com/rust-lang/rustlings/commit/31457940846b3844d78d4a4d2b074bc8d6aaf1eb)) -* remove trailing whitespace ([d9b69bd1](https://github.com/rust-lang/rustlings/commit/d9b69bd1a0a7a99f2c0d80933ad2eea44c8c71b2)) -* **installation:** first PowerShell command ([aa9a943d](https://github.com/rust-lang/rustlings/commit/aa9a943ddf3ae260782e73c26bcc9db60e5894b6)) -* **iterators5:** derive Clone, Copy ([91fc9e31](https://github.com/rust-lang/rustlings/commit/91fc9e3118f4af603c9911698cc2a234725cb032)) -* **quiz1:** Updated question description (#794) ([d8766496](https://github.com/rust-lang/rustlings/commit/d876649616cc8a8dd5f539f8bc1a5434b960b1e9)) -* **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e)) -* **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5)) - - +- rename result1 to errors4 ([50ab289d](https://github.com/rust-lang/rustlings/commit/50ab289da6b9eb19a7486c341b00048c516b88c0)) +- move_semantics5 hints ([1b858285](https://github.com/rust-lang/rustlings/commit/1b85828548f46f58b622b5e0c00f8c989f928807)) +- remove trailing whitespaces from iterators1 ([4d4fa774](https://github.com/rust-lang/rustlings/commit/4d4fa77459392acd3581c6068aa8be9a02de12fc)) +- add hints to generics1 and generics2 exercises ([31457940](https://github.com/rust-lang/rustlings/commit/31457940846b3844d78d4a4d2b074bc8d6aaf1eb)) +- remove trailing whitespace ([d9b69bd1](https://github.com/rust-lang/rustlings/commit/d9b69bd1a0a7a99f2c0d80933ad2eea44c8c71b2)) +- **installation:** first PowerShell command ([aa9a943d](https://github.com/rust-lang/rustlings/commit/aa9a943ddf3ae260782e73c26bcc9db60e5894b6)) +- **iterators5:** derive Clone, Copy ([91fc9e31](https://github.com/rust-lang/rustlings/commit/91fc9e3118f4af603c9911698cc2a234725cb032)) +- **quiz1:** Updated question description (#794) ([d8766496](https://github.com/rust-lang/rustlings/commit/d876649616cc8a8dd5f539f8bc1a5434b960b1e9)) +- **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e)) +- **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5)) -## 4.4.0 (2021-04-24) +## 4.4.0 (2021-04-24) #### Bug Fixes -* Fix spelling error in main.rs ([91ee27f2](https://github.com/rust-lang/rustlings/commit/91ee27f22bd3797a9db57e5fd430801c170c5db8)) -* typo in default out text ([644c49f1](https://github.com/rust-lang/rustlings/commit/644c49f1e04cbb24e95872b3a52b07d692ae3bc8)) -* **collections:** Naming exercises for vectors and hashmap ([bef39b12](https://github.com/rust-lang/rustlings/commit/bef39b125961310b34b34871e480a82e82af4678)) -* **from_str:** - * Correct typos ([5f7c89f8](https://github.com/rust-lang/rustlings/commit/5f7c89f85db1f33da01911eaa479c3a2d4721678)) - * test for error instead of unwrap/should_panic ([15e71535](https://github.com/rust-lang/rustlings/commit/15e71535f37cfaed36e22eb778728d186e2104ab)) - * use trait objects for from_str ([c3e7b831](https://github.com/rust-lang/rustlings/commit/c3e7b831786c9172ed8bd5d150f3c432f242fba9)) -* **functions3:** improve function argument type (#687) ([a6509cc4](https://github.com/rust-lang/rustlings/commit/a6509cc4d545d8825f01ddf7ee37823b372154dd)) -* **hashmap2:** Update incorrect assertion (#660) ([72aaa15e](https://github.com/rust-lang/rustlings/commit/72aaa15e6ab4b72b3422f1c6356396e20a2a2bb8)) -* **info:** Fix typo (#635) ([cddc1e86](https://github.com/rust-lang/rustlings/commit/cddc1e86e7ec744ee644cc774a4887b1a0ded3e8)) -* **iterators2:** Moved errors out of tests. ([baf4ba17](https://github.com/rust-lang/rustlings/commit/baf4ba175ba6eb92989e3dd54ecbec4bedc9a863), closes [#359](https://github.com/rust-lang/rustlings/issues/359)) -* **iterators3:** Enabled iterators3.rs to run without commented out tests. ([c6712dfc](https://github.com/rust-lang/rustlings/commit/c6712dfccd1a093e590ad22bbc4f49edc417dac0)) -* **main:** Let find_exercise work with borrows ([347f30bd](https://github.com/rust-lang/rustlings/commit/347f30bd867343c5ace1097e085a1f7e356553f7)) -* **move_semantics4:** - * Remove redundant "instead" (#640) ([cc266d7d](https://github.com/rust-lang/rustlings/commit/cc266d7d80b91e79df3f61984f231b7f1587218e)) - * Small readbility improvement (#617) ([10965920](https://github.com/rust-lang/rustlings/commit/10965920fbdf8a1efc85bed869e55a1787006404)) -* **option2:** Rename uninformative variables (#675) ([b4de6594](https://github.com/rust-lang/rustlings/commit/b4de6594380636817d13c2677ec6f472a964cf43)) -* **quiz3:** Force an answer to Q2 (#672) ([0d894e6f](https://github.com/rust-lang/rustlings/commit/0d894e6ff739943901e1ae8c904582e5c2f843bd)) -* **structs:** Add 5.3 to structs/README (#652) ([6bd791f2](https://github.com/rust-lang/rustlings/commit/6bd791f2f44aa7f0ad926df767f6b1fa8f12a9a9)) -* **structs2:** correct grammar in hint (#663) ([ebdb66c7](https://github.com/rust-lang/rustlings/commit/ebdb66c7bfb6d687a14cc511a559a222e6fc5de4)) -* **structs3:** - * reword heading comment (#664) ([9f3e8c2d](https://github.com/rust-lang/rustlings/commit/9f3e8c2dde645e5264c2d2200e68842b5f47bfa3)) - * add check to prevent naive implementation of is_international ([05a753fe](https://github.com/rust-lang/rustlings/commit/05a753fe6333d36dbee5f68c21dec04eacdc75df)) -* **threads1:** line number correction ([7857b0a6](https://github.com/rust-lang/rustlings/commit/7857b0a689b0847f48d8c14cbd1865e3b812d5ca)) -* **try_from_into:** use trait objects ([2e93a588](https://github.com/rust-lang/rustlings/commit/2e93a588e0abe8badb7eafafb9e7d073c2be5df8)) +- Fix spelling error in main.rs ([91ee27f2](https://github.com/rust-lang/rustlings/commit/91ee27f22bd3797a9db57e5fd430801c170c5db8)) +- typo in default out text ([644c49f1](https://github.com/rust-lang/rustlings/commit/644c49f1e04cbb24e95872b3a52b07d692ae3bc8)) +- **collections:** Naming exercises for vectors and hashmap ([bef39b12](https://github.com/rust-lang/rustlings/commit/bef39b125961310b34b34871e480a82e82af4678)) +- **from_str:** + - Correct typos ([5f7c89f8](https://github.com/rust-lang/rustlings/commit/5f7c89f85db1f33da01911eaa479c3a2d4721678)) + - test for error instead of unwrap/should_panic ([15e71535](https://github.com/rust-lang/rustlings/commit/15e71535f37cfaed36e22eb778728d186e2104ab)) + - use trait objects for from_str ([c3e7b831](https://github.com/rust-lang/rustlings/commit/c3e7b831786c9172ed8bd5d150f3c432f242fba9)) +- **functions3:** improve function argument type (#687) ([a6509cc4](https://github.com/rust-lang/rustlings/commit/a6509cc4d545d8825f01ddf7ee37823b372154dd)) +- **hashmap2:** Update incorrect assertion (#660) ([72aaa15e](https://github.com/rust-lang/rustlings/commit/72aaa15e6ab4b72b3422f1c6356396e20a2a2bb8)) +- **info:** Fix typo (#635) ([cddc1e86](https://github.com/rust-lang/rustlings/commit/cddc1e86e7ec744ee644cc774a4887b1a0ded3e8)) +- **iterators2:** Moved errors out of tests. ([baf4ba17](https://github.com/rust-lang/rustlings/commit/baf4ba175ba6eb92989e3dd54ecbec4bedc9a863), closes [#359](https://github.com/rust-lang/rustlings/issues/359)) +- **iterators3:** Enabled iterators3.rs to run without commented out tests. ([c6712dfc](https://github.com/rust-lang/rustlings/commit/c6712dfccd1a093e590ad22bbc4f49edc417dac0)) +- **main:** Let find_exercise work with borrows ([347f30bd](https://github.com/rust-lang/rustlings/commit/347f30bd867343c5ace1097e085a1f7e356553f7)) +- **move_semantics4:** + - Remove redundant "instead" (#640) ([cc266d7d](https://github.com/rust-lang/rustlings/commit/cc266d7d80b91e79df3f61984f231b7f1587218e)) + - Small readbility improvement (#617) ([10965920](https://github.com/rust-lang/rustlings/commit/10965920fbdf8a1efc85bed869e55a1787006404)) +- **option2:** Rename uninformative variables (#675) ([b4de6594](https://github.com/rust-lang/rustlings/commit/b4de6594380636817d13c2677ec6f472a964cf43)) +- **quiz3:** Force an answer to Q2 (#672) ([0d894e6f](https://github.com/rust-lang/rustlings/commit/0d894e6ff739943901e1ae8c904582e5c2f843bd)) +- **structs:** Add 5.3 to structs/README (#652) ([6bd791f2](https://github.com/rust-lang/rustlings/commit/6bd791f2f44aa7f0ad926df767f6b1fa8f12a9a9)) +- **structs2:** correct grammar in hint (#663) ([ebdb66c7](https://github.com/rust-lang/rustlings/commit/ebdb66c7bfb6d687a14cc511a559a222e6fc5de4)) +- **structs3:** + - reword heading comment (#664) ([9f3e8c2d](https://github.com/rust-lang/rustlings/commit/9f3e8c2dde645e5264c2d2200e68842b5f47bfa3)) + - add check to prevent naive implementation of is_international ([05a753fe](https://github.com/rust-lang/rustlings/commit/05a753fe6333d36dbee5f68c21dec04eacdc75df)) +- **threads1:** line number correction ([7857b0a6](https://github.com/rust-lang/rustlings/commit/7857b0a689b0847f48d8c14cbd1865e3b812d5ca)) +- **try_from_into:** use trait objects ([2e93a588](https://github.com/rust-lang/rustlings/commit/2e93a588e0abe8badb7eafafb9e7d073c2be5df8)) #### Features -* Replace clap with argh ([7928122f](https://github.com/rust-lang/rustlings/commit/7928122fcef9ca7834d988b1ec8ca0687478beeb)) -* Replace emojis when NO_EMOJI env variable present ([8d62a996](https://github.com/rust-lang/rustlings/commit/8d62a9963708dbecd9312e8bcc4b47049c72d155)) -* Added iterators5.rs exercise. ([b29ea17e](https://github.com/rust-lang/rustlings/commit/b29ea17ea94d1862114af2cf5ced0e09c197dc35)) -* **arc1:** Add more details to description and hint (#710) ([81be4044](https://github.com/rust-lang/rustlings/commit/81be40448777fa338ebced3b0bfc1b32d6370313)) -* **cli:** Improve the list command with options, and then some ([8bbe4ff1](https://github.com/rust-lang/rustlings/commit/8bbe4ff1385c5c169c90cd3ff9253f9a91daaf8e)) -* **list:** - * updated progress percentage ([1c6f7e4b](https://github.com/rust-lang/rustlings/commit/1c6f7e4b7b9b3bd36f4da2bb2b69c549cc8bd913)) - * added progress info ([c0e3daac](https://github.com/rust-lang/rustlings/commit/c0e3daacaf6850811df5bc57fa43e0f249d5cfa4)) - - +- Replace clap with argh ([7928122f](https://github.com/rust-lang/rustlings/commit/7928122fcef9ca7834d988b1ec8ca0687478beeb)) +- Replace emojis when NO_EMOJI env variable present ([8d62a996](https://github.com/rust-lang/rustlings/commit/8d62a9963708dbecd9312e8bcc4b47049c72d155)) +- Added iterators5.rs exercise. ([b29ea17e](https://github.com/rust-lang/rustlings/commit/b29ea17ea94d1862114af2cf5ced0e09c197dc35)) +- **arc1:** Add more details to description and hint (#710) ([81be4044](https://github.com/rust-lang/rustlings/commit/81be40448777fa338ebced3b0bfc1b32d6370313)) +- **cli:** Improve the list command with options, and then some ([8bbe4ff1](https://github.com/rust-lang/rustlings/commit/8bbe4ff1385c5c169c90cd3ff9253f9a91daaf8e)) +- **list:** + - updated progress percentage ([1c6f7e4b](https://github.com/rust-lang/rustlings/commit/1c6f7e4b7b9b3bd36f4da2bb2b69c549cc8bd913)) + - added progress info ([c0e3daac](https://github.com/rust-lang/rustlings/commit/c0e3daacaf6850811df5bc57fa43e0f249d5cfa4)) + ## 4.3.0 (2020-12-29) #### Features -* Rewrite default out text ([44d39112](https://github.com/rust-lang/rustlings/commit/44d39112ff122b29c9793fe52e605df1612c6490)) -* match exercise order to book chapters (#541) ([033bf119](https://github.com/rust-lang/rustlings/commit/033bf1198fc8bfce1b570e49da7cde010aa552e3)) -* Crab? (#586) ([fa9f522b](https://github.com/rust-lang/rustlings/commit/fa9f522b7f043d7ef73a39f003a9272dfe72c4f4)) -* add "rustlings list" command ([838f9f30](https://github.com/rust-lang/rustlings/commit/838f9f30083d0b23fd67503dcf0fbeca498e6647)) -* **try_from_into:** remove duplicate annotation ([04f1d079](https://github.com/rust-lang/rustlings/commit/04f1d079aa42a2f49af694bc92c67d731d31a53f)) +- Rewrite default out text ([44d39112](https://github.com/rust-lang/rustlings/commit/44d39112ff122b29c9793fe52e605df1612c6490)) +- match exercise order to book chapters (#541) ([033bf119](https://github.com/rust-lang/rustlings/commit/033bf1198fc8bfce1b570e49da7cde010aa552e3)) +- Crab? (#586) ([fa9f522b](https://github.com/rust-lang/rustlings/commit/fa9f522b7f043d7ef73a39f003a9272dfe72c4f4)) +- add "rustlings list" command ([838f9f30](https://github.com/rust-lang/rustlings/commit/838f9f30083d0b23fd67503dcf0fbeca498e6647)) +- **try_from_into:** remove duplicate annotation ([04f1d079](https://github.com/rust-lang/rustlings/commit/04f1d079aa42a2f49af694bc92c67d731d31a53f)) #### Bug Fixes -* update structs README ([bcf14cf6](https://github.com/rust-lang/rustlings/commit/bcf14cf677adb3a38a3ac3ca53f3c69f61153025)) -* added missing exercises to info.toml ([90cfb6ff](https://github.com/rust-lang/rustlings/commit/90cfb6ff28377531bfc34acb70547bdb13374f6b)) -* gives a bit more context to magic number ([30644c9a](https://github.com/rust-lang/rustlings/commit/30644c9a062b825c0ea89435dc59f0cad86b110e)) -* **functions2:** Change signature to trigger precise error message: (#605) ([0ef95947](https://github.com/rust-lang/rustlings/commit/0ef95947cc30482e63a7045be6cc2fb6f6dcb4cc)) -* **structs1:** Adjust wording (#573) ([9334783d](https://github.com/rust-lang/rustlings/commit/9334783da31d821cc59174fbe8320df95828926c)) -* **try_from_into:** - * type error ([4f4cfcf3](https://github.com/rust-lang/rustlings/commit/4f4cfcf3c36c8718c7c170c9c3a6935e6ef0618c)) - * Update description (#584) ([96347df9](https://github.com/rust-lang/rustlings/commit/96347df9df294f01153b29d9ad4ba361f665c755)) -* **vec1:** Have test compare every element in a and v ([9b6c6293](https://github.com/rust-lang/rustlings/commit/9b6c629397b24b944f484f5b2bbd8144266b5695)) +- update structs README ([bcf14cf6](https://github.com/rust-lang/rustlings/commit/bcf14cf677adb3a38a3ac3ca53f3c69f61153025)) +- added missing exercises to info.toml ([90cfb6ff](https://github.com/rust-lang/rustlings/commit/90cfb6ff28377531bfc34acb70547bdb13374f6b)) +- gives a bit more context to magic number ([30644c9a](https://github.com/rust-lang/rustlings/commit/30644c9a062b825c0ea89435dc59f0cad86b110e)) +- **functions2:** Change signature to trigger precise error message: (#605) ([0ef95947](https://github.com/rust-lang/rustlings/commit/0ef95947cc30482e63a7045be6cc2fb6f6dcb4cc)) +- **structs1:** Adjust wording (#573) ([9334783d](https://github.com/rust-lang/rustlings/commit/9334783da31d821cc59174fbe8320df95828926c)) +- **try_from_into:** + - type error ([4f4cfcf3](https://github.com/rust-lang/rustlings/commit/4f4cfcf3c36c8718c7c170c9c3a6935e6ef0618c)) + - Update description (#584) ([96347df9](https://github.com/rust-lang/rustlings/commit/96347df9df294f01153b29d9ad4ba361f665c755)) +- **vec1:** Have test compare every element in a and v ([9b6c6293](https://github.com/rust-lang/rustlings/commit/9b6c629397b24b944f484f5b2bbd8144266b5695)) + ## 4.2.0 (2020-11-07) #### Features -* Add HashMap exercises ([633c00cf](https://github.com/rust-lang/rustlings/commit/633c00cf8071e1e82959a3010452a32f34f29fc9)) -* Add Vec exercises ([0c12fa31](https://github.com/rust-lang/rustlings/commit/0c12fa31c57c03c6287458a0a8aca7afd057baf6)) -* **primitive_types6:** Add a test (#548) ([2b1fb2b7](https://github.com/rust-lang/rustlings/commit/2b1fb2b739bf9ad8d6b7b12af25fee173011bfc4)) -* **try_from_into:** Add tests (#571) ([95ccd926](https://github.com/rust-lang/rustlings/commit/95ccd92616ae79ba287cce221101e0bbe4f68cdc)) +- Add HashMap exercises ([633c00cf](https://github.com/rust-lang/rustlings/commit/633c00cf8071e1e82959a3010452a32f34f29fc9)) +- Add Vec exercises ([0c12fa31](https://github.com/rust-lang/rustlings/commit/0c12fa31c57c03c6287458a0a8aca7afd057baf6)) +- **primitive_types6:** Add a test (#548) ([2b1fb2b7](https://github.com/rust-lang/rustlings/commit/2b1fb2b739bf9ad8d6b7b12af25fee173011bfc4)) +- **try_from_into:** Add tests (#571) ([95ccd926](https://github.com/rust-lang/rustlings/commit/95ccd92616ae79ba287cce221101e0bbe4f68cdc)) #### Bug Fixes -* log error output when inotify limit is exceeded ([d61b4e5a](https://github.com/rust-lang/rustlings/commit/d61b4e5a13b44d72d004082f523fa1b6b24c1aca)) -* more unique temp_file ([5643ef05](https://github.com/rust-lang/rustlings/commit/5643ef05bc81e4a840e9456f4406a769abbe1392)) -* **installation:** Update the MinRustVersion ([21bfb2d4](https://github.com/rust-lang/rustlings/commit/21bfb2d4777429c87d8d3b5fbf0ce66006dcd034)) -* **iterators2:** Update description (#578) ([197d3a3d](https://github.com/rust-lang/rustlings/commit/197d3a3d8961b2465579218a6749b2b2cefa8ddd)) -* **primitive_types6:** - * remove 'unused doc comment' warning ([472d8592](https://github.com/rust-lang/rustlings/commit/472d8592d65c8275332a20dfc269e7ac0d41bc88)) - * missing comma in test ([4fb230da](https://github.com/rust-lang/rustlings/commit/4fb230daf1251444fcf29e085cee222a91f8a37e)) -* **quiz3:** Second test is for odd numbers, not even. (#553) ([18e0bfef](https://github.com/rust-lang/rustlings/commit/18e0bfef1de53071e353ba1ec5837002ff7290e6)) +- log error output when inotify limit is exceeded ([d61b4e5a](https://github.com/rust-lang/rustlings/commit/d61b4e5a13b44d72d004082f523fa1b6b24c1aca)) +- more unique temp_file ([5643ef05](https://github.com/rust-lang/rustlings/commit/5643ef05bc81e4a840e9456f4406a769abbe1392)) +- **installation:** Update the MinRustVersion ([21bfb2d4](https://github.com/rust-lang/rustlings/commit/21bfb2d4777429c87d8d3b5fbf0ce66006dcd034)) +- **iterators2:** Update description (#578) ([197d3a3d](https://github.com/rust-lang/rustlings/commit/197d3a3d8961b2465579218a6749b2b2cefa8ddd)) +- **primitive_types6:** + - remove 'unused doc comment' warning ([472d8592](https://github.com/rust-lang/rustlings/commit/472d8592d65c8275332a20dfc269e7ac0d41bc88)) + - missing comma in test ([4fb230da](https://github.com/rust-lang/rustlings/commit/4fb230daf1251444fcf29e085cee222a91f8a37e)) +- **quiz3:** Second test is for odd numbers, not even. (#553) ([18e0bfef](https://github.com/rust-lang/rustlings/commit/18e0bfef1de53071e353ba1ec5837002ff7290e6)) + ## 4.1.0 (2020-10-05) #### Bug Fixes -* Update rustlings version in Cargo.lock ([1cc40bc9](https://github.com/rust-lang/rustlings/commit/1cc40bc9ce95c23d56f6d91fa1c4deb646231fef)) -* **arc1:** index mod should equal thread count ([b4062ef6](https://github.com/rust-lang/rustlings/commit/b4062ef6993e80dac107c4093ea85166ad3ee0fa)) -* **enums3:** Update Message::ChangeColor to take a tuple. (#457) ([4b6540c7](https://github.com/rust-lang/rustlings/commit/4b6540c71adabad647de8a09e57295e7c7c7d794)) -* **exercises:** adding question mark to quiz2 ([101072ab](https://github.com/rust-lang/rustlings/commit/101072ab9f8c80b40b8b88cb06cbe38aca2481c5)) -* **generics3:** clarify grade change ([47f7672c](https://github.com/rust-lang/rustlings/commit/47f7672c0307732056e7426e81d351f0dd7e22e5)) -* **structs3:** Small adjustment of variable name ([114b54cb](https://github.com/rust-lang/rustlings/commit/114b54cbdb977234b39e5f180d937c14c78bb8b2)) -* **using_as:** Add test so that proper type is returned. (#512) ([3286c5ec](https://github.com/rust-lang/rustlings/commit/3286c5ec19ea5fb7ded81d047da5f8594108a490)) +- Update rustlings version in Cargo.lock ([1cc40bc9](https://github.com/rust-lang/rustlings/commit/1cc40bc9ce95c23d56f6d91fa1c4deb646231fef)) +- **arc1:** index mod should equal thread count ([b4062ef6](https://github.com/rust-lang/rustlings/commit/b4062ef6993e80dac107c4093ea85166ad3ee0fa)) +- **enums3:** Update Message::ChangeColor to take a tuple. (#457) ([4b6540c7](https://github.com/rust-lang/rustlings/commit/4b6540c71adabad647de8a09e57295e7c7c7d794)) +- **exercises:** adding question mark to quiz2 ([101072ab](https://github.com/rust-lang/rustlings/commit/101072ab9f8c80b40b8b88cb06cbe38aca2481c5)) +- **generics3:** clarify grade change ([47f7672c](https://github.com/rust-lang/rustlings/commit/47f7672c0307732056e7426e81d351f0dd7e22e5)) +- **structs3:** Small adjustment of variable name ([114b54cb](https://github.com/rust-lang/rustlings/commit/114b54cbdb977234b39e5f180d937c14c78bb8b2)) +- **using_as:** Add test so that proper type is returned. (#512) ([3286c5ec](https://github.com/rust-lang/rustlings/commit/3286c5ec19ea5fb7ded81d047da5f8594108a490)) #### Features -* Added iterators1.rs exercise ([9642f5a3](https://github.com/rust-lang/rustlings/commit/9642f5a3f686270a4f8f6ba969919ddbbc4f7fdd)) -* Add ability to run rustlings on repl.it (#471) ([8f7b5bd0](https://github.com/rust-lang/rustlings/commit/8f7b5bd00eb83542b959830ef55192d2d76db90a)) -* Add gitpod support (#473) ([4821a8be](https://github.com/rust-lang/rustlings/commit/4821a8be94af4f669042a06ab917934cfacd032f)) -* Remind the user of the hint option (#425) ([816b1f5e](https://github.com/rust-lang/rustlings/commit/816b1f5e85d6cc6e72673813a85d0ada2a8f84af)) -* Remind the user of the hint option (#425) ([9f61db5d](https://github.com/rust-lang/rustlings/commit/9f61db5dbe38538cf06571fcdd5f806e7901e83a)) -* **cli:** Added 'cls' command to 'watch' mode (#474) ([4f2468e1](https://github.com/rust-lang/rustlings/commit/4f2468e14f574a93a2e9b688367b5752ed96ae7b)) -* **try_from_into:** Add insufficient length test (#469) ([523d18b8](https://github.com/rust-lang/rustlings/commit/523d18b873a319f7c09262f44bd40e2fab1830e5)) +- Added iterators1.rs exercise ([9642f5a3](https://github.com/rust-lang/rustlings/commit/9642f5a3f686270a4f8f6ba969919ddbbc4f7fdd)) +- Add ability to run rustlings on repl.it (#471) ([8f7b5bd0](https://github.com/rust-lang/rustlings/commit/8f7b5bd00eb83542b959830ef55192d2d76db90a)) +- Add gitpod support (#473) ([4821a8be](https://github.com/rust-lang/rustlings/commit/4821a8be94af4f669042a06ab917934cfacd032f)) +- Remind the user of the hint option (#425) ([816b1f5e](https://github.com/rust-lang/rustlings/commit/816b1f5e85d6cc6e72673813a85d0ada2a8f84af)) +- Remind the user of the hint option (#425) ([9f61db5d](https://github.com/rust-lang/rustlings/commit/9f61db5dbe38538cf06571fcdd5f806e7901e83a)) +- **cli:** Added 'cls' command to 'watch' mode (#474) ([4f2468e1](https://github.com/rust-lang/rustlings/commit/4f2468e14f574a93a2e9b688367b5752ed96ae7b)) +- **try_from_into:** Add insufficient length test (#469) ([523d18b8](https://github.com/rust-lang/rustlings/commit/523d18b873a319f7c09262f44bd40e2fab1830e5)) + ## 4.0.0 (2020-07-08) #### Breaking Changes -* Add a --nocapture option to display test harnesses' outputs ([8ad5f9bf](https://github.com/rust-lang/rustlings/commit/8ad5f9bf531a4848b1104b7b389a20171624c82f)) -* Rename test to quiz, fixes #244 ([010a0456](https://github.com/rust-lang/rustlings/commit/010a04569282149cea7f7a76fc4d7f4c9f0f08dd)) +- Add a --nocapture option to display test harnesses' outputs ([8ad5f9bf](https://github.com/rust-lang/rustlings/commit/8ad5f9bf531a4848b1104b7b389a20171624c82f)) +- Rename test to quiz, fixes #244 ([010a0456](https://github.com/rust-lang/rustlings/commit/010a04569282149cea7f7a76fc4d7f4c9f0f08dd)) #### Features -* Add traits README ([173bb141](https://github.com/rust-lang/rustlings/commit/173bb14140c5530cbdb59e53ace3991a99d804af)) -* Add box1.rs exercise ([7479a473](https://github.com/rust-lang/rustlings/commit/7479a4737bdcac347322ad0883ca528c8675e720)) -* Rewrite try_from_into (#393) ([763aa6e3](https://github.com/rust-lang/rustlings/commit/763aa6e378a586caae2d8d63755a85eeba227933)) -* Add if2 exercise ([1da84b5f](https://github.com/rust-lang/rustlings/commit/1da84b5f7c489f65bd683c244f13c7d1ee812df0)) -* Added exercise structs3.rs ([b66e2e09](https://github.com/rust-lang/rustlings/commit/b66e2e09622243e086a0f1258dd27e1a2d61c891)) -* Add exercise variables6 covering const (#352) ([5999acd2](https://github.com/rust-lang/rustlings/commit/5999acd24a4f203292be36e0fd18d385887ec481)) +- Add traits README ([173bb141](https://github.com/rust-lang/rustlings/commit/173bb14140c5530cbdb59e53ace3991a99d804af)) +- Add box1.rs exercise ([7479a473](https://github.com/rust-lang/rustlings/commit/7479a4737bdcac347322ad0883ca528c8675e720)) +- Rewrite try_from_into (#393) ([763aa6e3](https://github.com/rust-lang/rustlings/commit/763aa6e378a586caae2d8d63755a85eeba227933)) +- Add if2 exercise ([1da84b5f](https://github.com/rust-lang/rustlings/commit/1da84b5f7c489f65bd683c244f13c7d1ee812df0)) +- Added exercise structs3.rs ([b66e2e09](https://github.com/rust-lang/rustlings/commit/b66e2e09622243e086a0f1258dd27e1a2d61c891)) +- Add exercise variables6 covering const (#352) ([5999acd2](https://github.com/rust-lang/rustlings/commit/5999acd24a4f203292be36e0fd18d385887ec481)) #### Bug Fixes -* Change then to than ([ddd98ad7](https://github.com/rust-lang/rustlings/commit/ddd98ad75d3668fbb10eff74374148aa5ed2344d)) -* rename quiz1 to tests1 in info (#420) ([0dd1c6ca](https://github.com/rust-lang/rustlings/commit/0dd1c6ca6b389789e0972aa955fe17aa15c95f29)) -* fix quiz naming inconsistency (#421) ([5563adbb](https://github.com/rust-lang/rustlings/commit/5563adbb890587fc48fbbc9c4028642687f1e85b)) -* confine the user further in variable exercises ([06ef4cc6](https://github.com/rust-lang/rustlings/commit/06ef4cc654e75d22a526812919ee49b8956280bf)) -* update iterator and macro text for typos and clarity ([95900828](https://github.com/rust-lang/rustlings/commit/959008284834bece0196a01e17ac69a7e3590116)) -* update generics2 closes #362 ([964c974a](https://github.com/rust-lang/rustlings/commit/964c974a0274199d755073b917c2bc5da0c9b4f1)) -* confusing comment in conversions/try_from_into.rs ([c9e4f2cf](https://github.com/rust-lang/rustlings/commit/c9e4f2cfb4c48d0b7451263cfb43b9426438122d)) -* **arc1:** Passively introduce attributes (#429) ([113cdae2](https://github.com/rust-lang/rustlings/commit/113cdae2d4e4c55905e8056ad326ede7fd7de356)) -* **box1:** fix comment typo (#426) ([bb2ca251](https://github.com/rust-lang/rustlings/commit/bb2ca251106b27a7272d9a30872904dd1376654c)) -* **errorsn:** Try harder to confine the user. (#388) ([2b20c8a0](https://github.com/rust-lang/rustlings/commit/2b20c8a0f5774d07c58d110d75879f33fc6273b5)) -* **from_into.rs:** typo ([a901499e](https://github.com/rust-lang/rustlings/commit/a901499ededd3ce1995164700514fe4e9a0373ea)) -* **generics2:** Guide students to the answer (#430) ([e6bd8021](https://github.com/rust-lang/rustlings/commit/e6bd8021d9a7dd06feebc30c9d5f953901d7b419)) -* **installation:** - * Provide a backup git reference when tag can't be curl ([9e4fb100](https://github.com/rust-lang/rustlings/commit/9e4fb1009f1c9e3433915c03e22c2af422e5c5fe)) - * Check if python is available while checking for git,rustc and cargo ([9cfb617d](https://github.com/rust-lang/rustlings/commit/9cfb617d5b0451b4b51644a1298965390cda9884)) -* **option1:** - * Don't add only zeros to the numbers array ([cce6a442](https://github.com/rust-lang/rustlings/commit/cce6a4427718724a9096800754cd3abeca6a1580)) - * Add cast to usize, as it is confusing in the context of an exercise about Option ([f6cffc7e](https://github.com/rust-lang/rustlings/commit/f6cffc7e487b42f15a6f958e49704c93a8d4465b)) -* **option2:** Add TODO to comments (#400) ([10967bce](https://github.com/rust-lang/rustlings/commit/10967bce57682812dc0891a9f9757da1a9d87404)) -* **options1:** Add hint about Array Initialization (#389) ([9f75554f](https://github.com/rust-lang/rustlings/commit/9f75554f2a30295996f03f0160b98c0458305502)) -* **test2:** name of type String and &str (#394) ([d6c0a688](https://github.com/rust-lang/rustlings/commit/d6c0a688e6a96f93ad60d540d4b326f342fc0d45)) -* **variables6:** minor typo (#419) ([524e17df](https://github.com/rust-lang/rustlings/commit/524e17df10db95f7b90a0f75cc8997182a8a4094)) +- Change then to than ([ddd98ad7](https://github.com/rust-lang/rustlings/commit/ddd98ad75d3668fbb10eff74374148aa5ed2344d)) +- rename quiz1 to tests1 in info (#420) ([0dd1c6ca](https://github.com/rust-lang/rustlings/commit/0dd1c6ca6b389789e0972aa955fe17aa15c95f29)) +- fix quiz naming inconsistency (#421) ([5563adbb](https://github.com/rust-lang/rustlings/commit/5563adbb890587fc48fbbc9c4028642687f1e85b)) +- confine the user further in variable exercises ([06ef4cc6](https://github.com/rust-lang/rustlings/commit/06ef4cc654e75d22a526812919ee49b8956280bf)) +- update iterator and macro text for typos and clarity ([95900828](https://github.com/rust-lang/rustlings/commit/959008284834bece0196a01e17ac69a7e3590116)) +- update generics2 closes #362 ([964c974a](https://github.com/rust-lang/rustlings/commit/964c974a0274199d755073b917c2bc5da0c9b4f1)) +- confusing comment in conversions/try_from_into.rs ([c9e4f2cf](https://github.com/rust-lang/rustlings/commit/c9e4f2cfb4c48d0b7451263cfb43b9426438122d)) +- **arc1:** Passively introduce attributes (#429) ([113cdae2](https://github.com/rust-lang/rustlings/commit/113cdae2d4e4c55905e8056ad326ede7fd7de356)) +- **box1:** fix comment typo (#426) ([bb2ca251](https://github.com/rust-lang/rustlings/commit/bb2ca251106b27a7272d9a30872904dd1376654c)) +- **errorsn:** Try harder to confine the user. (#388) ([2b20c8a0](https://github.com/rust-lang/rustlings/commit/2b20c8a0f5774d07c58d110d75879f33fc6273b5)) +- **from_into.rs:** typo ([a901499e](https://github.com/rust-lang/rustlings/commit/a901499ededd3ce1995164700514fe4e9a0373ea)) +- **generics2:** Guide students to the answer (#430) ([e6bd8021](https://github.com/rust-lang/rustlings/commit/e6bd8021d9a7dd06feebc30c9d5f953901d7b419)) +- **installation:** + - Provide a backup git reference when tag can't be curl ([9e4fb100](https://github.com/rust-lang/rustlings/commit/9e4fb1009f1c9e3433915c03e22c2af422e5c5fe)) + - Check if python is available while checking for git,rustc and cargo ([9cfb617d](https://github.com/rust-lang/rustlings/commit/9cfb617d5b0451b4b51644a1298965390cda9884)) +- **option1:** + - Don't add only zeros to the numbers array ([cce6a442](https://github.com/rust-lang/rustlings/commit/cce6a4427718724a9096800754cd3abeca6a1580)) + - Add cast to usize, as it is confusing in the context of an exercise about Option ([f6cffc7e](https://github.com/rust-lang/rustlings/commit/f6cffc7e487b42f15a6f958e49704c93a8d4465b)) +- **option2:** Add TODO to comments (#400) ([10967bce](https://github.com/rust-lang/rustlings/commit/10967bce57682812dc0891a9f9757da1a9d87404)) +- **options1:** Add hint about Array Initialization (#389) ([9f75554f](https://github.com/rust-lang/rustlings/commit/9f75554f2a30295996f03f0160b98c0458305502)) +- **test2:** name of type String and &str (#394) ([d6c0a688](https://github.com/rust-lang/rustlings/commit/d6c0a688e6a96f93ad60d540d4b326f342fc0d45)) +- **variables6:** minor typo (#419) ([524e17df](https://github.com/rust-lang/rustlings/commit/524e17df10db95f7b90a0f75cc8997182a8a4094)) + ## 3.0.0 (2020-04-11) #### Breaking Changes -* make "compile" exercises print output (#278) ([3b6d5c](https://github.com/fmoko/rustlings/commit/3b6d5c3aaa27a242a832799eb66e96897d26fde3)) +- make "compile" exercises print output (#278) ([3b6d5c](https://github.com/fmoko/rustlings/commit/3b6d5c3aaa27a242a832799eb66e96897d26fde3)) #### Bug Fixes -* **primitive_types:** revert primitive_types4 (#296) ([b3a3351e](https://github.com/rust-lang/rustlings/commit/b3a3351e8e6a0bdee07077d7b0382953821649ae)) -* **run:** compile clippy exercise files (#295) ([3ab084a4](https://github.com/rust-lang/rustlings/commit/3ab084a421c0f140ae83bf1fc3f47b39342e7373)) -* **conversions:** - * add additional test to meet exercise rules (#284) ([bc22ec3](https://github.com/fmoko/rustlings/commit/bc22ec382f843347333ef1301fc1bad773657f38)) - * remove duplicate not done comment (#292) ([dab90f](https://github.com/fmoko/rustlings/commit/dab90f7b91a6000fe874e3d664f244048e5fa342)) -* don't hardcode documentation version for traits (#288) ([30e6af](https://github.com/fmoko/rustlings/commit/30e6af60690c326fb5d3a9b7335f35c69c09137d)) +- **primitive_types:** revert primitive_types4 (#296) ([b3a3351e](https://github.com/rust-lang/rustlings/commit/b3a3351e8e6a0bdee07077d7b0382953821649ae)) +- **run:** compile clippy exercise files (#295) ([3ab084a4](https://github.com/rust-lang/rustlings/commit/3ab084a421c0f140ae83bf1fc3f47b39342e7373)) +- **conversions:** + - add additional test to meet exercise rules (#284) ([bc22ec3](https://github.com/fmoko/rustlings/commit/bc22ec382f843347333ef1301fc1bad773657f38)) + - remove duplicate not done comment (#292) ([dab90f](https://github.com/fmoko/rustlings/commit/dab90f7b91a6000fe874e3d664f244048e5fa342)) +- don't hardcode documentation version for traits (#288) ([30e6af](https://github.com/fmoko/rustlings/commit/30e6af60690c326fb5d3a9b7335f35c69c09137d)) #### Features -* add Option2 exercise (#290) ([86b5c08b](https://github.com/rust-lang/rustlings/commit/86b5c08b9bea1576127a7c5f599f5752072c087d)) -* add exercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4)) -* add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229)) -* **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71)) +- add Option2 exercise (#290) ([86b5c08b](https://github.com/rust-lang/rustlings/commit/86b5c08b9bea1576127a7c5f599f5752072c087d)) +- add exercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4)) +- add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229)) +- **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71)) + ### 2.2.1 (2020-02-27) #### Bug Fixes -* Re-add cloning the repo to install scripts ([3d9b03c5](https://github.com/rust-lang/rustlings/commit/3d9b03c52b8dc51b140757f6fd25ad87b5782ef5)) +- Re-add cloning the repo to install scripts ([3d9b03c5](https://github.com/rust-lang/rustlings/commit/3d9b03c52b8dc51b140757f6fd25ad87b5782ef5)) #### Features -* Add clippy lints (#269) ([1e2fd9c9](https://github.com/rust-lang/rustlings/commit/1e2fd9c92f8cd6e389525ca1a999fca4c90b5921)) +- Add clippy lints (#269) ([1e2fd9c9](https://github.com/rust-lang/rustlings/commit/1e2fd9c92f8cd6e389525ca1a999fca4c90b5921)) -## 2.2.0 (2020-02-25) +## 2.2.0 (2020-02-25) #### Bug Fixes -* Update deps to version compatable with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401)) -* **docs:** - * Added a necessary step to Windows installation process (#242) ([3906efcd](https://github.com/rust-lang/rustlings/commit/3906efcd52a004047b460ed548037093de3f523f)) - * Fixed mangled sentence from book; edited for clarity (#266) ([ade52ff](https://github.com/rust-lang/rustlings/commit/ade52ffb739987287ddd5705944c8777705faed9)) - * Updated iterators readme to account for iterators4 exercise (#273) ([bec8e3a](https://github.com/rust-lang/rustlings/commit/bec8e3a644cbd88db1c73ea5f1d8a364f4a34016)) -* **installation:** make fatal errors more obvious (#272) ([17d0951e](https://github.com/rust-lang/rustlings/commit/17d0951e66fda8e11b204d5c4c41a0d5e22e78f7)) -* **iterators2:** - * Remove reference to missing iterators2.rs (#245) ([419f7797](https://github.com/rust-lang/rustlings/commit/419f7797f294e4ce6a2b883199731b5bde77d262)) -* **as_ref_mut:** Enable a test and improve per clippy's suggestion (#256) ([dfdf809](https://github.com/rust-lang/rustlings/commit/dfdf8093ebbd4145864995627b812780de52f902)) -* **tests1:** - * Change test command ([fe10e06c](https://github.com/rust-lang/rustlings/commit/fe10e06c3733ddb4a21e90d09bf79bfe618e97ce) - * Correct test command in tests1.rs comment (#263) ([39fa7ae](https://github.com/rust-lang/rustlings/commit/39fa7ae8b70ad468da49b06f11b2383135a63bcf)) +- Update deps to version compatable with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401)) +- **docs:** + - Added a necessary step to Windows installation process (#242) ([3906efcd](https://github.com/rust-lang/rustlings/commit/3906efcd52a004047b460ed548037093de3f523f)) + - Fixed mangled sentence from book; edited for clarity (#266) ([ade52ff](https://github.com/rust-lang/rustlings/commit/ade52ffb739987287ddd5705944c8777705faed9)) + - Updated iterators readme to account for iterators4 exercise (#273) ([bec8e3a](https://github.com/rust-lang/rustlings/commit/bec8e3a644cbd88db1c73ea5f1d8a364f4a34016)) +- **installation:** make fatal errors more obvious (#272) ([17d0951e](https://github.com/rust-lang/rustlings/commit/17d0951e66fda8e11b204d5c4c41a0d5e22e78f7)) +- **iterators2:** + - Remove reference to missing iterators2.rs (#245) ([419f7797](https://github.com/rust-lang/rustlings/commit/419f7797f294e4ce6a2b883199731b5bde77d262)) +- **as_ref_mut:** Enable a test and improve per clippy's suggestion (#256) ([dfdf809](https://github.com/rust-lang/rustlings/commit/dfdf8093ebbd4145864995627b812780de52f902)) +- **tests1:** + - Change test command ([fe10e06c](https://github.com/rust-lang/rustlings/commit/fe10e06c3733ddb4a21e90d09bf79bfe618e97ce) + - Correct test command in tests1.rs comment (#263) ([39fa7ae](https://github.com/rust-lang/rustlings/commit/39fa7ae8b70ad468da49b06f11b2383135a63bcf)) #### Features -* Add variables5.rs exercise (#264) ([0c73609e](https://github.com/rust-lang/rustlings/commit/0c73609e6f2311295e95d6f96f8c747cfc4cba03)) -* Show a completion message when watching (#253) ([d25ee55a](https://github.com/rust-lang/rustlings/commit/d25ee55a3205882d35782e370af855051b39c58c)) -* Add type conversion and parsing exercises (#249) ([0c85dc11](https://github.com/rust-lang/rustlings/commit/0c85dc1193978b5165491b99cc4922caf8d14a65)) -* Created consistent money unit (#258) ([fd57f8f](https://github.com/rust-lang/rustlings/commit/fd57f8f2c1da2af8ddbebbccec214e6f40f4dbab)) -* Enable test for exercise test4 (#276) ([8b971ff](https://github.com/rust-lang/rustlings/commit/8b971ffab6079a706ac925f5917f987932b55c07)) -* Added traits exercises (#274 but specifically #216, which originally added - this :heart:) ([b559cdd](https://github.com/rust-lang/rustlings/commit/b559cdd73f32c0d0cfc1feda39f82b3e3583df17)) - +- Add variables5.rs exercise (#264) ([0c73609e](https://github.com/rust-lang/rustlings/commit/0c73609e6f2311295e95d6f96f8c747cfc4cba03)) +- Show a completion message when watching (#253) ([d25ee55a](https://github.com/rust-lang/rustlings/commit/d25ee55a3205882d35782e370af855051b39c58c)) +- Add type conversion and parsing exercises (#249) ([0c85dc11](https://github.com/rust-lang/rustlings/commit/0c85dc1193978b5165491b99cc4922caf8d14a65)) +- Created consistent money unit (#258) ([fd57f8f](https://github.com/rust-lang/rustlings/commit/fd57f8f2c1da2af8ddbebbccec214e6f40f4dbab)) +- Enable test for exercise test4 (#276) ([8b971ff](https://github.com/rust-lang/rustlings/commit/8b971ffab6079a706ac925f5917f987932b55c07)) +- Added traits exercises (#274 but specifically #216, which originally added + this :heart:) ([b559cdd](https://github.com/rust-lang/rustlings/commit/b559cdd73f32c0d0cfc1feda39f82b3e3583df17)) + ## 2.1.0 (2019-11-27) #### Bug Fixes -* add line numbers in several exercises and hints ([b565c4d3](https://github.com/rust-lang/rustlings/commit/b565c4d3e74e8e110bef201a082fa1302722a7c3)) -* **arc1:** Fix some words in the comment ([c42c3b21](https://github.com/rust-lang/rustlings/commit/c42c3b2101df9164c8cd7bb344def921e5ba3e61)) -* **enums:** Add link to chapter on pattern syntax (#242) ([615ce327](https://github.com/rust-lang/rustlings/commit/615ce3279800c56d89f19d218ccb7ef576624feb)) -* **primitive_types4:** - * update outdated hint ([4c5189df](https://github.com/rust-lang/rustlings/commit/4c5189df2bdd9a231f6b2611919ba5aa14da0d3f)) - * update outdated comment ([ded2c034](https://github.com/rust-lang/rustlings/commit/ded2c034ba93fa1e3c2c2ea16b83abc1a57265e8)) -* **strings2:** update line number in hint ([a09f684f](https://github.com/rust-lang/rustlings/commit/a09f684f05c58d239a6fc59ec5f81c2533e8b820)) -* **variables1:** Correct wrong word in comment ([fda5a470](https://github.com/rust-lang/rustlings/commit/fda5a47069e0954f16a04e8e50945e03becb71a5)) +- add line numbers in several exercises and hints ([b565c4d3](https://github.com/rust-lang/rustlings/commit/b565c4d3e74e8e110bef201a082fa1302722a7c3)) +- **arc1:** Fix some words in the comment ([c42c3b21](https://github.com/rust-lang/rustlings/commit/c42c3b2101df9164c8cd7bb344def921e5ba3e61)) +- **enums:** Add link to chapter on pattern syntax (#242) ([615ce327](https://github.com/rust-lang/rustlings/commit/615ce3279800c56d89f19d218ccb7ef576624feb)) +- **primitive_types4:** + - update outdated hint ([4c5189df](https://github.com/rust-lang/rustlings/commit/4c5189df2bdd9a231f6b2611919ba5aa14da0d3f)) + - update outdated comment ([ded2c034](https://github.com/rust-lang/rustlings/commit/ded2c034ba93fa1e3c2c2ea16b83abc1a57265e8)) +- **strings2:** update line number in hint ([a09f684f](https://github.com/rust-lang/rustlings/commit/a09f684f05c58d239a6fc59ec5f81c2533e8b820)) +- **variables1:** Correct wrong word in comment ([fda5a470](https://github.com/rust-lang/rustlings/commit/fda5a47069e0954f16a04e8e50945e03becb71a5)) #### Features -* **watch:** show hint while watching ([8143d57b](https://github.com/rust-lang/rustlings/commit/8143d57b4e88c51341dd4a18a14c536042cc009c)) +- **watch:** show hint while watching ([8143d57b](https://github.com/rust-lang/rustlings/commit/8143d57b4e88c51341dd4a18a14c536042cc009c)) + ## 2.0.0 (2019-11-12) #### Bug Fixes -* **default:** Clarify the installation procedure ([c371b853](https://github.com/rust-lang/rustlings/commit/c371b853afa08947ddeebec0edd074b171eeaae0)) -* **info:** Fix trailing newlines for hints ([795b6e34](https://github.com/rust-lang/rustlings/commit/795b6e348094a898e9227a14f6232f7bb94c8d31)) -* **run:** make `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3)) +- **default:** Clarify the installation procedure ([c371b853](https://github.com/rust-lang/rustlings/commit/c371b853afa08947ddeebec0edd074b171eeaae0)) +- **info:** Fix trailing newlines for hints ([795b6e34](https://github.com/rust-lang/rustlings/commit/795b6e348094a898e9227a14f6232f7bb94c8d31)) +- **run:** make `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3)) #### Breaking Changes -* Refactor hint system ([9bdb0a12](https://github.com/rust-lang/rustlings/commit/9bdb0a12e45a8e9f9f6a4bd4a9c172c5376c7f60)) -* improve `watch` execution mode ([2cdd6129](https://github.com/rust-lang/rustlings/commit/2cdd61294f0d9a53775ee24ad76435bec8a21e60)) -* Index exercises by name ([627cdc07](https://github.com/rust-lang/rustlings/commit/627cdc07d07dfe6a740e885e0ddf6900e7ec336b)) -* **run:** makes `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3)) +- Refactor hint system ([9bdb0a12](https://github.com/rust-lang/rustlings/commit/9bdb0a12e45a8e9f9f6a4bd4a9c172c5376c7f60)) +- improve `watch` execution mode ([2cdd6129](https://github.com/rust-lang/rustlings/commit/2cdd61294f0d9a53775ee24ad76435bec8a21e60)) +- Index exercises by name ([627cdc07](https://github.com/rust-lang/rustlings/commit/627cdc07d07dfe6a740e885e0ddf6900e7ec336b)) +- **run:** makes `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3)) #### Features -* **cli:** check for rustc before doing anything ([36a033b8](https://github.com/rust-lang/rustlings/commit/36a033b87a6549c1e5639c908bf7381c84f4f425)) -* **hint:** Add test for hint ([ce9fa6eb](https://github.com/rust-lang/rustlings/commit/ce9fa6ebbfdc3e7585d488d9409797285708316f)) +- **cli:** check for rustc before doing anything ([36a033b8](https://github.com/rust-lang/rustlings/commit/36a033b87a6549c1e5639c908bf7381c84f4f425)) +- **hint:** Add test for hint ([ce9fa6eb](https://github.com/rust-lang/rustlings/commit/ce9fa6ebbfdc3e7585d488d9409797285708316f)) + ### 1.5.1 (2019-11-11) #### Bug Fixes -* **errors3:** Update hint ([dcfb427b](https://github.com/rust-lang/rustlings/commit/dcfb427b09585f0193f0a294443fdf99f11c64cb), closes [#185](https://github.com/rust-lang/rustlings/issues/185)) -* **if1:** Remove `return` reference ([ad03d180](https://github.com/rust-lang/rustlings/commit/ad03d180c9311c0093e56a3531eec1a9a70cdb45)) -* **strings:** Move Strings before Structs ([6dcecb38](https://github.com/rust-lang/rustlings/commit/6dcecb38a4435593beb87c8e12d6314143631482), closes [#204](https://github.com/rust-lang/rustlings/issues/204)) -* **structs1:** Remove misleading comment ([f72e5a8f](https://github.com/rust-lang/rustlings/commit/f72e5a8f05568dde04eaeac10b9a69872f21cb37)) -* **threads:** Move Threads behind SLT ([fbe91a67](https://github.com/rust-lang/rustlings/commit/fbe91a67a482bfe64cbcdd58d06ba830a0f39da3), closes [#205](https://github.com/rust-lang/rustlings/issues/205)) -* **watch:** clear screen before each `verify()` ([3aff590](https://github.com/rust-lang/rustlings/commit/3aff59085586c24196a547c2693adbdcf4432648)) +- **errors3:** Update hint ([dcfb427b](https://github.com/rust-lang/rustlings/commit/dcfb427b09585f0193f0a294443fdf99f11c64cb), closes [#185](https://github.com/rust-lang/rustlings/issues/185)) +- **if1:** Remove `return` reference ([ad03d180](https://github.com/rust-lang/rustlings/commit/ad03d180c9311c0093e56a3531eec1a9a70cdb45)) +- **strings:** Move Strings before Structs ([6dcecb38](https://github.com/rust-lang/rustlings/commit/6dcecb38a4435593beb87c8e12d6314143631482), closes [#204](https://github.com/rust-lang/rustlings/issues/204)) +- **structs1:** Remove misleading comment ([f72e5a8f](https://github.com/rust-lang/rustlings/commit/f72e5a8f05568dde04eaeac10b9a69872f21cb37)) +- **threads:** Move Threads behind SLT ([fbe91a67](https://github.com/rust-lang/rustlings/commit/fbe91a67a482bfe64cbcdd58d06ba830a0f39da3), closes [#205](https://github.com/rust-lang/rustlings/issues/205)) +- **watch:** clear screen before each `verify()` ([3aff590](https://github.com/rust-lang/rustlings/commit/3aff59085586c24196a547c2693adbdcf4432648)) + ## 1.5.0 (2019-11-09) #### Bug Fixes -* **test1:** Rewrite logic ([79a56942](https://github.com/rust-lang/rustlings/commit/79a569422c8309cfc9e4aed25bf4ab3b3859996b)) -* **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6)) -* **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6)) -* **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac)) -* **option1:** - * Fix arguments passed to assert! macro (#222) ([4c2cf6da](https://github.com/rust-lang/rustlings/commit/4c2cf6da755efe02725e05ecc3a303304c10a6da)) - * Fix arguments passed to assert! macro ([ead4f7af](https://github.com/rust-lang/rustlings/commit/ead4f7af9e10e53418efdde5c359159347282afd)) - * Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6)) -* **primitive_types4:** Fail on a slice covering the wrong area ([5b1e673c](https://github.com/rust-lang/rustlings/commit/5b1e673cec1658afc4ebbbc800213847804facf5)) -* **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5)) -* **test1:** - * Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446)) - * renamed function name to snake case closes #180 ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b)) +- **test1:** Rewrite logic ([79a56942](https://github.com/rust-lang/rustlings/commit/79a569422c8309cfc9e4aed25bf4ab3b3859996b)) +- **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6)) +- **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6)) +- **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac)) +- **option1:** + - Fix arguments passed to assert! macro (#222) ([4c2cf6da](https://github.com/rust-lang/rustlings/commit/4c2cf6da755efe02725e05ecc3a303304c10a6da)) + - Fix arguments passed to assert! macro ([ead4f7af](https://github.com/rust-lang/rustlings/commit/ead4f7af9e10e53418efdde5c359159347282afd)) + - Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6)) +- **primitive_types4:** Fail on a slice covering the wrong area ([5b1e673c](https://github.com/rust-lang/rustlings/commit/5b1e673cec1658afc4ebbbc800213847804facf5)) +- **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5)) +- **test1:** + - Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446)) + - renamed function name to snake case closes #180 ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b)) #### Features -* Add enums exercises ([dc150321](https://github.com/rust-lang/rustlings/commit/dc15032112fc485226a573a18139e5ce928b1755)) -* Added exercise for struct update syntax ([1c4c8764](https://github.com/rust-lang/rustlings/commit/1c4c8764ed118740cd4cee73272ddc6cceb9d959)) -* **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031)) +- Add enums exercises ([dc150321](https://github.com/rust-lang/rustlings/commit/dc15032112fc485226a573a18139e5ce928b1755)) +- Added exercise for struct update syntax ([1c4c8764](https://github.com/rust-lang/rustlings/commit/1c4c8764ed118740cd4cee73272ddc6cceb9d959)) +- **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031)) -### 1.4.1 (2019-08-13) +### 1.4.1 (2019-08-13) #### Bug Fixes -* **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac)) -* **option1:** Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6)) -* **test1:** Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446)) - - +- **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac)) +- **option1:** Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6)) +- **test1:** Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446)) + ## 1.4.0 (2019-07-13) #### Bug Fixes -* **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6)) -* **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6)) -* **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5)) -* **test1:** renamed function name to snake case ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b)) -* **cli:** Check if changed exercise file exists before calling verify ([ba85ca3](https://github.com/rust-lang/rustlings/commit/ba85ca32c4cfc61de46851ab89f9c58a28f33c88)) -* **structs1:** Fix the irrefutable let pattern warning ([cc6a141](https://github.com/rust-lang/rustlings/commit/cc6a14104d7c034eadc98297eaaa972d09c50b1f)) +- **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6)) +- **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6)) +- **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5)) +- **test1:** renamed function name to snake case ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b)) +- **cli:** Check if changed exercise file exists before calling verify ([ba85ca3](https://github.com/rust-lang/rustlings/commit/ba85ca32c4cfc61de46851ab89f9c58a28f33c88)) +- **structs1:** Fix the irrefutable let pattern warning ([cc6a141](https://github.com/rust-lang/rustlings/commit/cc6a14104d7c034eadc98297eaaa972d09c50b1f)) #### Features -* **changelog:** Use clog for changelogs ([34e31232](https://github.com/rust-lang/rustlings/commit/34e31232dfddde284a341c9609b33cd27d9d5724)) -* **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031)) +- **changelog:** Use clog for changelogs ([34e31232](https://github.com/rust-lang/rustlings/commit/34e31232dfddde284a341c9609b33cd27d9d5724)) +- **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031)) + ### 1.3.0 (2019-06-05) #### Features @@ -652,6 +692,7 @@ - Remove highlighting and syntect (#167, @komaeda) + ### 1.2.2 (2019-05-07) #### Bug Fixes @@ -659,6 +700,7 @@ - Reverted `--nocapture` flag since it was causing tests to pass unconditionally + ### 1.2.1 (2019-04-22) #### Bug Fixes @@ -667,6 +709,7 @@ - Provide a nicer error message for when you're in the wrong directory + ### 1.2.0 (2019-04-22) #### Features @@ -675,6 +718,7 @@ - Use --nocapture when testing, enabling `println!` when running (@komaeda) + ### 1.1.1 (2019-04-14) #### Bug fixes @@ -688,6 +732,7 @@ - Canonicalize paths to fix path matching (@cjpearce, #143) + ### 1.1.0 (2019-03-20) - errors2.rs: update link to Rust book (#124) @@ -698,6 +743,7 @@ - Verify that rust version is recent enough to install Rustlings (#131) + ### 1.0.1 (2019-03-06) - Adds a way to install Rustlings in one command (`curl -L https://git.io/rustlings | bash`) @@ -705,6 +751,7 @@ - Reworks the exercise management to use an external TOML file instead of just listing them in the code + ### 1.0.0 (2019-03-06) Initial release. diff --git a/Cargo.lock b/Cargo.lock index 49f20b6641..3d04953d90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -459,7 +459,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rustlings" -version = "5.3.0" +version = "5.4.0" dependencies = [ "argh", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index c2c54fd2ed..4b4505845f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustlings" -version = "5.3.0" +version = "5.4.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", diff --git a/README.md b/README.md index 956bb6d0d5..4056ba896d 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,12 @@ curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | This will install Rustlings and give you access to the `rustlings` command. Run it to get started! ### Nix + Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.3.0) -git clone -b 5.3.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.0) +git clone -b 5.4.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings # if nix version > 2.3 nix develop @@ -70,8 +71,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.3.0) -git clone -b 5.3.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.0) +git clone -b 5.4.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/flake.nix b/flake.nix index 2c29ffa50a..5d485801e7 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; - version = "5.3.0"; + version = "5.4.0"; buildInputs = cargoBuildInputs; diff --git a/src/main.rs b/src/main.rs index 59adb418c0..c09088ba5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ mod run; mod verify; // In sync with crate version -const VERSION: &str = "5.3.0"; +const VERSION: &str = "5.4.0"; #[derive(FromArgs, PartialEq, Debug)] /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code From 48ce9d2fd827a18314a6fdfdc5895091c6888755 Mon Sep 17 00:00:00 2001 From: magnusrodseth <59113973+magnusrodseth@users.noreply.github.com> Date: Sun, 12 Feb 2023 18:26:13 +0100 Subject: [PATCH 0144/1432] docs: add link to docs about `iter_mut` and `map` --- exercises/vecs/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exercises/vecs/README.md b/exercises/vecs/README.md index ebe90bf310..8ff9b85f52 100644 --- a/exercises/vecs/README.md +++ b/exercises/vecs/README.md @@ -13,3 +13,5 @@ the other useful data structure, hash maps, later. ## Further information - [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) +- [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut) +- [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) From bbdc5c60395ce7e126bcd6f8babacdf6a76ea2d9 Mon Sep 17 00:00:00 2001 From: Daan Wynen Date: Tue, 14 Feb 2023 20:37:33 +0100 Subject: [PATCH 0145/1432] refactor(cow1): replace main with tests Following the discussion in #1195 this is the best I could come up with. The issue for me (and apparently a few other learners) was that the code needed to complete the exercise was not _missing_, but was rather there but wrong. In the end, what made the difference between this exercise and others (for me) was that in this exercise I was supposed to learn what to *expect* of an output. So I think it makes sense here to let the learner modify the tests and not the code itself. Fixes #1195 Signed-off-by: Daan Wynen # Conflicts: # info.toml --- exercises/smart_pointers/cow1.rs | 66 ++++++++++++++++++++++---------- info.toml | 6 +-- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/exercises/smart_pointers/cow1.rs b/exercises/smart_pointers/cow1.rs index 5fba2519d3..885138a740 100644 --- a/exercises/smart_pointers/cow1.rs +++ b/exercises/smart_pointers/cow1.rs @@ -4,6 +4,9 @@ // 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 required. // The type is designed to work with general borrowed data via the Borrow trait. +// +// 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. // I AM NOT DONE @@ -20,29 +23,52 @@ fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { input } -fn main() { - // No clone occurs because `input` doesn't need to be mutated. - let slice = [0, 1, 2]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Borrowed(_) => println!("I borrowed the slice!"), - _ => panic!("expected borrowed value"), +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reference_mutation() -> Result<(), &'static str> { + // Clone occurs because `input` needs to be mutated. + let slice = [-1, 0, 1]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + Cow::Owned(_) => Ok(()), + _ => Err("Expected owned value"), + } } - // Clone occurs because `input` needs to be mutated. - let slice = [-1, 0, 1]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Owned(_) => println!("I modified the slice and now own it!"), - _ => panic!("expected owned value"), + #[test] + fn reference_no_mutation() -> Result<(), &'static str> { + // No clone occurs because `input` doesn't need to be mutated. + let slice = [0, 1, 2]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + // TODO + } } - // No clone occurs because `input` is already owned. - let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - Cow::Borrowed(_) => println!("I own this slice!"), - _ => panic!("expected borrowed value"), + #[test] + fn owned_no_mutation() -> Result<(), &'static str> { + // We can also pass `slice` without `&` so Cow owns it directly. + // In this case no mutation occurs and thus also no clone, + // but the result is still owned because it always was. + let slice = vec![0, 1, 2]; + let mut input = Cow::from(slice); + match abs_all(&mut input) { + // TODO + } + } + + #[test] + fn owned_mutation() -> Result<(), &'static str> { + // Of course this is also the case if a mutation does occur. + // In this case the call to `to_mut()` returns a reference to + // the same data as before. + let slice = vec![-1, 0, 1]; + let mut input = Cow::from(slice); + match abs_all(&mut input) { + // TODO + } } } diff --git a/info.toml b/info.toml index 299d932fa5..2f42403378 100644 --- a/info.toml +++ b/info.toml @@ -1010,11 +1010,11 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" path = "exercises/smart_pointers/cow1.rs" -mode = "compile" +mode = "test" hint = """ -Since the vector is already owned, the `Cow` type doesn't need to clone it. +If Cow already owns the data it doesn't need to clone it when to_mut() is called. -Checkout https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation +Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation on the `Cow` type. """ From e1e67d0d41b1795ea497d66c422c3797bf1c0bf2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 13:16:58 +0000 Subject: [PATCH 0146/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 85cd1774cb..bd3b20208a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -262,6 +262,7 @@ authors. Grzegorz Ε»ur
Grzegorz Ε»ur

πŸ–‹ + Daan Wynen
Daan Wynen

πŸ–‹ From 046a18cd16f66f4245ad738f54035e4eae405163 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 13:16:59 +0000 Subject: [PATCH 0147/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 5db992ac5e..4eaab0d520 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1839,6 +1839,15 @@ "contributions": [ "content" ] + }, + { + "login": "black-puppydog", + "name": "Daan Wynen", + "avatar_url": "https://avatars.githubusercontent.com/u/189241?v=4", + "profile": "https://github.com/black-puppydog", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From ef0b25c3921503588fc1c76e43cfa454e0f447b1 Mon Sep 17 00:00:00 2001 From: Anush Date: Fri, 17 Feb 2023 22:51:42 +0530 Subject: [PATCH 0148/1432] Removed verbose instructions from the uninstall section --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 4056ba896d..14ae2317a6 100644 --- a/README.md +++ b/README.md @@ -155,8 +155,7 @@ for you: rm -rf rustlings # or your custom folder name, if you chose and or renamed it ``` -Second, since Rustlings got installed via `cargo install`, it's only reasonable to assume that you can also remove it using Cargo, and -exactly that is the case. Run `cargo uninstall` to remove the `rustlings` binary: +Second, run `cargo uninstall` to remove the `rustlings` binary: ```bash cargo uninstall rustlings From 338c95f120698f642165eb22c76d8b98483784fb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 17 Feb 2023 18:03:58 +0000 Subject: [PATCH 0149/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index bd3b20208a..80c10706bc 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -263,6 +263,7 @@ authors. Grzegorz Ε»ur
Grzegorz Ε»ur

πŸ–‹ Daan Wynen
Daan Wynen

πŸ–‹ + Anush
Anush

πŸ“– From 65f05f0541cfeba95dea6a390009d6deeb08e696 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 17 Feb 2023 18:03:59 +0000 Subject: [PATCH 0150/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4eaab0d520..8bd8c20ea1 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1848,6 +1848,15 @@ "contributions": [ "content" ] + }, + { + "login": "Anush008", + "name": "Anush", + "avatar_url": "https://avatars.githubusercontent.com/u/46051506?v=4", + "profile": "https://github.com/Anush008", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 8, From 238a496af536b0390c7d7987aee6c63a9a25427d Mon Sep 17 00:00:00 2001 From: Gleb Shevchenko Date: Sat, 18 Feb 2023 14:02:11 +0100 Subject: [PATCH 0151/1432] fix: unify undisclosed type notation in errors5.rs --- exercises/error_handling/errors5.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs index 6da06ef371..eb5506cb5f 100644 --- a/exercises/error_handling/errors5.rs +++ b/exercises/error_handling/errors5.rs @@ -4,7 +4,7 @@ // This exercise uses some concepts that we won't get to until later in the course, like `Box` and the // `From` trait. It's not important to understand them in detail right now, but you can read ahead if you like. -// For now, think of the `Box` type as an "I want anything that does ???" type, which, given +// For now, think of the `Box` type as an "I want anything that does ???" type, which, given // Rust's usual standards for runtime safety, should strike you as somewhat lenient! // In short, this particular use case for boxes is for when you want to own a value and you care only that it is a From 1272d0b99003ac645c214b8170ffac875d84885d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 18 Feb 2023 13:26:37 +0000 Subject: [PATCH 0152/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 80c10706bc..b3f0452e3c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -264,6 +264,7 @@ authors. Grzegorz Ε»ur
Grzegorz Ε»ur

πŸ–‹ Daan Wynen
Daan Wynen

πŸ–‹ Anush
Anush

πŸ“– + Gleb Shevchenko
Gleb Shevchenko

πŸ–‹ From d42ac49de410a19b0c6ce7694a59822dc7f11c17 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 18 Feb 2023 13:26:38 +0000 Subject: [PATCH 0153/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8bd8c20ea1..e2c721360e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1857,6 +1857,15 @@ "contributions": [ "doc" ] + }, + { + "login": "shgew", + "name": "Gleb Shevchenko", + "avatar_url": "https://avatars.githubusercontent.com/u/5584672?v=4", + "profile": "https://github.com/shgew", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From fef8314d3d45aa1ec3655cd6e9a52f877da51c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20T=C3=B6rnquist?= Date: Sat, 18 Feb 2023 18:43:34 +0100 Subject: [PATCH 0154/1432] fix(move_semantics2): add expected output comment You can easily get this to compile with `vec0` being `[]` and `vec1` being `[22, 44, 66, 88]` --- exercises/move_semantics/move_semantics2.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 64870850ec..68dbf021d5 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -2,6 +2,10 @@ // Make me compile without changing line 13 or moving line 10! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. +// Expected output: +// vec0 has length 3 content `[22, 44, 66]` +// vec1 has length 4 content `[22, 44, 66, 88]` + // I AM NOT DONE fn main() { From 89069f78b165e2e10d0b0a72c8d6b246bb9e9c94 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 18 Feb 2023 19:26:40 +0000 Subject: [PATCH 0155/1432] chore: update move_semantics4.rs' hint after #144, the signature doesn't need changing anymore --- info.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/info.toml b/info.toml index 2f42403378..431b49ff7c 100644 --- a/info.toml +++ b/info.toml @@ -325,8 +325,7 @@ doing one step and then fixing the compiler errors that result! So the end goal is to: - get rid of the first line in main that creates the new vector - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` - - we don't want to pass anything to `fill_vec`, so its signature should - reflect that it does not take any arguments + - `fill_vec` has had its signature changed, which our call should reflect - since we're not creating a new vec in `main` anymore, we need to create a new vec in `fill_vec`, similarly to the way we did in `main`""" From 045d86aa42cbdb81d2be123fc9a7379dd5e38b08 Mon Sep 17 00:00:00 2001 From: Cyril MARPAUD Date: Sat, 18 Feb 2023 21:38:43 +0100 Subject: [PATCH 0156/1432] refactor(arc1): improve readability by avoiding implicit dereference --- exercises/smart_pointers/arc1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/smart_pointers/arc1.rs b/exercises/smart_pointers/arc1.rs index 93a27036f2..ffb306afb3 100644 --- a/exercises/smart_pointers/arc1.rs +++ b/exercises/smart_pointers/arc1.rs @@ -32,7 +32,7 @@ fn main() { for offset in 0..8 { let child_numbers = // TODO joinhandles.push(thread::spawn(move || { - let sum: u32 = child_numbers.iter().filter(|n| *n % 8 == offset).sum(); + let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); println!("Sum of offset {} is {}", offset, sum); })); } From 1ac66f372b670f5218334c1bb0dadcb67ca5cbd4 Mon Sep 17 00:00:00 2001 From: Aaron Suggs Date: Tue, 21 Feb 2023 09:45:59 -0500 Subject: [PATCH 0157/1432] docs: clarify instructions on iterators5.rs I changed the sentence that referenced the imperative implementation in iterators5.rs. That implementation was already removed and replaced with `todo!()` --- exercises/iterators/iterators5.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/exercises/iterators/iterators5.rs b/exercises/iterators/iterators5.rs index 0593d1231b..8709795675 100644 --- a/exercises/iterators/iterators5.rs +++ b/exercises/iterators/iterators5.rs @@ -2,13 +2,11 @@ // 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 -// number of exercises with a given progress. These counting functions use -// imperative style for loops. Recreate this counting functionality using -// iterators. Only the two iterator methods (count_iterator and -// count_collection_iterator) need to be modified. +// number of exercises with a given progress. Recreate this counting +// 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. -// -// Make the code compile and the tests pass. // I AM NOT DONE From de24536187841eed4ab60aa375e2a02e1abe3f63 Mon Sep 17 00:00:00 2001 From: 0lhi <74732674+0lhi@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:43:23 +0100 Subject: [PATCH 0158/1432] macros4.rs: Add rustfmt::skip to prevent auto-fix. The `macros4.rs` challenge can automatically be solved by rustfmt without the user noticing. Adding `#[rustfmt::skip]` above the `macro_rules!` line fixes this issue. --- exercises/macros/macros4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index c1fc5e8b16..4ee9803550 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -3,6 +3,7 @@ // I AM NOT DONE +#[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); From 8c88f769b6f87d695a08b5d61a3d3d9fc447323a Mon Sep 17 00:00:00 2001 From: Chad Dougherty Date: Fri, 24 Feb 2023 08:51:03 -0500 Subject: [PATCH 0159/1432] rustfmt rustfmt converts "main ()" -> "main()" --- exercises/iterators/iterators1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/iterators/iterators1.rs b/exercises/iterators/iterators1.rs index 0379c6bb41..f9cc3b399a 100644 --- a/exercises/iterators/iterators1.rs +++ b/exercises/iterators/iterators1.rs @@ -10,7 +10,7 @@ // I AM NOT DONE -fn main () { +fn main() { let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; let mut my_iterable_fav_fruits = ???; // TODO: Step 1 From 1afc7ed8c53a2845f557e88b31e1f45f2f28abe4 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Sat, 25 Feb 2023 17:20:31 +0100 Subject: [PATCH 0160/1432] Update README.md Create Rustlings Codespace in one click --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 383a96ebb9..305411418a 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ If you get a permission denied message, you might have to exclude the directory [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) -Open up Rustlings in [Codespaces](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace-for-a-repository#creating-a-codespace-for-a-repository) +[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=master) ## Manually From 351e4e2460338785a474d66fd8c2ea1a324d2104 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Sat, 25 Feb 2023 17:56:00 +0100 Subject: [PATCH 0161/1432] Update devcontainer.json --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0fd90cc94b..e1b2cec10b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "image": "mcr.microsoft.com/devcontainers/universal:2", + "image": "mcr.microsoft.com/devcontainers/universal:2-linux", "waitFor": "onCreateCommand", "onCreateCommand": ".devcontainer/setup.sh", "updateContentCommand": "cargo build", From 86f8fa8e6e87274543a41697c2fdc810fbdbc079 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Sat, 25 Feb 2023 17:57:34 +0100 Subject: [PATCH 0162/1432] Update README.md Fix refs from master to main --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 305411418a..e78d1a3715 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ If you get a permission denied message, you might have to exclude the directory [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) -[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=master) +[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=main) ## Manually From 3c841c4685608b9eb91103451626045daea24085 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:06:50 +0000 Subject: [PATCH 0163/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index b3f0452e3c..cc0eeda0cb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -265,6 +265,7 @@ authors. Daan Wynen
Daan Wynen

πŸ–‹ Anush
Anush

πŸ“– Gleb Shevchenko
Gleb Shevchenko

πŸ–‹ + Emmanuel Roullit
Emmanuel Roullit

πŸš‡ From 0ad8eec3eb80bc9280611541d22ed8420f3e22cb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:06:51 +0000 Subject: [PATCH 0164/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e2c721360e..e652e5f28e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1866,6 +1866,15 @@ "contributions": [ "content" ] + }, + { + "login": "eroullit", + "name": "Emmanuel Roullit", + "avatar_url": "https://avatars.githubusercontent.com/u/301795?v=4", + "profile": "https://github.com/eroullit", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From 7f06bb5fa7919562a2c0710f91779dc7c25c27bf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:06:57 +0000 Subject: [PATCH 0165/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index b3f0452e3c..c5ad1af438 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -265,6 +265,7 @@ authors. Daan Wynen
Daan Wynen

πŸ–‹ Anush
Anush

πŸ“– Gleb Shevchenko
Gleb Shevchenko

πŸ–‹ + Edmundo Paulino
Edmundo Paulino

πŸš‡ From ac9c1adb757bd5982ba3fcb9a3d50f99aa6959a6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:08:06 +0000 Subject: [PATCH 0166/1432] docs: update AUTHORS.md [skip ci] From eb7f21df040621327c8dd9fc28c11c9745afefea Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 12:08:07 +0000 Subject: [PATCH 0167/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e2c721360e..004e63e737 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1866,6 +1866,15 @@ "contributions": [ "content" ] + }, + { + "login": "mdmundo", + "name": "Edmundo Paulino", + "avatar_url": "https://avatars.githubusercontent.com/u/60408300?v=4", + "profile": "https://github.com/mdmundo", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From 6c5ba7cc013762be48c1a72f1060979296c78e4b Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Sun, 26 Feb 2023 19:30:57 +0330 Subject: [PATCH 0168/1432] Better error message when failing --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 1929691097..4ee56bb1fe 100755 --- a/install.sh +++ b/install.sh @@ -141,7 +141,7 @@ git clone -q https://github.com/rust-lang/rustlings "$Path" cd "$Path" -Version=$(curl -s https://api.github.com/repos/rust-lang/rustlings/releases/latest | ${PY} -c "import json,sys;obj=json.load(sys.stdin);print(obj['tag_name']);") +Version=$(curl -s https://api.github.com/repos/rust-lang/rustlings/releases/latest | ${PY} -c "import json,sys;obj=json.load(sys.stdin);print(obj['tag_name']) if 'tag_name' in obj else sys.exit(f\"Error: {obj['message']}\");") CargoBin="${CARGO_HOME:-$HOME/.cargo}/bin" if [[ -z ${Version} ]] From 52ed5dbcf912602c24c24ba5c79fcf6ee749d999 Mon Sep 17 00:00:00 2001 From: Nidhal Messaoudi Date: Sun, 26 Feb 2023 17:28:24 +0100 Subject: [PATCH 0169/1432] First quiz and its related modules --- exercises/error_handling/errors5.rs | 2 +- exercises/functions/functions1.rs | 6 ++- exercises/functions/functions2.rs | 4 +- exercises/functions/functions3.rs | 4 +- exercises/functions/functions4.rs | 4 +- exercises/functions/functions5.rs | 4 +- exercises/if/if1.rs | 7 ++- exercises/if/if2.rs | 8 ++-- exercises/intro/intro1.rs | 2 - exercises/intro/intro2.rs | 4 +- exercises/iterators/iterators1.rs | 2 +- exercises/macros/macros4.rs | 1 - exercises/quiz1.rs | 16 +++++-- exercises/smart_pointers/arc1.rs | 2 +- exercises/smart_pointers/cow1.rs | 66 +++++++++-------------------- exercises/variables/variables1.rs | 4 +- exercises/variables/variables2.rs | 4 +- exercises/variables/variables3.rs | 4 +- exercises/variables/variables4.rs | 4 +- exercises/variables/variables5.rs | 4 +- exercises/variables/variables6.rs | 4 +- exercises/vecs/README.md | 2 - 22 files changed, 60 insertions(+), 98 deletions(-) diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs index eb5506cb5f..6da06ef371 100644 --- a/exercises/error_handling/errors5.rs +++ b/exercises/error_handling/errors5.rs @@ -4,7 +4,7 @@ // This exercise uses some concepts that we won't get to until later in the course, like `Box` and the // `From` trait. It's not important to understand them in detail right now, but you can read ahead if you like. -// For now, think of the `Box` type as an "I want anything that does ???" type, which, given +// For now, think of the `Box` type as an "I want anything that does ???" type, which, given // Rust's usual standards for runtime safety, should strike you as somewhat lenient! // In short, this particular use case for boxes is for when you want to own a value and you care only that it is a diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 03d8af7020..38c353891d 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,8 +1,10 @@ // functions1.rs // Execute `rustlings hint functions1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { call_me(); } + +fn call_me() { + println!("I'm Nidhal Messaoudi, a software developer going Rusty!!!"); +} diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs index 7d40a578c7..5a51bdfbe4 100644 --- a/exercises/functions/functions2.rs +++ b/exercises/functions/functions2.rs @@ -1,13 +1,11 @@ // functions2.rs // Execute `rustlings hint functions2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { call_me(3); } -fn call_me(num:) { +fn call_me(num: i32) { for i in 0..num { println!("Ring! Call number {}", i + 1); } diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs index 3b9e585b6c..5b2a9a1ab3 100644 --- a/exercises/functions/functions3.rs +++ b/exercises/functions/functions3.rs @@ -1,10 +1,8 @@ // functions3.rs // Execute `rustlings hint functions3` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { - call_me(); + call_me(12); } fn call_me(num: u32) { diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs index 65d5be4ff6..40677fc94f 100644 --- a/exercises/functions/functions4.rs +++ b/exercises/functions/functions4.rs @@ -7,14 +7,12 @@ // in the signatures for now. If anything, this is a good way to peek ahead // to future exercises!) -// I AM NOT DONE - fn main() { let original_price = 51; println!("Your sale price is {}", sale_price(original_price)); } -fn sale_price(price: i32) -> { +fn sale_price(price: i32) -> i32 { if is_even(price) { price - 10 } else { diff --git a/exercises/functions/functions5.rs b/exercises/functions/functions5.rs index 5d7629617f..52b8400f6b 100644 --- a/exercises/functions/functions5.rs +++ b/exercises/functions/functions5.rs @@ -1,13 +1,11 @@ // functions5.rs // Execute `rustlings hint functions5` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { let answer = square(3); println!("The square of 3 is {}", answer); } fn square(num: i32) -> i32 { - num * num; + return num * num; } diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index 587e03f88a..660a093c73 100644 --- a/exercises/if/if1.rs +++ b/exercises/if/if1.rs @@ -1,13 +1,16 @@ // if1.rs // Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! // Do not use: // - another function call // - additional variables + if a > b { + a + } else { + b + } } // Don't mind this for now :) diff --git a/exercises/if/if2.rs b/exercises/if/if2.rs index effddbb6eb..1fca9c3b34 100644 --- a/exercises/if/if2.rs +++ b/exercises/if/if2.rs @@ -4,13 +4,13 @@ // 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. -// I AM NOT DONE - pub fn foo_if_fizz(fizzish: &str) -> &str { - if fizzish == "fizz" { + if fizzish == "fuzz" { + "bar" + } else if fizzish == "fizz" { "foo" } else { - 1 + "baz" } } diff --git a/exercises/intro/intro1.rs b/exercises/intro/intro1.rs index cfc55c306f..5488977887 100644 --- a/exercises/intro/intro1.rs +++ b/exercises/intro/intro1.rs @@ -9,8 +9,6 @@ // 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! -// I AM NOT DONE - fn main() { println!("Hello and"); println!(r#" welcome to... "#); diff --git a/exercises/intro/intro2.rs b/exercises/intro/intro2.rs index efc1af2059..b5b1d0a804 100644 --- a/exercises/intro/intro2.rs +++ b/exercises/intro/intro2.rs @@ -2,8 +2,6 @@ // Make the code print a greeting to the world. // Execute `rustlings hint intro2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { - println!("Hello {}!"); + println!("Hello {}!", "World"); } diff --git a/exercises/iterators/iterators1.rs b/exercises/iterators/iterators1.rs index f9cc3b399a..0379c6bb41 100644 --- a/exercises/iterators/iterators1.rs +++ b/exercises/iterators/iterators1.rs @@ -10,7 +10,7 @@ // I AM NOT DONE -fn main() { +fn main () { let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; let mut my_iterable_fav_fruits = ???; // TODO: Step 1 diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index 4ee9803550..c1fc5e8b16 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -3,7 +3,6 @@ // I AM NOT DONE -#[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index dbb5cdc9a1..9988f9de8f 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -10,10 +10,20 @@ // Write a function that calculates the price of an order of apples given // the quantity bought. No hints this time! -// I AM NOT DONE - // Put your function here! -// fn calculate_price_of_apples { +fn calculate_price_of_apples(quantity: i32) -> i32 { + let mut quantity= quantity; + if quantity <= 40 { + quantity = quantity * 2; + } + return quantity; + + // if quantity > 40 { + // quantity + // } else { + // quantity * 2 + // } +} // Don't modify this function! #[test] diff --git a/exercises/smart_pointers/arc1.rs b/exercises/smart_pointers/arc1.rs index ffb306afb3..93a27036f2 100644 --- a/exercises/smart_pointers/arc1.rs +++ b/exercises/smart_pointers/arc1.rs @@ -32,7 +32,7 @@ fn main() { for offset in 0..8 { let child_numbers = // TODO joinhandles.push(thread::spawn(move || { - let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); + let sum: u32 = child_numbers.iter().filter(|n| *n % 8 == offset).sum(); println!("Sum of offset {} is {}", offset, sum); })); } diff --git a/exercises/smart_pointers/cow1.rs b/exercises/smart_pointers/cow1.rs index 885138a740..5fba2519d3 100644 --- a/exercises/smart_pointers/cow1.rs +++ b/exercises/smart_pointers/cow1.rs @@ -4,9 +4,6 @@ // 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 required. // The type is designed to work with general borrowed data via the Borrow trait. -// -// 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. // I AM NOT DONE @@ -23,52 +20,29 @@ fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { input } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn reference_mutation() -> Result<(), &'static str> { - // Clone occurs because `input` needs to be mutated. - let slice = [-1, 0, 1]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Owned(_) => Ok(()), - _ => Err("Expected owned value"), - } +fn main() { + // No clone occurs because `input` doesn't need to be mutated. + let slice = [0, 1, 2]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + Cow::Borrowed(_) => println!("I borrowed the slice!"), + _ => panic!("expected borrowed value"), } - #[test] - fn reference_no_mutation() -> Result<(), &'static str> { - // No clone occurs because `input` doesn't need to be mutated. - let slice = [0, 1, 2]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - // TODO - } + // Clone occurs because `input` needs to be mutated. + let slice = [-1, 0, 1]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + Cow::Owned(_) => println!("I modified the slice and now own it!"), + _ => panic!("expected owned value"), } - #[test] - fn owned_no_mutation() -> Result<(), &'static str> { - // We can also pass `slice` without `&` so Cow owns it directly. - // In this case no mutation occurs and thus also no clone, - // but the result is still owned because it always was. - let slice = vec![0, 1, 2]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } - } - - #[test] - fn owned_mutation() -> Result<(), &'static str> { - // Of course this is also the case if a mutation does occur. - // In this case the call to `to_mut()` returns a reference to - // the same data as before. - let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } + // No clone occurs because `input` is already owned. + let slice = vec![-1, 0, 1]; + let mut input = Cow::from(slice); + match abs_all(&mut input) { + // TODO + Cow::Borrowed(_) => println!("I own this slice!"), + _ => panic!("expected borrowed value"), } } diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs index f4d182accf..84de9fdcfa 100644 --- a/exercises/variables/variables1.rs +++ b/exercises/variables/variables1.rs @@ -2,9 +2,7 @@ // Make me compile! // Execute `rustlings hint variables1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { - x = 5; + let x = 5; println!("x has the value {}", x); } diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs index 641aeb8e09..6715cf5ce0 100644 --- a/exercises/variables/variables2.rs +++ b/exercises/variables/variables2.rs @@ -1,10 +1,8 @@ // variables2.rs // Execute `rustlings hint variables2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { - let x; + let x = 12; if x == 10 { println!("x is ten!"); } else { diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs index 819b1bc791..30afbf1409 100644 --- a/exercises/variables/variables3.rs +++ b/exercises/variables/variables3.rs @@ -1,9 +1,7 @@ // variables3.rs // Execute `rustlings hint variables3` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { - let x: i32; + let x: i32 = 12; println!("Number {}", x); } diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs index 54491b0a20..8c2ddd6dd4 100644 --- a/exercises/variables/variables4.rs +++ b/exercises/variables/variables4.rs @@ -1,10 +1,8 @@ // variables4.rs // Execute `rustlings hint variables4` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { - let x = 3; + let mut x = 3; println!("Number {}", x); x = 5; // don't change this line println!("Number {}", x); diff --git a/exercises/variables/variables5.rs b/exercises/variables/variables5.rs index 0e670d2afe..1b9d9f487c 100644 --- a/exercises/variables/variables5.rs +++ b/exercises/variables/variables5.rs @@ -1,11 +1,9 @@ // variables5.rs // Execute `rustlings hint variables5` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); - number = 3; // don't rename this variable + let number = 3; // don't rename this variable println!("Number plus two is : {}", number + 2); } diff --git a/exercises/variables/variables6.rs b/exercises/variables/variables6.rs index a8520122c5..e8314b28ae 100644 --- a/exercises/variables/variables6.rs +++ b/exercises/variables/variables6.rs @@ -1,9 +1,7 @@ // variables6.rs // Execute `rustlings hint variables6` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - -const NUMBER = 3; +const NUMBER: i32 = 3; fn main() { println!("Number {}", NUMBER); } diff --git a/exercises/vecs/README.md b/exercises/vecs/README.md index 8ff9b85f52..ebe90bf310 100644 --- a/exercises/vecs/README.md +++ b/exercises/vecs/README.md @@ -13,5 +13,3 @@ the other useful data structure, hash maps, later. ## Further information - [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) -- [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut) -- [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) From f7678b4a9ed03c81e2bedb998d5893f882acf915 Mon Sep 17 00:00:00 2001 From: Nidhal Messaoudi Date: Mon, 27 Feb 2023 12:57:40 +0100 Subject: [PATCH 0170/1432] Learning about primitive data types --- exercises/primitive_types/primitive_types1.rs | 4 +--- exercises/primitive_types/primitive_types2.rs | 10 ++++------ exercises/primitive_types/primitive_types3.rs | 6 +++--- exercises/primitive_types/primitive_types4.rs | 4 +--- exercises/primitive_types/primitive_types5.rs | 4 +--- exercises/primitive_types/primitive_types6.rs | 4 +--- 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/exercises/primitive_types/primitive_types1.rs b/exercises/primitive_types/primitive_types1.rs index 091213925c..91734c6914 100644 --- a/exercises/primitive_types/primitive_types1.rs +++ b/exercises/primitive_types/primitive_types1.rs @@ -2,8 +2,6 @@ // Fill in the rest of the line that has code missing! // No hints, there's no tricks, just get used to typing these :) -// I AM NOT DONE - fn main() { // Booleans (`bool`) @@ -12,7 +10,7 @@ fn main() { println!("Good morning!"); } - let // Finish the rest of this line like the example! Or make it be false! + let is_evening = false; // Finish the rest of this line like the example! Or make it be false! if is_evening { println!("Good evening!"); } diff --git a/exercises/primitive_types/primitive_types2.rs b/exercises/primitive_types/primitive_types2.rs index 8730baab43..2f7512320c 100644 --- a/exercises/primitive_types/primitive_types2.rs +++ b/exercises/primitive_types/primitive_types2.rs @@ -2,14 +2,12 @@ // Fill in the rest of the line that has code missing! // No hints, there's no tricks, just get used to typing these :) -// I AM NOT DONE - fn main() { // Characters (`char`) // Note the _single_ quotes, these are different from the double quotes // you've been seeing around. - let my_first_initial = 'C'; + let my_first_initial = 'N'; if my_first_initial.is_alphabetic() { println!("Alphabetical!"); } else if my_first_initial.is_numeric() { @@ -18,12 +16,12 @@ fn main() { println!("Neither alphabetic nor numeric!"); } - let // Finish this line like the example! What's your favorite character? + let my_favorite_character = 'πŸ”₯'; // Finish this line like the example! What's your favorite character? // Try a letter, try a number, try a special character, try a character // from a different language than your own, try an emoji! - if your_character.is_alphabetic() { + if my_favorite_character.is_alphabetic() { println!("Alphabetical!"); - } else if your_character.is_numeric() { + } else if my_favorite_character.is_numeric() { println!("Numerical!"); } else { println!("Neither alphabetic nor numeric!"); diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs index fa7d019a44..62ae7149ec 100644 --- a/exercises/primitive_types/primitive_types3.rs +++ b/exercises/primitive_types/primitive_types3.rs @@ -2,10 +2,10 @@ // 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. -// I AM NOT DONE - fn main() { - let a = ??? + let a = ["Nidhal Messaoudi, A Rust professional!"; 100]; + + println!("{}", a.len()); if a.len() >= 100 { println!("Wow, that's a big array!"); diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs index 71fa243cfb..e337178484 100644 --- a/exercises/primitive_types/primitive_types4.rs +++ b/exercises/primitive_types/primitive_types4.rs @@ -2,13 +2,11 @@ // 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. -// I AM NOT DONE - #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; - let nice_slice = ??? + let nice_slice = &a[1..4]; assert_eq!([2, 3, 4], nice_slice) } diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs index 4fd9141fe6..5cba3c15e2 100644 --- a/exercises/primitive_types/primitive_types5.rs +++ b/exercises/primitive_types/primitive_types5.rs @@ -2,11 +2,9 @@ // Destructure the `cat` tuple so that the println will work. // Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { let cat = ("Furry McFurson", 3.5); - let /* your pattern here */ = cat; + let (name, age) = cat; println!("{} is {} years old.", name, age); } diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs index ddf8b42351..695d2815c2 100644 --- a/exercises/primitive_types/primitive_types6.rs +++ b/exercises/primitive_types/primitive_types6.rs @@ -3,13 +3,11 @@ // 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. -// I AM NOT DONE - #[test] fn indexing_tuple() { let numbers = (1, 2, 3); // Replace below ??? with the tuple indexing syntax. - let second = ???; + let second = numbers.1; assert_eq!(2, second, "This is not the 2nd number in the tuple!") From e3a20b8bc899eae9dfb440b885836812fa8614ee Mon Sep 17 00:00:00 2001 From: Nidhal Messaoudi Date: Mon, 27 Feb 2023 21:16:40 +0100 Subject: [PATCH 0171/1432] Learning about vectors --- exercises/move_semantics/move_semantics1.rs | 4 +--- exercises/move_semantics/move_semantics2.rs | 10 ++++------ exercises/move_semantics/move_semantics3.rs | 4 +--- exercises/vecs/vecs1.rs | 4 +--- exercises/vecs/vecs2.rs | 6 ++---- 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index aac6dfc39c..c60ee74f3f 100644 --- a/exercises/move_semantics/move_semantics1.rs +++ b/exercises/move_semantics/move_semantics1.rs @@ -1,12 +1,10 @@ // move_semantics1.rs // Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { let vec0 = Vec::new(); - let vec1 = fill_vec(vec0); + let mut vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 64870850ec..229f13bd00 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -2,12 +2,10 @@ // Make me compile without changing line 13 or moving line 10! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { - let vec0 = Vec::new(); + let mut vec0 = Vec::new(); - let mut vec1 = fill_vec(vec0); + let mut vec1 = fill_vec(&mut vec0); // Do not change the following line! println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0); @@ -17,12 +15,12 @@ fn main() { println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } -fn fill_vec(vec: Vec) -> Vec { +fn fill_vec(vec: &mut Vec) -> Vec { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); - vec + vec.to_vec() } diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index eaa30e3344..bb830234c4 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -3,8 +3,6 @@ // (no lines with multiple semicolons necessary!) // Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn main() { let vec0 = Vec::new(); @@ -17,7 +15,7 @@ fn main() { println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } -fn fill_vec(vec: Vec) -> Vec { +fn fill_vec(mut vec: Vec) -> Vec { vec.push(22); vec.push(44); vec.push(66); diff --git a/exercises/vecs/vecs1.rs b/exercises/vecs/vecs1.rs index 4e8c4cbbb7..0e8e2f7317 100644 --- a/exercises/vecs/vecs1.rs +++ b/exercises/vecs/vecs1.rs @@ -4,11 +4,9 @@ // Make me compile and pass the test! // Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn array_and_vec() -> ([i32; 4], Vec) { let a = [10, 20, 30, 40]; // a plain array - let v = // TODO: declare your vector here with the macro for vectors + let v = vec![10, 20, 30, 40]; // TODO: declare your vector here with the macro for vectors (a, v) } diff --git a/exercises/vecs/vecs2.rs b/exercises/vecs/vecs2.rs index 5bea09a2c2..d016daaf37 100644 --- a/exercises/vecs/vecs2.rs +++ b/exercises/vecs/vecs2.rs @@ -6,13 +6,11 @@ // // Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn vec_loop(mut v: Vec) -> Vec { for i in v.iter_mut() { // TODO: Fill this up so that each element in the Vec `v` is // multiplied by 2. - ??? + *i *= 2; } // At this point, `v` should be equal to [4, 8, 12, 16, 20]. @@ -23,7 +21,7 @@ fn vec_map(v: &Vec) -> Vec { v.iter().map(|num| { // TODO: Do the same thing as above - but instead of mutating the // Vec, you can just return the new number! - ??? + num * 2 }).collect() } From 1acbbb6d430d69720e0f9370d4649de92e510a31 Mon Sep 17 00:00:00 2001 From: Nidhal Messaoudi Date: Mon, 27 Feb 2023 21:17:45 +0100 Subject: [PATCH 0172/1432] Fixing the progress percentage --- src/verify.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/verify.rs b/src/verify.rs index cf319e473c..68ba6cef05 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -13,14 +13,15 @@ pub fn verify<'a>( progress: (usize, usize), verbose: bool, ) -> Result<(), &'a Exercise> { - let (mut num_done, total) = progress; + let (num_done, total) = progress; let bar = ProgressBar::new(total as u64); + let mut percentage = num_done as f32 / total as f32 * 100.0; bar.set_style(ProgressStyle::default_bar() .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") .progress_chars("#>-") ); bar.set_position(num_done as u64); - bar.set_message(format!("({:.1} %)", 0.)); + bar.set_message(format!("({:.1} %)", percentage)); for exercise in exercises { let compile_result = match exercise.mode { @@ -31,8 +32,7 @@ pub fn verify<'a>( if !compile_result.unwrap_or(false) { return Err(exercise); } - num_done += 1; - let percentage = num_done as f32 / total as f32 * 100.0; + percentage += 100.0 / total as f32; bar.inc(1); bar.set_message(format!("({:.1} %)", percentage)); } From 278a1f103b7ba694ff448fe4abe663411b7f6c92 Mon Sep 17 00:00:00 2001 From: Nidhal Messaoudi Date: Mon, 27 Feb 2023 21:33:28 +0100 Subject: [PATCH 0173/1432] Original exercises --- exercises/functions/functions1.rs | 6 ++---- exercises/functions/functions2.rs | 4 +++- exercises/functions/functions3.rs | 4 +++- exercises/functions/functions4.rs | 4 +++- exercises/functions/functions5.rs | 4 +++- exercises/if/if1.rs | 7 ++----- exercises/if/if2.rs | 8 ++++---- exercises/intro/intro1.rs | 2 ++ exercises/intro/intro2.rs | 4 +++- exercises/move_semantics/move_semantics1.rs | 4 +++- exercises/move_semantics/move_semantics2.rs | 10 ++++++---- exercises/move_semantics/move_semantics3.rs | 4 +++- exercises/primitive_types/primitive_types1.rs | 4 +++- exercises/primitive_types/primitive_types2.rs | 10 ++++++---- exercises/primitive_types/primitive_types3.rs | 6 +++--- exercises/primitive_types/primitive_types4.rs | 4 +++- exercises/primitive_types/primitive_types5.rs | 4 +++- exercises/primitive_types/primitive_types6.rs | 4 +++- exercises/quiz1.rs | 16 +++------------- exercises/variables/variables1.rs | 4 +++- exercises/variables/variables2.rs | 4 +++- exercises/variables/variables3.rs | 4 +++- exercises/variables/variables4.rs | 4 +++- exercises/variables/variables5.rs | 4 +++- exercises/variables/variables6.rs | 4 +++- exercises/vecs/vecs1.rs | 4 +++- exercises/vecs/vecs2.rs | 6 ++++-- 27 files changed, 86 insertions(+), 57 deletions(-) diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 38c353891d..03d8af7020 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,10 +1,8 @@ // functions1.rs // Execute `rustlings hint functions1` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { call_me(); } - -fn call_me() { - println!("I'm Nidhal Messaoudi, a software developer going Rusty!!!"); -} diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs index 5a51bdfbe4..7d40a578c7 100644 --- a/exercises/functions/functions2.rs +++ b/exercises/functions/functions2.rs @@ -1,11 +1,13 @@ // functions2.rs // Execute `rustlings hint functions2` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { call_me(3); } -fn call_me(num: i32) { +fn call_me(num:) { for i in 0..num { println!("Ring! Call number {}", i + 1); } diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs index 5b2a9a1ab3..3b9e585b6c 100644 --- a/exercises/functions/functions3.rs +++ b/exercises/functions/functions3.rs @@ -1,8 +1,10 @@ // functions3.rs // Execute `rustlings hint functions3` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { - call_me(12); + call_me(); } fn call_me(num: u32) { diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs index 40677fc94f..65d5be4ff6 100644 --- a/exercises/functions/functions4.rs +++ b/exercises/functions/functions4.rs @@ -7,12 +7,14 @@ // in the signatures for now. If anything, this is a good way to peek ahead // to future exercises!) +// I AM NOT DONE + fn main() { let original_price = 51; println!("Your sale price is {}", sale_price(original_price)); } -fn sale_price(price: i32) -> i32 { +fn sale_price(price: i32) -> { if is_even(price) { price - 10 } else { diff --git a/exercises/functions/functions5.rs b/exercises/functions/functions5.rs index 52b8400f6b..5d7629617f 100644 --- a/exercises/functions/functions5.rs +++ b/exercises/functions/functions5.rs @@ -1,11 +1,13 @@ // functions5.rs // Execute `rustlings hint functions5` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { let answer = square(3); println!("The square of 3 is {}", answer); } fn square(num: i32) -> i32 { - return num * num; + num * num; } diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index 660a093c73..587e03f88a 100644 --- a/exercises/if/if1.rs +++ b/exercises/if/if1.rs @@ -1,16 +1,13 @@ // if1.rs // Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! // Do not use: // - another function call // - additional variables - if a > b { - a - } else { - b - } } // Don't mind this for now :) diff --git a/exercises/if/if2.rs b/exercises/if/if2.rs index 1fca9c3b34..effddbb6eb 100644 --- a/exercises/if/if2.rs +++ b/exercises/if/if2.rs @@ -4,13 +4,13 @@ // 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. +// I AM NOT DONE + pub fn foo_if_fizz(fizzish: &str) -> &str { - if fizzish == "fuzz" { - "bar" - } else if fizzish == "fizz" { + if fizzish == "fizz" { "foo" } else { - "baz" + 1 } } diff --git a/exercises/intro/intro1.rs b/exercises/intro/intro1.rs index 5488977887..cfc55c306f 100644 --- a/exercises/intro/intro1.rs +++ b/exercises/intro/intro1.rs @@ -9,6 +9,8 @@ // 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! +// I AM NOT DONE + fn main() { println!("Hello and"); println!(r#" welcome to... "#); diff --git a/exercises/intro/intro2.rs b/exercises/intro/intro2.rs index b5b1d0a804..efc1af2059 100644 --- a/exercises/intro/intro2.rs +++ b/exercises/intro/intro2.rs @@ -2,6 +2,8 @@ // Make the code print a greeting to the world. // Execute `rustlings hint intro2` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { - println!("Hello {}!", "World"); + println!("Hello {}!"); } diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index c60ee74f3f..aac6dfc39c 100644 --- a/exercises/move_semantics/move_semantics1.rs +++ b/exercises/move_semantics/move_semantics1.rs @@ -1,10 +1,12 @@ // move_semantics1.rs // Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { let vec0 = Vec::new(); - let mut vec1 = fill_vec(vec0); + let vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 229f13bd00..64870850ec 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -2,10 +2,12 @@ // Make me compile without changing line 13 or moving line 10! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { - let mut vec0 = Vec::new(); + let vec0 = Vec::new(); - let mut vec1 = fill_vec(&mut vec0); + let mut vec1 = fill_vec(vec0); // Do not change the following line! println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0); @@ -15,12 +17,12 @@ fn main() { println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } -fn fill_vec(vec: &mut Vec) -> Vec { +fn fill_vec(vec: Vec) -> Vec { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); - vec.to_vec() + vec } diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index bb830234c4..eaa30e3344 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -3,6 +3,8 @@ // (no lines with multiple semicolons necessary!) // Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { let vec0 = Vec::new(); @@ -15,7 +17,7 @@ fn main() { println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } -fn fill_vec(mut vec: Vec) -> Vec { +fn fill_vec(vec: Vec) -> Vec { vec.push(22); vec.push(44); vec.push(66); diff --git a/exercises/primitive_types/primitive_types1.rs b/exercises/primitive_types/primitive_types1.rs index 91734c6914..091213925c 100644 --- a/exercises/primitive_types/primitive_types1.rs +++ b/exercises/primitive_types/primitive_types1.rs @@ -2,6 +2,8 @@ // Fill in the rest of the line that has code missing! // No hints, there's no tricks, just get used to typing these :) +// I AM NOT DONE + fn main() { // Booleans (`bool`) @@ -10,7 +12,7 @@ fn main() { println!("Good morning!"); } - let is_evening = false; // Finish the rest of this line like the example! Or make it be false! + let // Finish the rest of this line like the example! Or make it be false! if is_evening { println!("Good evening!"); } diff --git a/exercises/primitive_types/primitive_types2.rs b/exercises/primitive_types/primitive_types2.rs index 2f7512320c..8730baab43 100644 --- a/exercises/primitive_types/primitive_types2.rs +++ b/exercises/primitive_types/primitive_types2.rs @@ -2,12 +2,14 @@ // Fill in the rest of the line that has code missing! // No hints, there's no tricks, just get used to typing these :) +// I AM NOT DONE + fn main() { // Characters (`char`) // Note the _single_ quotes, these are different from the double quotes // you've been seeing around. - let my_first_initial = 'N'; + let my_first_initial = 'C'; if my_first_initial.is_alphabetic() { println!("Alphabetical!"); } else if my_first_initial.is_numeric() { @@ -16,12 +18,12 @@ fn main() { println!("Neither alphabetic nor numeric!"); } - let my_favorite_character = 'πŸ”₯'; // Finish this line like the example! What's your favorite character? + let // Finish this line like the example! What's your favorite character? // Try a letter, try a number, try a special character, try a character // from a different language than your own, try an emoji! - if my_favorite_character.is_alphabetic() { + if your_character.is_alphabetic() { println!("Alphabetical!"); - } else if my_favorite_character.is_numeric() { + } else if your_character.is_numeric() { println!("Numerical!"); } else { println!("Neither alphabetic nor numeric!"); diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs index 62ae7149ec..fa7d019a44 100644 --- a/exercises/primitive_types/primitive_types3.rs +++ b/exercises/primitive_types/primitive_types3.rs @@ -2,10 +2,10 @@ // 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 = ["Nidhal Messaoudi, A Rust professional!"; 100]; +// I AM NOT DONE - println!("{}", a.len()); +fn main() { + let a = ??? if a.len() >= 100 { println!("Wow, that's a big array!"); diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs index e337178484..71fa243cfb 100644 --- a/exercises/primitive_types/primitive_types4.rs +++ b/exercises/primitive_types/primitive_types4.rs @@ -2,11 +2,13 @@ // 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. +// I AM NOT DONE + #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; - let nice_slice = &a[1..4]; + let nice_slice = ??? assert_eq!([2, 3, 4], nice_slice) } diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs index 5cba3c15e2..4fd9141fe6 100644 --- a/exercises/primitive_types/primitive_types5.rs +++ b/exercises/primitive_types/primitive_types5.rs @@ -2,9 +2,11 @@ // Destructure the `cat` tuple so that the println will work. // Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { let cat = ("Furry McFurson", 3.5); - let (name, age) = cat; + let /* your pattern here */ = cat; println!("{} is {} years old.", name, age); } diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs index 695d2815c2..ddf8b42351 100644 --- a/exercises/primitive_types/primitive_types6.rs +++ b/exercises/primitive_types/primitive_types6.rs @@ -3,11 +3,13 @@ // 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. +// I AM NOT DONE + #[test] fn indexing_tuple() { let numbers = (1, 2, 3); // Replace below ??? with the tuple indexing syntax. - let second = numbers.1; + let second = ???; assert_eq!(2, second, "This is not the 2nd number in the tuple!") diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 9988f9de8f..dbb5cdc9a1 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -10,20 +10,10 @@ // 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(quantity: i32) -> i32 { - let mut quantity= quantity; - if quantity <= 40 { - quantity = quantity * 2; - } - return quantity; +// I AM NOT DONE - // if quantity > 40 { - // quantity - // } else { - // quantity * 2 - // } -} +// Put your function here! +// fn calculate_price_of_apples { // Don't modify this function! #[test] diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs index 84de9fdcfa..f4d182accf 100644 --- a/exercises/variables/variables1.rs +++ b/exercises/variables/variables1.rs @@ -2,7 +2,9 @@ // Make me compile! // Execute `rustlings hint variables1` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { - let x = 5; + x = 5; println!("x has the value {}", x); } diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs index 6715cf5ce0..641aeb8e09 100644 --- a/exercises/variables/variables2.rs +++ b/exercises/variables/variables2.rs @@ -1,8 +1,10 @@ // variables2.rs // Execute `rustlings hint variables2` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { - let x = 12; + let x; if x == 10 { println!("x is ten!"); } else { diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs index 30afbf1409..819b1bc791 100644 --- a/exercises/variables/variables3.rs +++ b/exercises/variables/variables3.rs @@ -1,7 +1,9 @@ // variables3.rs // Execute `rustlings hint variables3` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { - let x: i32 = 12; + let x: i32; println!("Number {}", x); } diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs index 8c2ddd6dd4..54491b0a20 100644 --- a/exercises/variables/variables4.rs +++ b/exercises/variables/variables4.rs @@ -1,8 +1,10 @@ // variables4.rs // Execute `rustlings hint variables4` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { - let mut x = 3; + let x = 3; println!("Number {}", x); x = 5; // don't change this line println!("Number {}", x); diff --git a/exercises/variables/variables5.rs b/exercises/variables/variables5.rs index 1b9d9f487c..0e670d2afe 100644 --- a/exercises/variables/variables5.rs +++ b/exercises/variables/variables5.rs @@ -1,9 +1,11 @@ // variables5.rs // Execute `rustlings hint variables5` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn main() { let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); - let number = 3; // don't rename this variable + number = 3; // don't rename this variable println!("Number plus two is : {}", number + 2); } diff --git a/exercises/variables/variables6.rs b/exercises/variables/variables6.rs index e8314b28ae..a8520122c5 100644 --- a/exercises/variables/variables6.rs +++ b/exercises/variables/variables6.rs @@ -1,7 +1,9 @@ // variables6.rs // Execute `rustlings hint variables6` or use the `hint` watch subcommand for a hint. -const NUMBER: i32 = 3; +// I AM NOT DONE + +const NUMBER = 3; fn main() { println!("Number {}", NUMBER); } diff --git a/exercises/vecs/vecs1.rs b/exercises/vecs/vecs1.rs index 0e8e2f7317..4e8c4cbbb7 100644 --- a/exercises/vecs/vecs1.rs +++ b/exercises/vecs/vecs1.rs @@ -4,9 +4,11 @@ // Make me compile and pass the test! // Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn array_and_vec() -> ([i32; 4], Vec) { let a = [10, 20, 30, 40]; // a plain array - let v = vec![10, 20, 30, 40]; // TODO: declare your vector here with the macro for vectors + let v = // TODO: declare your vector here with the macro for vectors (a, v) } diff --git a/exercises/vecs/vecs2.rs b/exercises/vecs/vecs2.rs index d016daaf37..5bea09a2c2 100644 --- a/exercises/vecs/vecs2.rs +++ b/exercises/vecs/vecs2.rs @@ -6,11 +6,13 @@ // // Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. +// I AM NOT DONE + fn vec_loop(mut v: Vec) -> Vec { for i in v.iter_mut() { // TODO: Fill this up so that each element in the Vec `v` is // multiplied by 2. - *i *= 2; + ??? } // At this point, `v` should be equal to [4, 8, 12, 16, 20]. @@ -21,7 +23,7 @@ fn vec_map(v: &Vec) -> Vec { v.iter().map(|num| { // TODO: Do the same thing as above - but instead of mutating the // Vec, you can just return the new number! - num * 2 + ??? }).collect() } From 34aafa82f9b8266c6ea2a80f2cee03615a2c5297 Mon Sep 17 00:00:00 2001 From: Nidhal Messaoudi Date: Mon, 27 Feb 2023 21:36:51 +0100 Subject: [PATCH 0174/1432] Main exercises --- exercises/error_handling/errors5.rs | 2 +- exercises/iterators/iterators1.rs | 2 +- exercises/macros/macros4.rs | 1 + exercises/smart_pointers/arc1.rs | 2 +- exercises/smart_pointers/cow1.rs | 66 ++++++++++++++++++++--------- exercises/vecs/README.md | 2 + 6 files changed, 52 insertions(+), 23 deletions(-) diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs index 6da06ef371..eb5506cb5f 100644 --- a/exercises/error_handling/errors5.rs +++ b/exercises/error_handling/errors5.rs @@ -4,7 +4,7 @@ // This exercise uses some concepts that we won't get to until later in the course, like `Box` and the // `From` trait. It's not important to understand them in detail right now, but you can read ahead if you like. -// For now, think of the `Box` type as an "I want anything that does ???" type, which, given +// For now, think of the `Box` type as an "I want anything that does ???" type, which, given // Rust's usual standards for runtime safety, should strike you as somewhat lenient! // In short, this particular use case for boxes is for when you want to own a value and you care only that it is a diff --git a/exercises/iterators/iterators1.rs b/exercises/iterators/iterators1.rs index 0379c6bb41..f9cc3b399a 100644 --- a/exercises/iterators/iterators1.rs +++ b/exercises/iterators/iterators1.rs @@ -10,7 +10,7 @@ // I AM NOT DONE -fn main () { +fn main() { let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; let mut my_iterable_fav_fruits = ???; // TODO: Step 1 diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index c1fc5e8b16..4ee9803550 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -3,6 +3,7 @@ // I AM NOT DONE +#[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/smart_pointers/arc1.rs b/exercises/smart_pointers/arc1.rs index 93a27036f2..ffb306afb3 100644 --- a/exercises/smart_pointers/arc1.rs +++ b/exercises/smart_pointers/arc1.rs @@ -32,7 +32,7 @@ fn main() { for offset in 0..8 { let child_numbers = // TODO joinhandles.push(thread::spawn(move || { - let sum: u32 = child_numbers.iter().filter(|n| *n % 8 == offset).sum(); + let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); println!("Sum of offset {} is {}", offset, sum); })); } diff --git a/exercises/smart_pointers/cow1.rs b/exercises/smart_pointers/cow1.rs index 5fba2519d3..885138a740 100644 --- a/exercises/smart_pointers/cow1.rs +++ b/exercises/smart_pointers/cow1.rs @@ -4,6 +4,9 @@ // 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 required. // The type is designed to work with general borrowed data via the Borrow trait. +// +// 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. // I AM NOT DONE @@ -20,29 +23,52 @@ fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { input } -fn main() { - // No clone occurs because `input` doesn't need to be mutated. - let slice = [0, 1, 2]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Borrowed(_) => println!("I borrowed the slice!"), - _ => panic!("expected borrowed value"), +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reference_mutation() -> Result<(), &'static str> { + // Clone occurs because `input` needs to be mutated. + let slice = [-1, 0, 1]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + Cow::Owned(_) => Ok(()), + _ => Err("Expected owned value"), + } } - // Clone occurs because `input` needs to be mutated. - let slice = [-1, 0, 1]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Owned(_) => println!("I modified the slice and now own it!"), - _ => panic!("expected owned value"), + #[test] + fn reference_no_mutation() -> Result<(), &'static str> { + // No clone occurs because `input` doesn't need to be mutated. + let slice = [0, 1, 2]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + // TODO + } } - // No clone occurs because `input` is already owned. - let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - Cow::Borrowed(_) => println!("I own this slice!"), - _ => panic!("expected borrowed value"), + #[test] + fn owned_no_mutation() -> Result<(), &'static str> { + // We can also pass `slice` without `&` so Cow owns it directly. + // In this case no mutation occurs and thus also no clone, + // but the result is still owned because it always was. + let slice = vec![0, 1, 2]; + let mut input = Cow::from(slice); + match abs_all(&mut input) { + // TODO + } + } + + #[test] + fn owned_mutation() -> Result<(), &'static str> { + // Of course this is also the case if a mutation does occur. + // In this case the call to `to_mut()` returns a reference to + // the same data as before. + let slice = vec![-1, 0, 1]; + let mut input = Cow::from(slice); + match abs_all(&mut input) { + // TODO + } } } diff --git a/exercises/vecs/README.md b/exercises/vecs/README.md index ebe90bf310..8ff9b85f52 100644 --- a/exercises/vecs/README.md +++ b/exercises/vecs/README.md @@ -13,3 +13,5 @@ the other useful data structure, hash maps, later. ## Further information - [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) +- [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut) +- [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) From ff50159a3d3fb98f40f004eb2cce106ae90cfe72 Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 3 Mar 2023 21:27:57 +0100 Subject: [PATCH 0175/1432] fix: remove repl.it support for now It stopped working and needs too much manual fixing, until someone comes along to fix it, it's not great to have in the README section. Closes #1352. --- .replit | 2 -- README.md | 2 -- 2 files changed, 4 deletions(-) delete mode 100644 .replit diff --git a/.replit b/.replit deleted file mode 100644 index 8462a6fca8..0000000000 --- a/.replit +++ /dev/null @@ -1,2 +0,0 @@ -language = "rust" -run = "[ -x ~/.cargo/bin/rustlings ] && ~/.cargo/bin/rustlings watch || ./install.sh" diff --git a/README.md b/README.md index e78d1a3715..571ba9547a 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,6 @@ If you get a permission denied message, you might have to exclude the directory ## Browser -[Run on Repl.it](https://repl.it/github/rust-lang/rustlings) - [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) [![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=main) From 3da5816db27f76e9fea4bd91e2949f036c6d830c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 10:51:23 +0000 Subject: [PATCH 0176/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 02404e8082..2452a83a08 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -267,6 +267,7 @@ authors. Gleb Shevchenko
Gleb Shevchenko

πŸ–‹ Edmundo Paulino
Edmundo Paulino

πŸš‡ Emmanuel Roullit
Emmanuel Roullit

πŸš‡ + Nidhal Messaoudi
Nidhal Messaoudi

πŸ’» From d334e0c66caf9c28f47b407579bb450f62cff9a0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 10:51:24 +0000 Subject: [PATCH 0177/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9dfc8defb0..81d80ae0db 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1884,6 +1884,15 @@ "contributions": [ "infra" ] + }, + { + "login": "nidhalmessaoudi", + "name": "Nidhal Messaoudi", + "avatar_url": "https://avatars.githubusercontent.com/u/63377412?v=4", + "profile": "https://nidhalmessaoudi.herokuapp.com", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 7b2bfda37f50f0c2807d4b1c588fa5d69a34e10a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 16:28:12 +0000 Subject: [PATCH 0178/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 2452a83a08..54157fcdb0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -268,6 +268,7 @@ authors. Edmundo Paulino
Edmundo Paulino

πŸš‡ Emmanuel Roullit
Emmanuel Roullit

πŸš‡ Nidhal Messaoudi
Nidhal Messaoudi

πŸ’» + Mahdi Bahrami
Mahdi Bahrami

πŸ”§ From ec974f5dac75466490ca0884d4b862686ba58ea9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 16:28:13 +0000 Subject: [PATCH 0179/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 81d80ae0db..2b0ffeaf3a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1893,6 +1893,15 @@ "contributions": [ "code" ] + }, + { + "login": "MahdiBM", + "name": "Mahdi Bahrami", + "avatar_url": "https://avatars.githubusercontent.com/u/54685446?v=4", + "profile": "https://github.com/MahdiBM", + "contributions": [ + "tool" + ] } ], "contributorsPerLine": 8, From 7f1754ecc5690f31db4aff38eef2bf30e2aec525 Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 10 Mar 2023 17:39:59 +0100 Subject: [PATCH 0180/1432] release: 5.4.1 --- CHANGELOG.md | 16 ++++++++++++++++ Cargo.toml | 2 +- README.md | 8 ++++---- flake.nix | 2 +- src/main.rs | 2 +- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63e448b9e8..2c14e7d793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +## 5.4.1 (2023-03-10) + +#### Changed + +- `vecs`: Added links to `iter_mut` and `map` to README.md +- `cow1`: Changed main to tests +- `iterators1`: Formatted according to rustfmt + +#### Fixed + +- `errors5`: Unified undisclosed type notation +- `arc1`: Improved readability by avoiding implicit dereference +- `macros4`: Prevented auto-fix by adding `#[rustfmt::skip]` +- `cli`: Actually show correct progress percentages + ## 5.4.0 (2023-02-12) diff --git a/Cargo.toml b/Cargo.toml index 4b4505845f..d22816cab7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustlings" -version = "5.4.0" +version = "5.4.1" authors = [ "Liv ", "Carol (Nichols || Goulding) ", diff --git a/README.md b/README.md index 571ba9547a..d16b73858e 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.0) -git clone -b 5.4.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1) +git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings # if nix version > 2.3 nix develop @@ -71,8 +71,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.0) -git clone -b 5.4.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1) +git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/flake.nix b/flake.nix index 5d485801e7..5815a92044 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; - version = "5.4.0"; + version = "5.4.1"; buildInputs = cargoBuildInputs; diff --git a/src/main.rs b/src/main.rs index c09088ba5b..312a0a127d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ mod run; mod verify; // In sync with crate version -const VERSION: &str = "5.4.0"; +const VERSION: &str = "5.4.1"; #[derive(FromArgs, PartialEq, Debug)] /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code From ec86b60e704520289acac180cc55058431ca2bdb Mon Sep 17 00:00:00 2001 From: Sven Siegmund Date: Fri, 10 Mar 2023 17:58:03 +0100 Subject: [PATCH 0181/1432] improve hint for strings2, #1407 --- info.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 2f42403378..731db504e6 100644 --- a/info.toml +++ b/info.toml @@ -439,7 +439,9 @@ mode = "compile" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way to add one character to line -9, though, that will coerce the `String` into a string slice.""" +9, though, that will coerce the `String` into a string slice. + +Side note: If you're interested in learning about how this kind of reference conversion works, you can jump ahead in the book and read this part in the smart pointers chapter: https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" [[exercises]] name = "strings3" From 31b208248bd47069ba592b575804c37c763be6bb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 17:03:20 +0000 Subject: [PATCH 0182/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 54157fcdb0..312dde0522 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -270,6 +270,9 @@ authors. Nidhal Messaoudi
Nidhal Messaoudi

πŸ’» Mahdi Bahrami
Mahdi Bahrami

πŸ”§ + + Nagidal
Nagidal

πŸ–‹ + From 53a8318f32037c340d96488b46b6620d4ffbcd7f Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 17:03:21 +0000 Subject: [PATCH 0183/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2b0ffeaf3a..0c74225f93 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1902,6 +1902,15 @@ "contributions": [ "tool" ] + }, + { + "login": "Nagidal", + "name": "Nagidal", + "avatar_url": "https://avatars.githubusercontent.com/u/7075397?v=4", + "profile": "https://github.com/Nagidal", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 7bab78c66d4c0fff4b9f7ca082964353215265e3 Mon Sep 17 00:00:00 2001 From: Adam Brewer Date: Fri, 10 Mar 2023 14:13:06 -0500 Subject: [PATCH 0184/1432] Rename iteration var names in vec2.rs for clarity Resolves #1417 --- exercises/vecs/vecs2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/vecs/vecs2.rs b/exercises/vecs/vecs2.rs index 5bea09a2c2..1ea26071d4 100644 --- a/exercises/vecs/vecs2.rs +++ b/exercises/vecs/vecs2.rs @@ -9,7 +9,7 @@ // I AM NOT DONE fn vec_loop(mut v: Vec) -> Vec { - for i in v.iter_mut() { + for element in v.iter_mut() { // TODO: Fill this up so that each element in the Vec `v` is // multiplied by 2. ??? @@ -20,7 +20,7 @@ fn vec_loop(mut v: Vec) -> Vec { } fn vec_map(v: &Vec) -> Vec { - v.iter().map(|num| { + v.iter().map(|element| { // TODO: Do the same thing as above - but instead of mutating the // Vec, you can just return the new number! ??? From e8b44a39e023d83df0b1ba449f58f78361ef6847 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:15:21 +0000 Subject: [PATCH 0185/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 312dde0522..7bf26c79eb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -272,6 +272,7 @@ authors. Nagidal
Nagidal

πŸ–‹ + Adam Brewer
Adam Brewer

πŸ–‹ From 06fac0e5707071b573aaf6a465448d2e9d81dc54 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:15:22 +0000 Subject: [PATCH 0186/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0c74225f93..4d98014aa6 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1911,6 +1911,15 @@ "contributions": [ "content" ] + }, + { + "login": "adamhb123", + "name": "Adam Brewer", + "avatar_url": "https://avatars.githubusercontent.com/u/25161597?v=4", + "profile": "https://adabrew.com", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 5d91c86cf294e503f5580ea648f0d9140b895d7b Mon Sep 17 00:00:00 2001 From: hxztnxt Date: Thu, 16 Mar 2023 00:56:15 +0100 Subject: [PATCH 0187/1432] change order of references in README.md --- exercises/lifetimes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/lifetimes/README.md b/exercises/lifetimes/README.md index 72befb3eab..db1931a53e 100644 --- a/exercises/lifetimes/README.md +++ b/exercises/lifetimes/README.md @@ -13,5 +13,5 @@ restrictive of their callers. ## Further information -- [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) - [Lifetimes (in Rust By Example)](https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime.html) +- [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) From 568767601427cccf11ef8ea0dd20ad1ac0dbee1e Mon Sep 17 00:00:00 2001 From: Ryan Whitehouse Date: Mon, 20 Mar 2023 12:04:47 +0100 Subject: [PATCH 0188/1432] docs:clarify instructions on hashmaps2.rs --- exercises/hashmaps/hashmaps2.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/exercises/hashmaps/hashmaps2.rs b/exercises/hashmaps/hashmaps2.rs index 454b3e1d7b..6d21658966 100644 --- a/exercises/hashmaps/hashmaps2.rs +++ b/exercises/hashmaps/hashmaps2.rs @@ -1,11 +1,11 @@ // hashmaps2.rs - // A basket of fruits in the form of a hash map is given. 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 *MORE -// THAN 11* fruits in the basket. Three types of fruits - Apple (4), -// Mango (2) and Lychee (5) are already given in the basket. You are -// not allowed to insert any more of these fruits! +// of that particular fruit is in the basket. +// Three types of fruits - Apple (4), Mango (2) and Lychee (5) are already given in the basket. +// You must add fruit to the basket so that there is at least +// one of each kind and more than 11 in total. +// You are not allowed to insert any more of these fruits! // // Make me pass the tests! // @@ -34,8 +34,8 @@ fn fruit_basket(basket: &mut HashMap) { ]; for fruit in fruit_kinds { - // TODO: Put new fruits if not already present. Note that you - // are not allowed to put any type of fruit that's already + // TODO: Insert new fruits if they are not already present in the basket. + // Note that you are not allowed to put any type of fruit that's already // present! } } @@ -44,6 +44,7 @@ fn fruit_basket(basket: &mut HashMap) { mod tests { use super::*; + // Don't modify this function! fn get_fruit_basket() -> HashMap { let mut basket = HashMap::::new(); basket.insert(Fruit::Apple, 4); From 6e9ed48f42df78b83c1693e6f495d7a9f571a498 Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 24 Mar 2023 16:48:11 +0100 Subject: [PATCH 0189/1432] cargo update for to resolve #1430 --- Cargo.lock | 280 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 170 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d04953d90..192a4ac07b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] [[package]] name = "argh" -version = "0.1.5" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7317a549bc17c5278d9e72bb6e62c6aa801ac2567048e39ebc1c194249323e" +checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e" dependencies = [ "argh_derive", "argh_shared", @@ -23,22 +23,21 @@ dependencies = [ [[package]] name = "argh_derive" -version = "0.1.5" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60949c42375351e9442e354434b0cba2ac402c1237edf673cac3a4bf983b8d3c" +checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6" dependencies = [ "argh_shared", - "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "argh_shared" -version = "0.1.5" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a61eb019cb8f415d162cb9f12130ee6bbe9168b7d953c17f4ad049e4051ca00" +checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" [[package]] name = "assert_cmd" @@ -54,9 +53,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" @@ -78,17 +77,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "console" -version = "0.15.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "regex", - "terminal_size", "unicode-width", - "winapi 0.3.9", + "windows-sys 0.42.0", ] [[package]] @@ -117,14 +114,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "winapi 0.3.9", + "windows-sys 0.45.0", ] [[package]] @@ -173,24 +170,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "heck" -version = "0.3.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "home" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" dependencies = [ "winapi 0.3.9", ] @@ -238,9 +226,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "kernel32-sys" @@ -266,24 +254,24 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.100" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" @@ -330,9 +318,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.37" +version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" dependencies = [ "cfg-if 0.1.10", "libc", @@ -365,9 +353,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -378,12 +366,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" -[[package]] -name = "once_cell" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" - [[package]] name = "predicates" version = "1.0.8" @@ -399,52 +381,52 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.3" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7dd0fd014130206c9352efbdc92be592751b2b9274dff685348341082c6ea3d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", - "treeline", + "termtree", ] [[package]] name = "proc-macro2" -version = "1.0.28" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" dependencies = [ "aho-corasick", "memchr", @@ -453,13 +435,13 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustlings" -version = "5.4.0" +version = "5.4.1" dependencies = [ "argh", "assert_cmd", @@ -477,9 +459,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" @@ -492,29 +474,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.129" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.129" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -523,72 +505,69 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.4" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] [[package]] name = "syn" -version = "1.0.75" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] -name = "terminal_size" -version = "0.1.17" +name = "syn" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" dependencies = [ - "libc", - "winapi 0.3.9", + "proc-macro2", + "quote", + "unicode-ident", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] -name = "treeline" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" - -[[package]] -name = "unicode-segmentation" -version = "1.8.0" +name = "unicode-ident" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "unicode-xid" -version = "0.2.2" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi 0.3.9", "winapi-util", ] @@ -635,6 +614,87 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "ws2_32-sys" version = "0.2.1" From 4160b06c6ca5bdb1a363fdb130a3a3055b9d2eac Mon Sep 17 00:00:00 2001 From: Ed Sweeney Date: Fri, 24 Mar 2023 21:52:58 -0700 Subject: [PATCH 0190/1432] correct comments in errors2.rs --- exercises/error_handling/errors2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs index aad3a93f4b..76b8d12c31 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -2,7 +2,7 @@ // 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 the `total_cost` function will calculate the total number of tokens. +// and the `total_cost` function will calculate the total cost of the tokens. // Since the player typed in the quantity, though, we get it as a string-- and // they might have typed anything, not just numbers! From d5449c992e8e7633ff929737fb6df4daa5413166 Mon Sep 17 00:00:00 2001 From: Tom Kunc Date: Sat, 25 Mar 2023 20:00:28 +1100 Subject: [PATCH 0191/1432] feat(lifetimekata): Add info about Lifetimekata project --- exercises/lifetimes/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exercises/lifetimes/README.md b/exercises/lifetimes/README.md index 72befb3eab..dc7ca797d1 100644 --- a/exercises/lifetimes/README.md +++ b/exercises/lifetimes/README.md @@ -11,6 +11,11 @@ be referenced outside. Lifetimes mean that calling code of e.g. functions can be checked to make sure their arguments are valid. Lifetimes are restrictive of their callers. +If you'd like to learn more about lifetime annotations, the +[lifetimekata](https://tfpk.github.io/lifetimekata/) project +has a similar style of exercises to Rustlings, but is all about +learning to write lifetime annotations. + ## Further information - [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) From efd4d83731e1d7bde5a041908fb3a81d7b02dfae Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:48:09 +0000 Subject: [PATCH 0192/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 7bf26c79eb..9e5f1e865f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -273,6 +273,7 @@ authors. Nagidal
Nagidal

πŸ–‹ Adam Brewer
Adam Brewer

πŸ–‹ + Eugene
Eugene

πŸ”§ From 82f72c3ec6ea111a07f5dab9c0fa05528bb3068d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:48:10 +0000 Subject: [PATCH 0193/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4d98014aa6..ecb7436ebd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1920,6 +1920,15 @@ "contributions": [ "content" ] + }, + { + "login": "eugkhp", + "name": "Eugene", + "avatar_url": "https://avatars.githubusercontent.com/u/25910599?v=4", + "profile": "https://github.com/eugkhp", + "contributions": [ + "tool" + ] } ], "contributorsPerLine": 8, From c28b78fae56e8a9aa30d627039ade17dd58547bd Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:50:13 +0000 Subject: [PATCH 0194/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 9e5f1e865f..29fc14b9e9 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -274,6 +274,7 @@ authors. Nagidal
Nagidal

πŸ–‹ Adam Brewer
Adam Brewer

πŸ–‹ Eugene
Eugene

πŸ”§ + Ed Sweeney
Ed Sweeney

πŸ–‹ From 766b83b513afbfb0a5a5c6b481887ff7c81f8909 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:50:14 +0000 Subject: [PATCH 0195/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index ecb7436ebd..69aaf78a79 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1929,6 +1929,15 @@ "contributions": [ "tool" ] + }, + { + "login": "navicore", + "name": "Ed Sweeney", + "avatar_url": "https://avatars.githubusercontent.com/u/110999?v=4", + "profile": "https://social.linux.pizza/@navicore", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 864e741dd87eb8df192e6272ae4187bd95f61760 Mon Sep 17 00:00:00 2001 From: liv Date: Sun, 26 Mar 2023 15:21:21 +0200 Subject: [PATCH 0196/1432] reword hashmaps2 a bit more --- exercises/hashmaps/hashmaps2.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/exercises/hashmaps/hashmaps2.rs b/exercises/hashmaps/hashmaps2.rs index 6d21658966..a4f069a82b 100644 --- a/exercises/hashmaps/hashmaps2.rs +++ b/exercises/hashmaps/hashmaps2.rs @@ -1,10 +1,12 @@ // hashmaps2.rs -// A basket of fruits in the form of a hash map is given. The key -// represents the name of the fruit and the value represents how many -// of that particular fruit is in the basket. -// Three types of fruits - Apple (4), Mango (2) and Lychee (5) are already given in the basket. -// You must add fruit to the basket so that there is at least -// one of each kind and more than 11 in total. +// 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 many of that particular fruit we have collected. +// Three types of fruits - Apple (4), Mango (2) and Lychee (5) are already +// in the basket hash map. +// You must add fruit to the basket so that there is at least +// one of each kind and more than 11 in total - we have a lot of mouths to feed. // You are not allowed to insert any more of these fruits! // // Make me pass the tests! @@ -34,7 +36,7 @@ fn fruit_basket(basket: &mut HashMap) { ]; for fruit in fruit_kinds { - // TODO: Insert new fruits if they are not already present in the basket. + // TODO: Insert new fruits if they are not already present in the basket. // Note that you are not allowed to put any type of fruit that's already // present! } From 18a9510048e20a40507f616a0b2fcbc849881ef8 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 13:23:18 +0000 Subject: [PATCH 0197/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 29fc14b9e9..b300f3e396 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -275,6 +275,7 @@ authors. Adam Brewer
Adam Brewer

πŸ–‹ Eugene
Eugene

πŸ”§ Ed Sweeney
Ed Sweeney

πŸ–‹ + javihernant
javihernant

πŸ–‹ From faf4d9a888dc9ada907ff293e56fa0f3786584fc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 26 Mar 2023 13:23:19 +0000 Subject: [PATCH 0198/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 69aaf78a79..d86e9a2a59 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1938,6 +1938,15 @@ "contributions": [ "content" ] + }, + { + "login": "javihernant", + "name": "javihernant", + "avatar_url": "https://avatars.githubusercontent.com/u/73640929?v=4", + "profile": "https://github.com/javihernant", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 95e51f384ef4b600f86dc08fcb234f9715c9bc44 Mon Sep 17 00:00:00 2001 From: Vegard Matthey Date: Sun, 26 Mar 2023 23:25:59 +0200 Subject: [PATCH 0199/1432] fixed a spelling mistake in info.toml --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 731db504e6..fde77e232c 100644 --- a/info.toml +++ b/info.toml @@ -844,7 +844,7 @@ Create an iterator from the slice. Transform the iterated values by applying the `capitalize_first` function. Remember to collect the iterator. Step 3. -This is surprising similar to the previous solution. Collect is very powerful +This is surprisingly similar to the previous solution. Collect is very powerful and very general. Rust just needs to know the desired type.""" [[exercises]] From db993899d3120d85e3b6e29ff6bc3eb67d1d4718 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:30:56 +0000 Subject: [PATCH 0200/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index b300f3e396..2561f744bf 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -276,6 +276,7 @@ authors. Eugene
Eugene

πŸ”§ Ed Sweeney
Ed Sweeney

πŸ–‹ javihernant
javihernant

πŸ–‹ + Vegard
Vegard

πŸ–‹ From 5e43c7aba503f71b6fc3afec2c37a7f0a2946376 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:30:57 +0000 Subject: [PATCH 0201/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d86e9a2a59..1a218e5f54 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1947,6 +1947,15 @@ "contributions": [ "content" ] + }, + { + "login": "VegardMatthey", + "name": "Vegard", + "avatar_url": "https://avatars.githubusercontent.com/u/59250656?v=4", + "profile": "https://github.com/VegardMatthey", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 592694036f7329b90712ea066f5f79d2396c6f50 Mon Sep 17 00:00:00 2001 From: Ali Afsharzadeh Date: Tue, 28 Mar 2023 09:58:59 +0330 Subject: [PATCH 0202/1432] docs(variables): wrap mut keyword with backtick --- exercises/variables/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/variables/README.md b/exercises/variables/README.md index 11a7a78ab8..7964ff29f4 100644 --- a/exercises/variables/README.md +++ b/exercises/variables/README.md @@ -2,7 +2,7 @@ In Rust, variables are immutable by default. When a variable is immutable, once a value is bound to a name, you can’t change that value. -You can make them mutable by adding mut in front of the variable name. +You can make them mutable by adding `mut` in front of the variable name. ## Further information From 13356e16a3994ead1d399cd879797a8f05d258aa Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:25:46 +0000 Subject: [PATCH 0203/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 2561f744bf..4a687d80fd 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -277,6 +277,7 @@ authors. Ed Sweeney
Ed Sweeney

πŸ–‹ javihernant
javihernant

πŸ–‹ Vegard
Vegard

πŸ–‹ + Ryan Whitehouse
Ryan Whitehouse

πŸ–‹ From 78f4d6636469506b03c6c6b87fc567271f863a7c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:25:47 +0000 Subject: [PATCH 0204/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 1a218e5f54..23b1c061d6 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1956,6 +1956,15 @@ "contributions": [ "content" ] + }, + { + "login": "ryanwhitehouse", + "name": "Ryan Whitehouse", + "avatar_url": "https://avatars.githubusercontent.com/u/13400784?v=4", + "profile": "https://github.com/ryanwhitehouse", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 99bd7181f9f3ed59f382499796cadbca7c2509a1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:26:45 +0000 Subject: [PATCH 0205/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4a687d80fd..48fbc53b97 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -278,6 +278,7 @@ authors. javihernant
javihernant

πŸ–‹ Vegard
Vegard

πŸ–‹ Ryan Whitehouse
Ryan Whitehouse

πŸ–‹ + Ali Afsharzadeh
Ali Afsharzadeh

πŸ–‹ From 347462d87ed021cd62485404731a454a0d890193 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:26:46 +0000 Subject: [PATCH 0206/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 23b1c061d6..9323238bd0 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1965,6 +1965,15 @@ "contributions": [ "content" ] + }, + { + "login": "guoard", + "name": "Ali Afsharzadeh", + "avatar_url": "https://avatars.githubusercontent.com/u/65511355?v=4", + "profile": "https://github.com/guoard", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 1db646474e84fcf1c87118efb9ef0f6c3a96512c Mon Sep 17 00:00:00 2001 From: Ali Afsharzadeh Date: Wed, 29 Mar 2023 21:13:27 +0330 Subject: [PATCH 0207/1432] docs(error_handling): resolve markdown linter warnings --- exercises/error_handling/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/error_handling/README.md b/exercises/error_handling/README.md index 5255ace99e..3b21f2b782 100644 --- a/exercises/error_handling/README.md +++ b/exercises/error_handling/README.md @@ -1,5 +1,6 @@ # Error handling -Most errors aren’t serious enough to require the program to stop entirely. + +Most errors aren’t serious enough to require the program to stop entirely. Sometimes, when a function fails, it’s for a reason that you can easily interpret and respond to. For example, if you try to open a file and that operation fails because the file doesn’t exist, you might want to create the file instead of terminating the process. From 382e16eb7ea66cddc4860f4b19453b031a2a8a8a Mon Sep 17 00:00:00 2001 From: Ali Afsharzadeh Date: Thu, 30 Mar 2023 19:53:22 +0330 Subject: [PATCH 0208/1432] feat(docs): add markdown linter for exercises README.md files --- .github/workflows/lint.yml | 18 ++++++++++++++++++ .markdownlint.yml | 2 ++ exercises/conversions/README.md | 4 +++- exercises/hashmaps/README.md | 3 ++- exercises/lifetimes/README.md | 12 ++++++------ exercises/macros/README.md | 2 +- exercises/options/README.md | 3 ++- exercises/smart_pointers/README.md | 1 + exercises/traits/README.md | 2 +- 9 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .markdownlint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..67339d1b12 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,18 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: DavidAnson/markdownlint-cli2-action@v9 + with: + globs: "exercises/**/*.md" diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000000..d5f7e3913d --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,2 @@ +# MD013/line-length Line length, Expected: 80 +MD013: false diff --git a/exercises/conversions/README.md b/exercises/conversions/README.md index 8d7da93ecb..619a78c565 100644 --- a/exercises/conversions/README.md +++ b/exercises/conversions/README.md @@ -6,6 +6,7 @@ The simplest form of type conversion is a type cast expression. It is denoted wi Rust also offers traits that facilitate type conversions upon implementation. These traits can be found under the [`convert`](https://doc.rust-lang.org/std/convert/index.html) module. The traits are the following: + - `From` and `Into` covered in [`from_into`](from_into.rs) - `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs) - `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs) @@ -17,5 +18,6 @@ These should be the main ways ***within the standard library*** to convert data ## Further information These are not directly covered in the book, but the standard library has a great documentation for it. + - [conversions](https://doc.rust-lang.org/std/convert/index.html) -- [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html) \ No newline at end of file +- [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html) diff --git a/exercises/hashmaps/README.md b/exercises/hashmaps/README.md index 30471cf967..80ec14416e 100644 --- a/exercises/hashmaps/README.md +++ b/exercises/hashmaps/README.md @@ -1,6 +1,7 @@ # Hashmaps + A *hash map* allows you to associate a value with a particular key. -You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), +You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), [*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages. This is the other data structure that we've been talking about before, when diff --git a/exercises/lifetimes/README.md b/exercises/lifetimes/README.md index 74a4a7863c..91373f733a 100644 --- a/exercises/lifetimes/README.md +++ b/exercises/lifetimes/README.md @@ -3,17 +3,17 @@ Lifetimes tell the compiler how to check whether references live long enough to be valid in any given situation. For example lifetimes say "make sure parameter 'a' lives as long as parameter 'b' so that the return -value is valid". +value is valid". -They are only necessary on borrows, i.e. references, +They are only necessary on borrows, i.e. references, since copied parameters or moves are owned in their scope and cannot be referenced outside. Lifetimes mean that calling code of e.g. functions -can be checked to make sure their arguments are valid. Lifetimes are +can be checked to make sure their arguments are valid. Lifetimes are restrictive of their callers. -If you'd like to learn more about lifetime annotations, the -[lifetimekata](https://tfpk.github.io/lifetimekata/) project -has a similar style of exercises to Rustlings, but is all about +If you'd like to learn more about lifetime annotations, the +[lifetimekata](https://tfpk.github.io/lifetimekata/) project +has a similar style of exercises to Rustlings, but is all about learning to write lifetime annotations. ## Further information diff --git a/exercises/macros/README.md b/exercises/macros/README.md index e34bc3a8e8..337816d6e6 100644 --- a/exercises/macros/README.md +++ b/exercises/macros/README.md @@ -4,7 +4,7 @@ Rust's macro system is very powerful, but also kind of difficult to wrap your head around. We're not going to teach you how to write your own fully-featured macros. Instead, we'll show you how to use and create them. -If you'd like to learn more about writing your own macros, the +If you'd like to learn more about writing your own macros, the [macrokata](https://github.com/tfpk/macrokata) project has a similar style of exercises to Rustlings, but is all about learning to write Macros. diff --git a/exercises/options/README.md b/exercises/options/README.md index 6140a16779..bdd33749af 100644 --- a/exercises/options/README.md +++ b/exercises/options/README.md @@ -1,7 +1,8 @@ # Options -Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. +Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. Option types are very common in Rust code, as they have a number of uses: + - Initial values - Return values for functions that are not defined over their entire input range (partial functions) - Return value for otherwise reporting simple errors, where None is returned on error diff --git a/exercises/smart_pointers/README.md b/exercises/smart_pointers/README.md index c517ae31a0..d56d2b6281 100644 --- a/exercises/smart_pointers/README.md +++ b/exercises/smart_pointers/README.md @@ -1,4 +1,5 @@ # Smart Pointers + In Rust, smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities. Smart pointers in Rust often own the data they point to, while references only borrow data. diff --git a/exercises/traits/README.md b/exercises/traits/README.md index de67acd060..ac87c64ebc 100644 --- a/exercises/traits/README.md +++ b/exercises/traits/README.md @@ -7,13 +7,13 @@ Data types can implement traits. To do so, the methods making up the trait are d In this way, traits are somewhat similar to Java interfaces and C++ abstract classes. Some additional common Rust traits include: + - `Clone` (the `clone` method) - `Display` (which allows formatted display via `{}`) - `Debug` (which allows formatted display via `{:?}`) Because traits indicate shared behavior between data types, they are useful when writing generics. - ## Further information - [Traits](https://doc.rust-lang.org/book/ch10-02-traits.html) From 8c4a7bea5fc9a4af6cb59dcb745aafa2d85bd2b9 Mon Sep 17 00:00:00 2001 From: Keogami <41939011+keogami@users.noreply.github.com> Date: Fri, 31 Mar 2023 02:26:23 +0530 Subject: [PATCH 0209/1432] docs(README.md): split quick installation commands into two separate code blocks Having the two quick installation commands in two separate code blocks makes it easy to copy them through the github's `copy to clipboard` button --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d16b73858e..be470fdbb7 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,10 @@ Just run: ```bash curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -# Or if you want it to be installed to a different path: +``` +Or if you want it to be installed to a different path: + +```bash curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -s mypath/ ``` From 22bb662d3eb9efd081a5603640628bdeebab97b9 Mon Sep 17 00:00:00 2001 From: Alexandre ESSE Date: Fri, 31 Mar 2023 11:20:11 +0200 Subject: [PATCH 0210/1432] fix(exercises): remove trailing spaces --- exercises/hashmaps/README.md | 2 +- exercises/lifetimes/README.md | 12 ++++++------ exercises/macros/README.md | 2 +- exercises/options/README.md | 2 +- exercises/threads/threads1.rs | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/exercises/hashmaps/README.md b/exercises/hashmaps/README.md index 30471cf967..6afa04b257 100644 --- a/exercises/hashmaps/README.md +++ b/exercises/hashmaps/README.md @@ -1,6 +1,6 @@ # Hashmaps A *hash map* allows you to associate a value with a particular key. -You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), +You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), [*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages. This is the other data structure that we've been talking about before, when diff --git a/exercises/lifetimes/README.md b/exercises/lifetimes/README.md index 74a4a7863c..91373f733a 100644 --- a/exercises/lifetimes/README.md +++ b/exercises/lifetimes/README.md @@ -3,17 +3,17 @@ Lifetimes tell the compiler how to check whether references live long enough to be valid in any given situation. For example lifetimes say "make sure parameter 'a' lives as long as parameter 'b' so that the return -value is valid". +value is valid". -They are only necessary on borrows, i.e. references, +They are only necessary on borrows, i.e. references, since copied parameters or moves are owned in their scope and cannot be referenced outside. Lifetimes mean that calling code of e.g. functions -can be checked to make sure their arguments are valid. Lifetimes are +can be checked to make sure their arguments are valid. Lifetimes are restrictive of their callers. -If you'd like to learn more about lifetime annotations, the -[lifetimekata](https://tfpk.github.io/lifetimekata/) project -has a similar style of exercises to Rustlings, but is all about +If you'd like to learn more about lifetime annotations, the +[lifetimekata](https://tfpk.github.io/lifetimekata/) project +has a similar style of exercises to Rustlings, but is all about learning to write lifetime annotations. ## Further information diff --git a/exercises/macros/README.md b/exercises/macros/README.md index e34bc3a8e8..337816d6e6 100644 --- a/exercises/macros/README.md +++ b/exercises/macros/README.md @@ -4,7 +4,7 @@ Rust's macro system is very powerful, but also kind of difficult to wrap your head around. We're not going to teach you how to write your own fully-featured macros. Instead, we'll show you how to use and create them. -If you'd like to learn more about writing your own macros, the +If you'd like to learn more about writing your own macros, the [macrokata](https://github.com/tfpk/macrokata) project has a similar style of exercises to Rustlings, but is all about learning to write Macros. diff --git a/exercises/options/README.md b/exercises/options/README.md index 6140a16779..5433fed86f 100644 --- a/exercises/options/README.md +++ b/exercises/options/README.md @@ -1,6 +1,6 @@ # Options -Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. +Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. Option types are very common in Rust code, as they have a number of uses: - Initial values - Return values for functions that are not defined over their entire input range (partial functions) diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index d6376db24e..ae124eeb9f 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -30,7 +30,7 @@ fn main() { if results.len() != 10 { panic!("Oh no! All the spawned threads did not finish!"); } - + println!(); for (i, result) in results.into_iter().enumerate() { println!("thread {} took {}ms", i, result); From d7111cb4a35b34c8e4e910a27ff10facb7efd917 Mon Sep 17 00:00:00 2001 From: Alexandre ESSE Date: Fri, 31 Mar 2023 11:20:30 +0200 Subject: [PATCH 0211/1432] fix(main.rs): remove trailing spaces --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 312a0a127d..704398e516 100644 --- a/src/main.rs +++ b/src/main.rs @@ -433,8 +433,8 @@ started, here's a couple of notes about how Rustlings operates: 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, and sometimes, other learners do too so you can help each other out! -5. If you want to use `rust-analyzer` with exercises, which provides features like - autocompletion, run the command `rustlings lsp`. +5. If you want to use `rust-analyzer` with exercises, which provides features like + autocompletion, run the command `rustlings lsp`. Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. Make sure to have your editor open!"#; From e185e27273e6e64e9dcda6e7ab6c316716edceac Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:29:29 +0000 Subject: [PATCH 0212/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 48fbc53b97..df85b14962 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -280,6 +280,9 @@ authors. Ryan Whitehouse
Ryan Whitehouse

πŸ–‹ Ali Afsharzadeh
Ali Afsharzadeh

πŸ–‹ + + Keogami
Keogami

πŸ“– + From 84dcd51d37846120cb9ff471a17e4c7c025d3ab3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:29:30 +0000 Subject: [PATCH 0213/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9323238bd0..55161bbe58 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1974,6 +1974,15 @@ "contributions": [ "content" ] + }, + { + "login": "keogami", + "name": "Keogami", + "avatar_url": "https://avatars.githubusercontent.com/u/41939011?v=4", + "profile": "http://keogami.ml", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 8, From 391e5d121a7e68c2006c0cde8af0a4fcae3ed07a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:31:32 +0000 Subject: [PATCH 0214/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index df85b14962..125fc10b2b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -282,6 +282,7 @@ authors. Keogami
Keogami

πŸ“– + Alexandre Esse
Alexandre Esse

πŸ–‹ From 75f0ab65d1d93be46440bf00bdaa211188f91db6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:31:33 +0000 Subject: [PATCH 0215/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 55161bbe58..0416dd8f81 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1983,6 +1983,15 @@ "contributions": [ "doc" ] + }, + { + "login": "ahresse", + "name": "Alexandre Esse", + "avatar_url": "https://avatars.githubusercontent.com/u/28402488?v=4", + "profile": "https://github.com/ahresse", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From b1fb38e73c506e9f4aec643dc9ef167517fa5437 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sun, 2 Apr 2023 12:25:24 +0530 Subject: [PATCH 0216/1432] fix: move lifetimes above tests in recommended order --- info.toml | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/info.toml b/info.toml index fde77e232c..ef891435ac 100644 --- a/info.toml +++ b/info.toml @@ -748,6 +748,33 @@ hint = """ To find the best solution to this challenge you're going to need to think back to your knowledge of traits, specifically Trait Bound Syntax - you may also need this: `use std::fmt::Display;`.""" +# LIFETIMES + +[[exercises]] +name = "lifetimes1" +path = "exercises/lifetimes/lifetimes1.rs" +mode = "compile" +hint = """ +Let the compiler guide you. Also take a look at the book if you need help: +https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" + +[[exercises]] +name = "lifetimes2" +path = "exercises/lifetimes/lifetimes2.rs" +mode = "compile" +hint = """ +Remember that the generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y. +You can take at least two paths to achieve the desired result while keeping the inner block: +1. Move the string2 declaration to make it live as long as string1 (how is result declared?) +2. Move println! into the inner block""" + +[[exercises]] +name = "lifetimes3" +path = "exercises/lifetimes/lifetimes3.rs" +mode = "compile" +hint = """ +If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" + # TESTS [[exercises]] @@ -780,33 +807,6 @@ You can call a function right where you're passing arguments to `assert!` -- so something like `assert!(having_fun())`. If you want to check that you indeed get false, you can negate the result of what you're doing using `!`, like `assert!(!having_fun())`.""" -# LIFETIMES - -[[exercises]] -name = "lifetimes1" -path = "exercises/lifetimes/lifetimes1.rs" -mode = "compile" -hint = """ -Let the compiler guide you. Also take a look at the book if you need help: -https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" - -[[exercises]] -name = "lifetimes2" -path = "exercises/lifetimes/lifetimes2.rs" -mode = "compile" -hint = """ -Remember that the generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y. -You can take at least two paths to achieve the desired result while keeping the inner block: -1. Move the string2 declaration to make it live as long as string1 (how is result declared?) -2. Move println! into the inner block""" - -[[exercises]] -name = "lifetimes3" -path = "exercises/lifetimes/lifetimes3.rs" -mode = "compile" -hint = """ -If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" - # STANDARD LIBRARY TYPES [[exercises]] From 2f2fd43771c1b89b580d879e6f741364a5e76737 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 2 Apr 2023 10:45:42 +0000 Subject: [PATCH 0217/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 125fc10b2b..cffd25eb67 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -283,6 +283,7 @@ authors. Keogami
Keogami

πŸ“– Alexandre Esse
Alexandre Esse

πŸ–‹ + Sagar Vora
Sagar Vora

πŸ–‹ From 54d21dc7c11064aae647cd3893e2f4d07adf0ae7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 2 Apr 2023 10:45:43 +0000 Subject: [PATCH 0218/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0416dd8f81..97cd1b04cd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1992,6 +1992,15 @@ "contributions": [ "content" ] + }, + { + "login": "sagarvora", + "name": "Sagar Vora", + "avatar_url": "https://avatars.githubusercontent.com/u/16315650?v=4", + "profile": "https://resilient.tech", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 27b75795666cddd3725c323cbc0b68b206fc150e Mon Sep 17 00:00:00 2001 From: poneciak Date: Wed, 5 Apr 2023 08:18:51 +0200 Subject: [PATCH 0219/1432] created task --- exercises/tests/tests4.rs | 40 +++++++++++++++++++++++++++++++++++++++ info.toml | 10 ++++++++++ 2 files changed, 50 insertions(+) create mode 100644 exercises/tests/tests4.rs diff --git a/exercises/tests/tests4.rs b/exercises/tests/tests4.rs new file mode 100644 index 0000000000..2d8dd1300b --- /dev/null +++ b/exercises/tests/tests4.rs @@ -0,0 +1,40 @@ +// tests4.rs +// Correct the tests to +// Do not change Rectangle::new body +// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +struct Rectangle { + width: i32, + height: i32 +} + +impl Rectangle { + pub fn new(width: i32, height: i32) -> Self { + if width < 0 || height < 0 { + panic!("Rectangle width and height cannot be negative!") + } + Rectangle {width, height} + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn correct_width_and_height() { + let _rect = Rectangle::new(10, 10); + } + + #[test] + fn negative_width() { + let _rect = Rectangle::new(-10, 10); + } + + #[test] + fn negative_height() { + let _rect = Rectangle::new(10, -10); + } +} diff --git a/info.toml b/info.toml index ef891435ac..ad06b888b1 100644 --- a/info.toml +++ b/info.toml @@ -807,6 +807,16 @@ You can call a function right where you're passing arguments to `assert!` -- so something like `assert!(having_fun())`. If you want to check that you indeed get false, you can negate the result of what you're doing using `!`, like `assert!(!having_fun())`.""" +[[exercises]] +name = "tests4" +path = "exercises/tests/tests4.rs" +mode = "test" +hint = """ +We expect method `Rectangle::new()` to panic for negative values. +To handle that you need to add special attribute to test function. +You can refer to the docs: https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html""" + + # STANDARD LIBRARY TYPES [[exercises]] From c4974ac7820784899592a26b4227683bca96bd2b Mon Sep 17 00:00:00 2001 From: poneciak Date: Wed, 5 Apr 2023 13:09:13 +0200 Subject: [PATCH 0220/1432] added required changes - fixed grammar in hint and added more specific link - added comments in test functions - changed introduction paragraph --- exercises/tests/tests4.rs | 11 ++++++++--- info.toml | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/exercises/tests/tests4.rs b/exercises/tests/tests4.rs index 2d8dd1300b..727dbd7ed0 100644 --- a/exercises/tests/tests4.rs +++ b/exercises/tests/tests4.rs @@ -1,6 +1,5 @@ // tests4.rs -// Correct the tests to -// Do not change Rectangle::new body +// Make sure that we're testing for the correct conditions! // Execute `rustlings hint tests4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -11,6 +10,7 @@ struct Rectangle { } impl Rectangle { + // Only change the test functions themselves pub fn new(width: i32, height: i32) -> Self { if width < 0 || height < 0 { panic!("Rectangle width and height cannot be negative!") @@ -25,16 +25,21 @@ mod tests { #[test] fn correct_width_and_height() { - let _rect = Rectangle::new(10, 10); + // This test should check if the rectangle is the size that we pass into its constructor + let rect = Rectangle::new(10, 20); + assert_eq!(???, 10); // check width + assert_eq!(???, 20); // check height } #[test] fn negative_width() { + // This test should check if thread panics when we try to create rectangle with negative width let _rect = Rectangle::new(-10, 10); } #[test] fn negative_height() { + // This test should check if thread panics when we try to create rectangle with negative height let _rect = Rectangle::new(10, -10); } } diff --git a/info.toml b/info.toml index ad06b888b1..fe79bdee12 100644 --- a/info.toml +++ b/info.toml @@ -813,8 +813,9 @@ path = "exercises/tests/tests4.rs" mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. -To handle that you need to add special attribute to test function. -You can refer to the docs: https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html""" +To handle that you need to add a special attribute to the test function. +You can refer to the docs: +https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" # STANDARD LIBRARY TYPES From 102d7f3d0ec8d9e6b0f4380c9ec199c47cf127f1 Mon Sep 17 00:00:00 2001 From: poneciak Date: Wed, 5 Apr 2023 13:24:14 +0200 Subject: [PATCH 0221/1432] changed comments in tests also fixed small logical issue in `Rectangle::new()` where u could create rectangle with width or height equals 0 --- exercises/tests/tests4.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/tests/tests4.rs b/exercises/tests/tests4.rs index 727dbd7ed0..1f34a2b2a2 100644 --- a/exercises/tests/tests4.rs +++ b/exercises/tests/tests4.rs @@ -12,7 +12,7 @@ struct Rectangle { impl Rectangle { // Only change the test functions themselves pub fn new(width: i32, height: i32) -> Self { - if width < 0 || height < 0 { + if width <= 0 || height <= 0 { panic!("Rectangle width and height cannot be negative!") } Rectangle {width, height} @@ -33,13 +33,13 @@ mod tests { #[test] fn negative_width() { - // This test should check if thread panics when we try to create rectangle with negative width + // This test should check if program panics when we try to create rectangle with negative width let _rect = Rectangle::new(-10, 10); } #[test] fn negative_height() { - // This test should check if thread panics when we try to create rectangle with negative height + // This test should check if program panics when we try to create rectangle with negative height let _rect = Rectangle::new(10, -10); } } From af365f444fb63d3f0cc7a86452d89ca6f73e9821 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:05:29 +0000 Subject: [PATCH 0222/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index cffd25eb67..f39c7642f3 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -284,6 +284,7 @@ authors. Keogami
Keogami

πŸ“– Alexandre Esse
Alexandre Esse

πŸ–‹ Sagar Vora
Sagar Vora

πŸ–‹ + Kacper Poneta
Kacper Poneta

πŸ–‹ From 63a467af31a8bda072db78c2dc0037a5498baf47 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:05:30 +0000 Subject: [PATCH 0223/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 97cd1b04cd..bcf8475371 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2001,6 +2001,15 @@ "contributions": [ "content" ] + }, + { + "login": "poneciak57", + "name": "Kacper Poneta", + "avatar_url": "https://avatars.githubusercontent.com/u/94321164?v=4", + "profile": "https://github.com/poneciak57", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 722c7910e411fb11cab5ada2222d6c52976a2411 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:06:58 +0000 Subject: [PATCH 0224/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index f39c7642f3..e32782e8b9 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -285,6 +285,7 @@ authors. Alexandre Esse
Alexandre Esse

πŸ–‹ Sagar Vora
Sagar Vora

πŸ–‹ Kacper Poneta
Kacper Poneta

πŸ–‹ + Aaron Suggs
Aaron Suggs

πŸ–‹ From 16ca8715441dabd402dbdf431e117f3c9ad7ff8e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:06:59 +0000 Subject: [PATCH 0225/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index bcf8475371..01576d2757 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2010,6 +2010,15 @@ "contributions": [ "content" ] + }, + { + "login": "ktheory", + "name": "Aaron Suggs", + "avatar_url": "https://avatars.githubusercontent.com/u/975?v=4", + "profile": "https://ktheory.com/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c7adaa7d14b66012d10f7e5ad55ee9c3e8615b8b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:09:50 +0000 Subject: [PATCH 0226/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index e32782e8b9..ed50669b25 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -286,6 +286,7 @@ authors. Sagar Vora
Sagar Vora

πŸ–‹ Kacper Poneta
Kacper Poneta

πŸ–‹ Aaron Suggs
Aaron Suggs

πŸ–‹ + Alex
Alex

πŸ–‹ From d544bfcd6ddc3167a5a484b8ea8776d4828642ee Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:09:51 +0000 Subject: [PATCH 0227/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 01576d2757..e055aed742 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2019,6 +2019,15 @@ "contributions": [ "content" ] + }, + { + "login": "alexwh", + "name": "Alex", + "avatar_url": "https://avatars.githubusercontent.com/u/1723612?v=4", + "profile": "https://github.com/alexwh", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 2f338260632ae65bfd6febf28bde68b26e187b9e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:11:14 +0000 Subject: [PATCH 0228/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index ed50669b25..9e757bc2f2 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -287,6 +287,7 @@ authors. Kacper Poneta
Kacper Poneta

πŸ–‹ Aaron Suggs
Aaron Suggs

πŸ–‹ Alex
Alex

πŸ–‹ + Sebastian TΓΆrnquist
Sebastian TΓΆrnquist

πŸ–‹ From 5de7124eeedbff53a493cbff0e6d4e5b97706c42 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:11:15 +0000 Subject: [PATCH 0229/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e055aed742..bdcf53c6e8 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2028,6 +2028,15 @@ "contributions": [ "content" ] + }, + { + "login": "stornquist", + "name": "Sebastian TΓΆrnquist", + "avatar_url": "https://avatars.githubusercontent.com/u/42915664?v=4", + "profile": "https://github.com/stornquist", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c74e0bfd188169c15e51dd10501119515dbc220e Mon Sep 17 00:00:00 2001 From: Aaron Wang Date: Fri, 7 Apr 2023 01:56:20 -0400 Subject: [PATCH 0230/1432] docs: update line numbers in move_semantics2 --- exercises/move_semantics/move_semantics2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 68dbf021d5..ae76a7aa27 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,5 +1,5 @@ // move_semantics2.rs -// Make me compile without changing line 13 or moving line 10! +// Make me compile without changing line 17 or moving line 14! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. // Expected output: From 8cb5cba7750a95336a2394c137b2e07202e30da3 Mon Sep 17 00:00:00 2001 From: "J.c" Date: Sat, 8 Apr 2023 10:50:50 +0200 Subject: [PATCH 0231/1432] docs(vecs2): update hints --- info.toml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/info.toml b/info.toml index 28f9bb31e7..8f391bae9c 100644 --- a/info.toml +++ b/info.toml @@ -260,15 +260,14 @@ name = "vecs2" path = "exercises/vecs/vecs2.rs" mode = "test" hint = """ -Hint 1: `i` is each element from the Vec as they are being iterated. Can you try -multiplying this? +In the first function we are looping over the Vector and getting a reference to one `element` at a time. +To modify the value of that `element` we need to use the * dereference operator. You can learn more in this chapter of the Rust book: +https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector -Hint 2: For the first function, there's a way to directly access the numbers stored -in the Vec, using the * dereference operator. You can both access and write to the -number that way. +In the second function this dereferencing is not necessary, because the map function expects the new value to be returned. -After you've completed both functions, decide for yourself which approach you like -better. What do you think is the more commonly used pattern under Rust developers? +After you've completed both functions, decide for yourself which approach you like better. +What do you think is the more commonly used pattern under Rust developers? """ # MOVE SEMANTICS From 8ed2cf7ef5209012f6948b55747a4142939f0930 Mon Sep 17 00:00:00 2001 From: Aaron Wang Date: Mon, 10 Apr 2023 22:36:21 -0400 Subject: [PATCH 0232/1432] Update move_semantics2.rs --- exercises/move_semantics/move_semantics2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index ae76a7aa27..93bb82efb9 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,5 +1,4 @@ // move_semantics2.rs -// Make me compile without changing line 17 or moving line 14! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. // Expected output: @@ -11,6 +10,7 @@ fn main() { let vec0 = Vec::new(); + // Do not move the following line! let mut vec1 = fill_vec(vec0); // Do not change the following line! From e6b1ef20496ef2287442c4abef130ae835395a23 Mon Sep 17 00:00:00 2001 From: bean5 <2052646+bean5@users.noreply.github.com> Date: Thu, 13 Apr 2023 19:37:19 -0600 Subject: [PATCH 0233/1432] docs: Apply fixes that linter noticed --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be470fdbb7..f37e122cbe 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Alternatively, for a first-time Rust learner, there are several other resources: _Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._ _Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt install gcc`. Yum: `sudo yum -y install gcc`._ -You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager. +You will need to have Rust installed. You can get it by visiting . This'll also install Cargo, Rust's package/project manager. ## MacOS/Linux @@ -23,6 +23,7 @@ Just run: ```bash curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash ``` + Or if you want it to be installed to a different path: ```bash From 15ae83f8681ecba76554841743c1a715001ded75 Mon Sep 17 00:00:00 2001 From: bean5 <2052646+bean5@users.noreply.github.com> Date: Thu, 13 Apr 2023 19:39:57 -0600 Subject: [PATCH 0234/1432] docs: Replace apostrophe (for consistency with other README files) --- exercises/threads/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/threads/README.md b/exercises/threads/README.md index d086694761..dbe66643c3 100644 --- a/exercises/threads/README.md +++ b/exercises/threads/README.md @@ -1,6 +1,6 @@ # Threads -In most current operating systems, an executed program’s code is run in a process, and the operating system manages multiple processes at once. +In most current operating systems, an executed program's code is run in a process, and the operating system manages multiple processes at once. Within your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called threads. ## Further information From 352267871cbc30d201c53179da3a31d9f5657d44 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 15 Apr 2023 17:19:16 +0100 Subject: [PATCH 0235/1432] fix: Added some extra tests to validate iterators5 solution closes: #1387 --- exercises/iterators/iterators5.rs | 53 ++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/exercises/iterators/iterators5.rs b/exercises/iterators/iterators5.rs index 8709795675..45f0d485bd 100644 --- a/exercises/iterators/iterators5.rs +++ b/exercises/iterators/iterators5.rs @@ -65,12 +65,27 @@ mod tests { } #[test] - fn count_equals_for() { + fn count_some() { let map = get_map(); - assert_eq!( - count_for(&map, Progress::Complete), - count_iterator(&map, Progress::Complete) - ); + assert_eq!(1, count_iterator(&map, Progress::Some)); + } + + #[test] + fn count_none() { + let map = get_map(); + assert_eq!(2, count_iterator(&map, Progress::None)); + } + + #[test] + fn count_complete_equals_for() { + let map = get_map(); + let progressStates = vec![Progress::Complete, Progress::Some, Progress::None]; + for progressState in progressStates { + assert_eq!( + count_for(&map, progressState), + count_iterator(&map, progressState) + ); + } } #[test] @@ -83,14 +98,36 @@ mod tests { } #[test] - fn count_collection_equals_for() { + fn count_collection_some() { let collection = get_vec_map(); assert_eq!( - count_collection_for(&collection, Progress::Complete), - count_collection_iterator(&collection, Progress::Complete) + 1, + count_collection_iterator(&collection, Progress::Some) ); } + #[test] + fn count_collection_none() { + let collection = get_vec_map(); + assert_eq!( + 4, + count_collection_iterator(&collection, Progress::None) + ); + } + + #[test] + fn count_collection_equals_for() { + let progressStates = vec![Progress::Complete, Progress::Some, Progress::None]; + let collection = get_vec_map(); + + for progressState in progressStates { + assert_eq!( + count_collection_for(&collection, progressState), + count_collection_iterator(&collection, progressState) + ); + } + } + fn get_map() -> HashMap { use Progress::*; From a4a5691a7b3fac8201cf2965d94267da12bae47f Mon Sep 17 00:00:00 2001 From: Sebastian LaVine Date: Sun, 16 Apr 2023 21:44:08 -0400 Subject: [PATCH 0236/1432] feat: Add "!" command to `rustlings watch` --- src/main.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 704398e516..793b826ddc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -298,13 +298,21 @@ fn spawn_watch_shell( println!("Bye!"); } else if input.eq("help") { println!("Commands available to you in watch mode:"); - println!(" hint - prints the current exercise's hint"); - println!(" clear - clears the screen"); - println!(" quit - quits watch mode"); - println!(" help - displays this help message"); + println!(" hint - prints the current exercise's hint"); + println!(" clear - clears the screen"); + println!(" quit - quits watch mode"); + println!(" ! - executes a command, like `!rustc --explain E0381`"); + println!(" help - displays this help message"); println!(); println!("Watch mode automatically re-evaluates the current exercise"); println!("when you edit a file's contents.") + } else if let Some(cmd) = input.strip_prefix('!') { + let parts: Vec<&str> = cmd.split_whitespace().collect(); + if parts.is_empty() { + println!("no command provided"); + } else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() { + println!("failed to execute command `{}`: {}", cmd, e); + } } else { println!("unknown command: {input}"); } From 4944488287ca28bb73b067c3b0321adb6fb010ae Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:51:24 +0000 Subject: [PATCH 0237/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 9e757bc2f2..f5accd799a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -288,6 +288,7 @@ authors. Aaron Suggs
Aaron Suggs

πŸ–‹ Alex
Alex

πŸ–‹ Sebastian TΓΆrnquist
Sebastian TΓΆrnquist

πŸ–‹ + Sebastian LaVine
Sebastian LaVine

πŸ’» From 6209e7004e24492776edc5408260707949e6ffee Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:51:25 +0000 Subject: [PATCH 0238/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index bdcf53c6e8..9c39e5ecb1 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2037,6 +2037,15 @@ "contributions": [ "content" ] + }, + { + "login": "smlavine", + "name": "Sebastian LaVine", + "avatar_url": "https://avatars.githubusercontent.com/u/33563640?v=4", + "profile": "http://smlavine.com", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 319a8253ba727d09a1c52c7d22b4e089137ecff9 Mon Sep 17 00:00:00 2001 From: Alan Gerber Date: Thu, 20 Apr 2023 12:15:31 -0400 Subject: [PATCH 0239/1432] fix(move_semantics2): fix line number comment Commit fef8314 added three lines of comments, which left the line numbers expected to stay unchanged mentioned on line 2 out of date. --- exercises/move_semantics/move_semantics2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 68dbf021d5..ae76a7aa27 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,5 +1,5 @@ // move_semantics2.rs -// Make me compile without changing line 13 or moving line 10! +// Make me compile without changing line 17 or moving line 14! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. // Expected output: From 836134202ef505e03e8177565e512059488aded2 Mon Sep 17 00:00:00 2001 From: lionel-rowe Date: Fri, 21 Apr 2023 06:05:25 +0100 Subject: [PATCH 0240/1432] feat(options2): better tests for layered_option The existing test can be solved with the following: ```rs while let Some(integer) = optional_integers.pop() { assert_eq!(integer.unwrap(), range); ``` Similarly with `expect(...)`, `unwrap_or(0)`, `unwrap_or_default()`, etc. However, none of these solutions use the learning point of stacking `Option`s. The updated test can _only_ be solved by stacking `Option`s: ```rs while let Some(Some(integer)) = optional_integers.pop() { assert_eq!(integer, cursor); ``` With the updated test, using `unwrap` or `expect` will panic when it hits the `None` value, and using `unwrap_or` or `unwrap_or_default` will cause the final `assert_eq!(cursor, 0)` to panic. --- exercises/options/options2.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/exercises/options/options2.rs b/exercises/options/options2.rs index 4e36443fd7..337c4261c5 100644 --- a/exercises/options/options2.rs +++ b/exercises/options/options2.rs @@ -18,17 +18,22 @@ mod tests { #[test] fn layered_option() { - let mut range = 10; - let mut optional_integers: Vec> = Vec::new(); - for i in 0..(range + 1) { + let range = 10; + let mut optional_integers: Vec> = vec![None]; + + for i in 1..(range + 1) { optional_integers.push(Some(i)); } + let mut cursor = range; + // TODO: make this a while let statement - remember that vector.pop also adds another layer of Option - // You can stack `Option`'s into while let and if let + // You can stack `Option`s into while let and if let integer = optional_integers.pop() { - assert_eq!(integer, range); - range -= 1; + assert_eq!(integer, cursor); + cursor -= 1; } + + assert_eq!(cursor, 0); } } From e2da663628f61f19066cd92b005bae3adc8521d8 Mon Sep 17 00:00:00 2001 From: deedy5 <65482418+deedy5@users.noreply.github.com> Date: Fri, 21 Apr 2023 15:51:52 +0000 Subject: [PATCH 0241/1432] Update info.toml modules2.rs: add more information to hint --- info.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 28f9bb31e7..ac04fc7dac 100644 --- a/info.toml +++ b/info.toml @@ -478,7 +478,8 @@ hint = """ The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and associated constants). Complete the `use` statements to fit the uses in main and -find the one keyword missing for both constants.""" +find the one keyword missing for both constants. +Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" [[exercises]] name = "modules3" From 065cd0170e46fbdf0f74aee4708c4e3a200ab6ce Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 22 Apr 2023 13:48:41 +0000 Subject: [PATCH 0242/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index f5accd799a..0f45c38f88 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -290,6 +290,9 @@ authors. Sebastian TΓΆrnquist
Sebastian TΓΆrnquist

πŸ–‹ Sebastian LaVine
Sebastian LaVine

πŸ’» + + Alan Gerber
Alan Gerber

πŸ–‹ + From 545a7262523cfe3074e91314ad621d84e48d81c1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 22 Apr 2023 13:48:42 +0000 Subject: [PATCH 0243/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9c39e5ecb1..8dd0d9285b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2046,6 +2046,15 @@ "contributions": [ "code" ] + }, + { + "login": "akgerber", + "name": "Alan Gerber", + "avatar_url": "https://avatars.githubusercontent.com/u/201313?v=4", + "profile": "http://www.alangerber.us", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c4627e7112f38a74e677f358bf5eafc7214c5a4f Mon Sep 17 00:00:00 2001 From: PiqqiDesigns Date: Thu, 27 Apr 2023 15:49:40 -0700 Subject: [PATCH 0244/1432] chore: clarified cow owned_no_mutation comments --- exercises/smart_pointers/cow1.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/smart_pointers/cow1.rs b/exercises/smart_pointers/cow1.rs index 885138a740..bc5b28e56f 100644 --- a/exercises/smart_pointers/cow1.rs +++ b/exercises/smart_pointers/cow1.rs @@ -52,7 +52,8 @@ mod tests { fn owned_no_mutation() -> Result<(), &'static str> { // We can also pass `slice` without `&` so Cow owns it directly. // In this case no mutation occurs and thus also no clone, - // but the result is still owned because it always was. + // but the result is still owned because it was never borrowed + // or mutated. let slice = vec![0, 1, 2]; let mut input = Cow::from(slice); match abs_all(&mut input) { From 72d0c53b33e45cc55a5eb0c82119b7e393c0c59b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 08:40:17 +0000 Subject: [PATCH 0245/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 0f45c38f88..8e4d0fab3a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -292,6 +292,7 @@ authors. Alan Gerber
Alan Gerber

πŸ–‹ + Eric
Eric

πŸ–‹ From eb4079c674640b4fc307d5dca7a23dcef33129f2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 08:40:18 +0000 Subject: [PATCH 0246/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8dd0d9285b..a973b2d7b6 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2055,6 +2055,15 @@ "contributions": [ "content" ] + }, + { + "login": "esotuvaka", + "name": "Eric", + "avatar_url": "https://avatars.githubusercontent.com/u/104941850?v=4", + "profile": "http://esotuvaka.github.io", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 5d3696a9e6bc6251f0c03d17d0d6a7a10ee09115 Mon Sep 17 00:00:00 2001 From: Romain Bayle <5014@holbertonstudents.com> Date: Mon, 1 May 2023 02:49:19 +0200 Subject: [PATCH 0247/1432] feat(cli): added success-hints option for the rustlings command closes #1373 --- src/main.rs | 14 +++++++++----- src/verify.rs | 32 +++++++++++++++++++------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main.rs b/src/main.rs index 793b826ddc..eaa365a479 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,9 @@ struct Args { /// show the executable version #[argh(switch, short = 'v')] version: bool, + /// show hints on success + #[argh(switch)] + success_hints: bool, #[argh(subcommand)] nested: Option, } @@ -148,6 +151,7 @@ fn main() { let toml_str = &fs::read_to_string("info.toml").unwrap(); let exercises = toml::from_str::(toml_str).unwrap().exercises; let verbose = args.nocapture; + let success_hints = args.success_hints; let command = args.nested.unwrap_or_else(|| { println!("{DEFAULT_OUT}\n"); @@ -229,7 +233,7 @@ fn main() { } Subcommands::Verify(_subargs) => { - verify(&exercises, (0, exercises.len()), verbose) + verify(&exercises, (0, exercises.len()), verbose, success_hints) .unwrap_or_else(|_| std::process::exit(1)); } @@ -252,7 +256,7 @@ fn main() { } } - Subcommands::Watch(_subargs) => match watch(&exercises, verbose) { + Subcommands::Watch(_subargs) => match watch(&exercises, verbose, success_hints) { Err(e) => { println!( "Error: Could not watch your progress. Error message was {:?}.", @@ -348,7 +352,7 @@ enum WatchStatus { Unfinished, } -fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result { +fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> notify::Result { /* Clears the terminal with an ANSI escape code. Works in UNIX and newer Windows terminals. */ fn clear_screen() { @@ -364,7 +368,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result { clear_screen(); let to_owned_hint = |t: &Exercise| t.hint.to_owned(); - let failed_exercise_hint = match verify(exercises.iter(), (0, exercises.len()), verbose) { + let failed_exercise_hint = match verify(exercises.iter(), (0, exercises.len()), verbose, success_hints) { Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))), }; @@ -386,7 +390,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result { ); let num_done = exercises.iter().filter(|e| e.looks_done()).count(); clear_screen(); - match verify(pending_exercises, (num_done, exercises.len()), verbose) { + match verify(pending_exercises, (num_done, exercises.len()), verbose, success_hints) { Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => { let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap(); diff --git a/src/verify.rs b/src/verify.rs index 68ba6cef05..f3f3b564a5 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -12,6 +12,7 @@ pub fn verify<'a>( exercises: impl IntoIterator, progress: (usize, usize), verbose: bool, + success_hints: bool, ) -> Result<(), &'a Exercise> { let (num_done, total) = progress; let bar = ProgressBar::new(total as u64); @@ -25,9 +26,9 @@ pub fn verify<'a>( for exercise in exercises { let compile_result = match exercise.mode { - Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose), - Mode::Compile => compile_and_run_interactively(exercise), - Mode::Clippy => compile_only(exercise), + Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints), + Mode::Compile => compile_and_run_interactively(exercise, success_hints), + Mode::Clippy => compile_only(exercise, success_hints), }; if !compile_result.unwrap_or(false) { return Err(exercise); @@ -46,12 +47,12 @@ enum RunMode { // Compile and run the resulting test harness of the given Exercise pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> { - compile_and_test(exercise, RunMode::NonInteractive, verbose)?; + compile_and_test(exercise, RunMode::NonInteractive, verbose, false)?; Ok(()) } // Invoke the rust compiler without running the resulting binary -fn compile_only(exercise: &Exercise) -> Result { +fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.enable_steady_tick(100); @@ -59,11 +60,11 @@ fn compile_only(exercise: &Exercise) -> Result { let _ = compile(exercise, &progress_bar)?; progress_bar.finish_and_clear(); - Ok(prompt_for_completion(exercise, None)) + Ok(prompt_for_completion(exercise, None, success_hints)) } // Compile the given Exercise and run the resulting binary in an interactive mode -fn compile_and_run_interactively(exercise: &Exercise) -> Result { +fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.enable_steady_tick(100); @@ -84,12 +85,12 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result { } }; - Ok(prompt_for_completion(exercise, Some(output.stdout))) + Ok(prompt_for_completion(exercise, Some(output.stdout), success_hints)) } // Compile the given Exercise as a test harness and display // the output if verbose is set to true -fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Result { +fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Testing {exercise}...")); progress_bar.enable_steady_tick(100); @@ -104,7 +105,7 @@ fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Re println!("{}", output.stdout); } if let RunMode::Interactive = run_mode { - Ok(prompt_for_completion(exercise, None)) + Ok(prompt_for_completion(exercise, None, success_hints)) } else { Ok(true) } @@ -142,12 +143,11 @@ fn compile<'a, 'b>( } } -fn prompt_for_completion(exercise: &Exercise, prompt_output: Option) -> bool { +fn prompt_for_completion(exercise: &Exercise, prompt_output: Option, success_hints: bool) -> bool { let context = match exercise.state() { State::Done => return true, State::Pending(context) => context, }; - match exercise.mode { Mode::Compile => success!("Successfully ran {}!", exercise), Mode::Test => success!("Successfully tested {}!", exercise), @@ -167,7 +167,6 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option) -> Mode::Test => "The code is compiling, and the tests pass!", Mode::Clippy => clippy_success_msg, }; - println!(); if no_emoji { println!("~*~ {success_msg} ~*~") @@ -183,6 +182,13 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option) -> println!("{}", separator()); println!(); } + if success_hints { + println!("Hints:"); + println!("{}", separator()); + println!("{}", exercise.hint); + println!("{}", separator()); + println!(); + } println!("You can keep working on this exercise,"); println!( From e631f3c78bf3f09955353778c8aa377085653d7a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 11:15:48 +0000 Subject: [PATCH 0248/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 8e4d0fab3a..164370ab26 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -293,6 +293,7 @@ authors. Alan Gerber
Alan Gerber

πŸ–‹ Eric
Eric

πŸ–‹ + Aaron Wang
Aaron Wang

πŸ–‹ From 583925c085308645504b30a323d66b8edf6f798a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 11:15:49 +0000 Subject: [PATCH 0249/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index a973b2d7b6..0e1b637367 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2064,6 +2064,15 @@ "contributions": [ "content" ] + }, + { + "login": "az0977776", + "name": "Aaron Wang", + "avatar_url": "https://avatars.githubusercontent.com/u/9172038?v=4", + "profile": "https://github.com/az0977776", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c44e3025f9a5af4b4a7999edddd76b7f7863328a Mon Sep 17 00:00:00 2001 From: Romain Bayle <5014@holbertonstudents.com> Date: Tue, 2 May 2023 22:46:41 +0200 Subject: [PATCH 0250/1432] option success_hints added to the struct Watchargs instead of Args --- src/main.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index eaa365a479..74e8161b26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,9 +37,6 @@ struct Args { /// show the executable version #[argh(switch, short = 'v')] version: bool, - /// show hints on success - #[argh(switch)] - success_hints: bool, #[argh(subcommand)] nested: Option, } @@ -64,7 +61,11 @@ struct VerifyArgs {} #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "watch")] /// Reruns `verify` when files were edited -struct WatchArgs {} +struct WatchArgs { + /// show hints on success + #[argh(switch)] + success_hints: bool, +} #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "run")] @@ -151,7 +152,6 @@ fn main() { let toml_str = &fs::read_to_string("info.toml").unwrap(); let exercises = toml::from_str::(toml_str).unwrap().exercises; let verbose = args.nocapture; - let success_hints = args.success_hints; let command = args.nested.unwrap_or_else(|| { println!("{DEFAULT_OUT}\n"); @@ -233,7 +233,7 @@ fn main() { } Subcommands::Verify(_subargs) => { - verify(&exercises, (0, exercises.len()), verbose, success_hints) + verify(&exercises, (0, exercises.len()), verbose, false) .unwrap_or_else(|_| std::process::exit(1)); } @@ -256,7 +256,7 @@ fn main() { } } - Subcommands::Watch(_subargs) => match watch(&exercises, verbose, success_hints) { + Subcommands::Watch(_subargs) => match watch(&exercises, verbose, _subargs.success_hints) { Err(e) => { println!( "Error: Could not watch your progress. Error message was {:?}.", From 3ecb47ff2cf7e5b6b6aa115fba02228896654b60 Mon Sep 17 00:00:00 2001 From: Noah May Date: Tue, 9 May 2023 15:13:18 -0500 Subject: [PATCH 0251/1432] fix(options3): panic when not matching to avoid false positives Closes #1503 --- exercises/options/options3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/options/options3.rs b/exercises/options/options3.rs index 3f995c520a..3ed76eebad 100644 --- a/exercises/options/options3.rs +++ b/exercises/options/options3.rs @@ -13,7 +13,7 @@ fn main() { match y { Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y), - _ => println!("no match"), + _ => panic!("no match!"), } y; // Fix without deleting this line. } From 70da26f649adcb7ea583db36a07bc8f285f3f5c3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 10 May 2023 08:49:44 +0000 Subject: [PATCH 0252/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 164370ab26..f1e4337608 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -294,6 +294,7 @@ authors. Alan Gerber
Alan Gerber

πŸ–‹ Eric
Eric

πŸ–‹ Aaron Wang
Aaron Wang

πŸ–‹ + Noah
Noah

πŸ–‹ From 0d252636be6675e24475d821a953cdaf5f1e0f45 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 10 May 2023 08:49:45 +0000 Subject: [PATCH 0253/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0e1b637367..4758924723 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2073,6 +2073,15 @@ "contributions": [ "content" ] + }, + { + "login": "nmay231", + "name": "Noah", + "avatar_url": "https://avatars.githubusercontent.com/u/35386821?v=4", + "profile": "https://github.com/nmay231", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 70cf7698acbde43025a23d0199057485d8d75ade Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 13:54:39 +0000 Subject: [PATCH 0254/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index f1e4337608..ed45690c43 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -295,6 +295,7 @@ authors. Eric
Eric

πŸ–‹ Aaron Wang
Aaron Wang

πŸ–‹ Noah
Noah

πŸ–‹ + rb5014
rb5014

πŸ–‹ From c5fe42089d5fe6a15f1e1a18f1a4f8d612f2d3c0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 13:54:40 +0000 Subject: [PATCH 0255/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4758924723..654d2d9374 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2082,6 +2082,15 @@ "contributions": [ "content" ] + }, + { + "login": "rb5014", + "name": "rb5014", + "avatar_url": "https://avatars.githubusercontent.com/u/105397317?v=4", + "profile": "https://github.com/rb5014", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 94c6131c59456c70302ee5c3d39114786533daf4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 13:55:28 +0000 Subject: [PATCH 0256/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index ed45690c43..8a24e68f79 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -296,6 +296,7 @@ authors. Aaron Wang
Aaron Wang

πŸ–‹ Noah
Noah

πŸ–‹ rb5014
rb5014

πŸ–‹ + deedy5
deedy5

πŸ–‹ From 6abff3954921aba490b9f135c701ccb5f26595c5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 13:55:29 +0000 Subject: [PATCH 0257/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 654d2d9374..35684e3923 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2091,6 +2091,15 @@ "contributions": [ "content" ] + }, + { + "login": "deedy5", + "name": "deedy5", + "avatar_url": "https://avatars.githubusercontent.com/u/65482418?v=4", + "profile": "https://github.com/deedy5", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 58cbdd71b64ad8d32d192e56cedd563ebca6fd5b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 13:57:10 +0000 Subject: [PATCH 0258/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 8a24e68f79..c1cc60c216 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -297,6 +297,7 @@ authors. Noah
Noah

πŸ–‹ rb5014
rb5014

πŸ–‹ deedy5
deedy5

πŸ–‹ + lionel-rowe
lionel-rowe

πŸ–‹ From eeb439eaf9e164307dd2533d5ef082f740f53af3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 13:57:11 +0000 Subject: [PATCH 0259/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 35684e3923..56c5e0289e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2100,6 +2100,15 @@ "contributions": [ "content" ] + }, + { + "login": "lionel-rowe", + "name": "lionel-rowe", + "avatar_url": "https://avatars.githubusercontent.com/u/26078826?v=4", + "profile": "https://github.com/lionel-rowe", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From a6f99645c6cb1b5493e1fbf16576ab5c0f1dd88c Mon Sep 17 00:00:00 2001 From: liv Date: Wed, 17 May 2023 16:02:39 +0200 Subject: [PATCH 0260/1432] chore: rustfmt --- exercises/iterators/iterators5.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/exercises/iterators/iterators5.rs b/exercises/iterators/iterators5.rs index 45f0d485bd..dcf0742ddd 100644 --- a/exercises/iterators/iterators5.rs +++ b/exercises/iterators/iterators5.rs @@ -77,7 +77,7 @@ mod tests { } #[test] - fn count_complete_equals_for() { + fn count_complete_equals_for() { let map = get_map(); let progressStates = vec![Progress::Complete, Progress::Some, Progress::None]; for progressState in progressStates { @@ -100,19 +100,13 @@ mod tests { #[test] fn count_collection_some() { let collection = get_vec_map(); - assert_eq!( - 1, - count_collection_iterator(&collection, Progress::Some) - ); + assert_eq!(1, count_collection_iterator(&collection, Progress::Some)); } #[test] fn count_collection_none() { let collection = get_vec_map(); - assert_eq!( - 4, - count_collection_iterator(&collection, Progress::None) - ); + assert_eq!(4, count_collection_iterator(&collection, Progress::None)); } #[test] From 9508e9791495f6e6f4c64b330a6cbba37241fafc Mon Sep 17 00:00:00 2001 From: liv Date: Wed, 17 May 2023 16:14:10 +0200 Subject: [PATCH 0261/1432] feat: write absolute root module paths for lsp --- src/project.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/project.rs b/src/project.rs index 6e483504f1..7865d92796 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,8 +1,9 @@ use glob::glob; use serde::{Deserialize, Serialize}; -use std::env; use std::error::Error; +use std::path::PathBuf; use std::process::Command; +use std::{env, fs}; /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file @@ -38,11 +39,12 @@ impl RustAnalyzerProject { } /// If path contains .rs extension, add a crate to `rust-project.json` - fn path_to_json(&mut self, path: String) { - if let Some((_, ext)) = path.split_once('.') { + fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box> { + if let Some(ext) = path.extension() { if ext == "rs" { + let abspath = fs::canonicalize(path)?; self.crates.push(Crate { - root_module: path, + root_module: abspath.display().to_string(), edition: "2021".to_string(), deps: Vec::new(), // This allows rust_analyzer to work inside #[test] blocks @@ -50,15 +52,16 @@ impl RustAnalyzerProject { }) } } + + Ok(()) } /// Parse the exercises folder for .rs files, any matches will create /// a new `crate` in rust-project.json which allows rust-analyzer to /// treat it like a normal binary pub fn exercises_to_json(&mut self) -> Result<(), Box> { - for e in glob("./exercises/**/*")? { - let path = e?.to_string_lossy().to_string(); - self.path_to_json(path); + for path in glob("./exercises/**/*")? { + self.path_to_json(path?)?; } Ok(()) } From af52fce275ba26b653026fffc5c09a73c7ba5846 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 14:14:43 +0000 Subject: [PATCH 0262/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index c1cc60c216..a27c60e9e0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -298,6 +298,7 @@ authors. rb5014
rb5014

πŸ–‹ deedy5
deedy5

πŸ–‹ lionel-rowe
lionel-rowe

πŸ–‹ + Ben
Ben

πŸ–‹ From ab61a0d5c7efb8cc4856587f5354fb2f9024a40a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 14:14:44 +0000 Subject: [PATCH 0263/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 56c5e0289e..1b9678e0e0 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2109,6 +2109,15 @@ "contributions": [ "content" ] + }, + { + "login": "Ben2917", + "name": "Ben", + "avatar_url": "https://avatars.githubusercontent.com/u/10279994?v=4", + "profile": "https://github.com/Ben2917", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From f452fd7bb0118d8e56848ec9e3f5e6e733be6316 Mon Sep 17 00:00:00 2001 From: liv Date: Wed, 17 May 2023 16:30:22 +0200 Subject: [PATCH 0264/1432] release: 5.5.0 --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 8 ++++---- flake.nix | 2 +- src/main.rs | 22 ++++++++++++++++++---- 6 files changed, 60 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c14e7d793..d4d6440648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ + +## 5.5.0 (2023-05-17) + +#### Added + +- `strings2`: Added a reference to the book chapter for reference conversion +- `lifetimes`: Added a link to the lifetimekata project +- Added a new `tests4` exercises, which teaches about testing for panics +- Added a `!` prefix command to watch mode that runs an external command +- Added a `--success-hints` option to watch mode that shows hints on exercise success + +#### Changed + +- `vecs2`: Renamed iterator variable bindings for clarify +- `lifetimes`: Changed order of book references +- `hashmaps2`: Clarified instructions in the todo block +- Moved lifetime exercises before test exercises (via the recommended book ordering) +- `options2`: Improved tests for layering options +- `modules2`: Added more information to the hint + +#### Fixed + +- `errors2`: Corrected a comment wording +- `iterators2`: Fixed a spelling mistake in the hint text +- `variables`: Wrapped the mut keyword with backticks for readability +- `move_semantics2`: Removed references to line numbers +- `cow1`: Clarified the `owned_no_mutation` comments +- `options3`: Changed exercise to panic when no match is found +- `rustlings lsp` now generates absolute paths, which should fix VSCode `rust-analyzer` usage on Windows + +#### Housekeeping + +- Added a markdown linter to run on GitHub actions +- Split quick installation section into two code blocks + ## 5.4.1 (2023-03-10) diff --git a/Cargo.lock b/Cargo.lock index 192a4ac07b..16f771c77f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -441,7 +441,7 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustlings" -version = "5.4.1" +version = "5.5.0" dependencies = [ "argh", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index d22816cab7..e603bbf07f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustlings" -version = "5.4.1" +version = "5.5.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", diff --git a/README.md b/README.md index be470fdbb7..92daa332d3 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1) -git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) +git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings # if nix version > 2.3 nix develop @@ -74,8 +74,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1) -git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) +git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/flake.nix b/flake.nix index 5815a92044..c874919734 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; - version = "5.4.1"; + version = "5.5.0"; buildInputs = cargoBuildInputs; diff --git a/src/main.rs b/src/main.rs index 74e8161b26..feb673da4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ mod run; mod verify; // In sync with crate version -const VERSION: &str = "5.4.1"; +const VERSION: &str = "5.5.0"; #[derive(FromArgs, PartialEq, Debug)] /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code @@ -352,7 +352,11 @@ enum WatchStatus { Unfinished, } -fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> notify::Result { +fn watch( + exercises: &[Exercise], + verbose: bool, + success_hints: bool, +) -> notify::Result { /* Clears the terminal with an ANSI escape code. Works in UNIX and newer Windows terminals. */ fn clear_screen() { @@ -368,7 +372,12 @@ fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> notify:: clear_screen(); let to_owned_hint = |t: &Exercise| t.hint.to_owned(); - let failed_exercise_hint = match verify(exercises.iter(), (0, exercises.len()), verbose, success_hints) { + let failed_exercise_hint = match verify( + exercises.iter(), + (0, exercises.len()), + verbose, + success_hints, + ) { Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))), }; @@ -390,7 +399,12 @@ fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> notify:: ); let num_done = exercises.iter().filter(|e| e.looks_done()).count(); clear_screen(); - match verify(pending_exercises, (num_done, exercises.len()), verbose, success_hints) { + match verify( + pending_exercises, + (num_done, exercises.len()), + verbose, + success_hints, + ) { Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => { let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap(); From 19cee732094c59669f29ce941ad87f5a90af6bf8 Mon Sep 17 00:00:00 2001 From: liv Date: Wed, 17 May 2023 16:56:01 +0200 Subject: [PATCH 0265/1432] chore: set up oranda Sets up oranda so we can get nice website things for free. Some caveats we have right now: - Absolutely manual, I have to do `oranda build` and the deploy manually right now - I had to pop the Readme into a new Markdown file because the first header in there was looking very strange --- .gitignore | 4 ++ Cargo.toml | 1 + oranda.json | 11 ++++ website.md | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+) create mode 100644 oranda.json create mode 100644 website.md diff --git a/.gitignore b/.gitignore index c14f9227aa..88bf2b6c7b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,7 @@ rust-project.json !.vscode/extensions.json *.iml *.o +public/ + +# Local Netlify folder +.netlify diff --git a/Cargo.toml b/Cargo.toml index e603bbf07f..498bbf9b88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "rustlings" +description = "Small exercises to get you used to reading and writing Rust code!" version = "5.5.0" authors = [ "Liv ", diff --git a/oranda.json b/oranda.json new file mode 100644 index 0000000000..503653a41c --- /dev/null +++ b/oranda.json @@ -0,0 +1,11 @@ +{ + "homepage": "https://rustlings.cool", + "readme_path": "website.md", + "repository": "https://github.com/rust-lang/rustlings", + "analytics": { + "plausible": { + "domain": "rustlings.cool" + } + }, + "changelog": true +} diff --git a/website.md b/website.md new file mode 100644 index 0000000000..92bedc8c27 --- /dev/null +++ b/website.md @@ -0,0 +1,176 @@ +Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! + +Alternatively, for a first-time Rust learner, there are several other resources: + +- [The Book](https://doc.rust-lang.org/book/index.html) - The most comprehensive resource for learning Rust, but a bit theoretical sometimes. You will be using this along with Rustlings! +- [Rust By Example](https://doc.rust-lang.org/rust-by-example/index.html) - Learn Rust by solving little exercises! It's almost like `rustlings`, but online + +## Getting Started + +_Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._ +_Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt install gcc`. Yum: `sudo yum -y install gcc`._ + +You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager. + +## MacOS/Linux + +Just run: + +```bash +curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash +``` + +Or if you want it to be installed to a different path: + +```bash +curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -s mypath/ +``` + +This will install Rustlings and give you access to the `rustlings` command. Run it to get started! + +### Nix + +Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. + +```bash +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) +git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings +cd rustlings +# if nix version > 2.3 +nix develop +# if nix version <= 2.3 +nix-shell +``` + +## Windows + +In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`: + +```ps1 +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` + +Then, you can run: + +```ps1 +Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 +``` + +To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors. + +If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus. + +## Browser + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) + +[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=main) + +## Manually + +Basically: Clone the repository at the latest tag, run `cargo install --path .`. + +```bash +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) +git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings +cd rustlings +cargo install --force --path . +``` + +If there are installation errors, ensure that your toolchain is up to date. For the latest, run: + +```bash +rustup update +``` + +Then, same as above, run `rustlings` to get started. + +## Doing exercises + +The exercises are sorted by topic and can be found in the subdirectory `rustlings/exercises/`. For every topic there is an additional README file with some resources to get you started on the topic. We really recommend that you have a look at them before you start. + +The task is simple. Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! Some exercises are also run as tests, but rustlings handles them all the same. To run the exercises in the recommended order, execute: + +```bash +rustlings watch +``` + +This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. If you want to only run it once, you can use: + +```bash +rustlings verify +``` + +This will do the same as watch, but it'll quit after running. + +In case you want to go by your own order, or want to only verify a single exercise, you can run: + +```bash +rustlings run myExercise1 +``` + +Or simply use the following command to run the next unsolved exercise in the course: + +```bash +rustlings run next +``` + +In case you get stuck, you can run the following command to get a hint for your +exercise: + +```bash +rustlings hint myExercise1 +``` + +You can also get the hint for the next unsolved exercise with the following command: + +```bash +rustlings hint next +``` + +To check your progress, you can run the following command: + +```bash +rustlings list +``` + +## Testing yourself + +After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. These quizzes are found in `exercises/quizN.rs`. + +## Enabling `rust-analyzer` + +Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise. + +## Continuing On + +Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. + +## Uninstalling Rustlings + +If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created +for you: + +```bash +rm -rf rustlings # or your custom folder name, if you chose and or renamed it +``` + +Second, run `cargo uninstall` to remove the `rustlings` binary: + +```bash +cargo uninstall rustlings +``` + +Now you should be done! + +## Contributing + +See [CONTRIBUTING.md](./CONTRIBUTING.md). + +Development-focused discussion about Rustlings happens in the [**rustlings** stream](https://rust-lang.zulipchat.com/#narrow/stream/334454-rustlings) +on the [Rust Project Zulip](https://rust-lang.zulipchat.com). Feel free to start a new thread there +if you have ideas or suggestions! + +## Contributors ✨ + +Thanks goes to the wonderful people listed in [AUTHORS.md](./AUTHORS.md) πŸŽ‰ From 0667ee7c4d2af44c98aff8db96887ddf40055c49 Mon Sep 17 00:00:00 2001 From: liv Date: Wed, 17 May 2023 17:11:49 +0200 Subject: [PATCH 0266/1432] fix: add oranda-hide class to readme --- README.md | 4 ++ oranda.json | 1 - website.md | 176 ---------------------------------------------------- 3 files changed, 4 insertions(+), 177 deletions(-) delete mode 100644 website.md diff --git a/README.md b/README.md index 92daa332d3..a6060a73bd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ +
+ # rustlings πŸ¦€β€οΈ +
+ Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! _...looking for the old, web-based version of Rustlings? Try [here](https://github.com/rust-lang/rustlings/tree/rustlings-1)_ diff --git a/oranda.json b/oranda.json index 503653a41c..08cc234d68 100644 --- a/oranda.json +++ b/oranda.json @@ -1,6 +1,5 @@ { "homepage": "https://rustlings.cool", - "readme_path": "website.md", "repository": "https://github.com/rust-lang/rustlings", "analytics": { "plausible": { diff --git a/website.md b/website.md deleted file mode 100644 index 92bedc8c27..0000000000 --- a/website.md +++ /dev/null @@ -1,176 +0,0 @@ -Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! - -Alternatively, for a first-time Rust learner, there are several other resources: - -- [The Book](https://doc.rust-lang.org/book/index.html) - The most comprehensive resource for learning Rust, but a bit theoretical sometimes. You will be using this along with Rustlings! -- [Rust By Example](https://doc.rust-lang.org/rust-by-example/index.html) - Learn Rust by solving little exercises! It's almost like `rustlings`, but online - -## Getting Started - -_Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._ -_Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt install gcc`. Yum: `sudo yum -y install gcc`._ - -You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager. - -## MacOS/Linux - -Just run: - -```bash -curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -``` - -Or if you want it to be installed to a different path: - -```bash -curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -s mypath/ -``` - -This will install Rustlings and give you access to the `rustlings` command. Run it to get started! - -### Nix - -Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. - -```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) -git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings -cd rustlings -# if nix version > 2.3 -nix develop -# if nix version <= 2.3 -nix-shell -``` - -## Windows - -In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`: - -```ps1 -Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -``` - -Then, you can run: - -```ps1 -Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 -``` - -To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors. - -If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus. - -## Browser - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) - -[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=main) - -## Manually - -Basically: Clone the repository at the latest tag, run `cargo install --path .`. - -```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) -git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings -cd rustlings -cargo install --force --path . -``` - -If there are installation errors, ensure that your toolchain is up to date. For the latest, run: - -```bash -rustup update -``` - -Then, same as above, run `rustlings` to get started. - -## Doing exercises - -The exercises are sorted by topic and can be found in the subdirectory `rustlings/exercises/`. For every topic there is an additional README file with some resources to get you started on the topic. We really recommend that you have a look at them before you start. - -The task is simple. Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! Some exercises are also run as tests, but rustlings handles them all the same. To run the exercises in the recommended order, execute: - -```bash -rustlings watch -``` - -This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. If you want to only run it once, you can use: - -```bash -rustlings verify -``` - -This will do the same as watch, but it'll quit after running. - -In case you want to go by your own order, or want to only verify a single exercise, you can run: - -```bash -rustlings run myExercise1 -``` - -Or simply use the following command to run the next unsolved exercise in the course: - -```bash -rustlings run next -``` - -In case you get stuck, you can run the following command to get a hint for your -exercise: - -```bash -rustlings hint myExercise1 -``` - -You can also get the hint for the next unsolved exercise with the following command: - -```bash -rustlings hint next -``` - -To check your progress, you can run the following command: - -```bash -rustlings list -``` - -## Testing yourself - -After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. These quizzes are found in `exercises/quizN.rs`. - -## Enabling `rust-analyzer` - -Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise. - -## Continuing On - -Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. - -## Uninstalling Rustlings - -If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created -for you: - -```bash -rm -rf rustlings # or your custom folder name, if you chose and or renamed it -``` - -Second, run `cargo uninstall` to remove the `rustlings` binary: - -```bash -cargo uninstall rustlings -``` - -Now you should be done! - -## Contributing - -See [CONTRIBUTING.md](./CONTRIBUTING.md). - -Development-focused discussion about Rustlings happens in the [**rustlings** stream](https://rust-lang.zulipchat.com/#narrow/stream/334454-rustlings) -on the [Rust Project Zulip](https://rust-lang.zulipchat.com). Feel free to start a new thread there -if you have ideas or suggestions! - -## Contributors ✨ - -Thanks goes to the wonderful people listed in [AUTHORS.md](./AUTHORS.md) πŸŽ‰ From 2d544f18b53102a11bc80be4e986c7f52f5262de Mon Sep 17 00:00:00 2001 From: liv Date: Wed, 17 May 2023 21:04:32 +0200 Subject: [PATCH 0267/1432] fix: revert back to using relative paths --- src/project.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/project.rs b/src/project.rs index 7865d92796..ebebe27d3a 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,9 +1,9 @@ use glob::glob; use serde::{Deserialize, Serialize}; +use std::env; use std::error::Error; use std::path::PathBuf; use std::process::Command; -use std::{env, fs}; /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file @@ -42,9 +42,8 @@ impl RustAnalyzerProject { fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box> { if let Some(ext) = path.extension() { if ext == "rs" { - let abspath = fs::canonicalize(path)?; self.crates.push(Crate { - root_module: abspath.display().to_string(), + root_module: path.display().to_string(), edition: "2021".to_string(), deps: Vec::new(), // This allows rust_analyzer to work inside #[test] blocks From f2de12aa3443af2e989a045d9035294184d36eee Mon Sep 17 00:00:00 2001 From: liv Date: Wed, 17 May 2023 21:05:51 +0200 Subject: [PATCH 0268/1432] release: 5.5.1 --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 8 ++++---- flake.nix | 2 +- src/main.rs | 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d6440648..a802faaff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ + +## 5.5.1 (2023-05-17) + +#### Fixed + +- Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix. + ## 5.5.0 (2023-05-17) diff --git a/Cargo.lock b/Cargo.lock index 16f771c77f..a09d98f7ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -441,7 +441,7 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustlings" -version = "5.5.0" +version = "5.5.1" dependencies = [ "argh", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 498bbf9b88..eca091f429 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" -version = "5.5.0" +version = "5.5.1" authors = [ "Liv ", "Carol (Nichols || Goulding) ", diff --git a/README.md b/README.md index a6060a73bd..12bd3925f6 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) -git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1) +git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings # if nix version > 2.3 nix develop @@ -78,8 +78,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.0) -git clone -b 5.5.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1) +git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/flake.nix b/flake.nix index c874919734..5dbca5c2ac 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; - version = "5.5.0"; + version = "5.5.1"; buildInputs = cargoBuildInputs; diff --git a/src/main.rs b/src/main.rs index feb673da4e..0a9af2ec09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ mod run; mod verify; // In sync with crate version -const VERSION: &str = "5.5.0"; +const VERSION: &str = "5.5.1"; #[derive(FromArgs, PartialEq, Debug)] /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code From a7257a1d1ef59642f692e2638085ec00a6f2f391 Mon Sep 17 00:00:00 2001 From: b1ue64 <77976308+b1ue64@users.noreply.github.com> Date: Sat, 20 May 2023 16:38:33 -0400 Subject: [PATCH 0269/1432] feat(iterators5): remove outdated part of hint --- info.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/info.toml b/info.toml index ac04fc7dac..2add5f0ccf 100644 --- a/info.toml +++ b/info.toml @@ -897,9 +897,6 @@ hint = """ The documentation for the std::iter::Iterator trait contains numerous methods that would be helpful here. -Return 0 from count_collection_iterator to make the code compile in order to -test count_iterator. - The collection variable in count_collection_iterator is a slice of HashMaps. It needs to be converted into an iterator in order to use the iterator methods. From 8e62346f8625965f1dac9603f3297e726670b3d3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 21 May 2023 09:39:25 +0000 Subject: [PATCH 0270/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index a27c60e9e0..e0631ce879 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -300,6 +300,9 @@ authors. lionel-rowe
lionel-rowe

πŸ–‹ Ben
Ben

πŸ–‹ + + b1ue64
b1ue64

πŸ–‹ + From 15ad02c984ef355a451189b2708b93112afc1840 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 21 May 2023 09:39:26 +0000 Subject: [PATCH 0271/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 1b9678e0e0..7978d245be 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2118,6 +2118,15 @@ "contributions": [ "content" ] + }, + { + "login": "b1ue64", + "name": "b1ue64", + "avatar_url": "https://avatars.githubusercontent.com/u/77976308?v=4", + "profile": "https://github.com/b1ue64", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From e35a84fcb3f448aa3c455fab8c3a6a13fd47e606 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 22 May 2023 11:09:57 +0200 Subject: [PATCH 0272/1432] chore(oranda): don't generate changelog pages --- oranda.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oranda.json b/oranda.json index 08cc234d68..18ef8c68a1 100644 --- a/oranda.json +++ b/oranda.json @@ -6,5 +6,5 @@ "domain": "rustlings.cool" } }, - "changelog": true + "changelog": false } From 9604ab66218a83710e0da1152ec75b5574f051a8 Mon Sep 17 00:00:00 2001 From: lazywalker Date: Tue, 23 May 2023 15:00:55 +0800 Subject: [PATCH 0273/1432] fix(exercises): use snake_case variables --- exercises/iterators/iterators5.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/exercises/iterators/iterators5.rs b/exercises/iterators/iterators5.rs index dcf0742ddd..d0fcc8cafa 100644 --- a/exercises/iterators/iterators5.rs +++ b/exercises/iterators/iterators5.rs @@ -79,11 +79,11 @@ mod tests { #[test] fn count_complete_equals_for() { let map = get_map(); - let progressStates = vec![Progress::Complete, Progress::Some, Progress::None]; - for progressState in progressStates { + let progress_states = vec![Progress::Complete, Progress::Some, Progress::None]; + for progress_state in progress_states { assert_eq!( - count_for(&map, progressState), - count_iterator(&map, progressState) + count_for(&map, progress_state), + count_iterator(&map, progress_state) ); } } @@ -111,13 +111,13 @@ mod tests { #[test] fn count_collection_equals_for() { - let progressStates = vec![Progress::Complete, Progress::Some, Progress::None]; + let progress_states = vec![Progress::Complete, Progress::Some, Progress::None]; let collection = get_vec_map(); - for progressState in progressStates { + for progress_state in progress_states { assert_eq!( - count_collection_for(&collection, progressState), - count_collection_iterator(&collection, progressState) + count_collection_for(&collection, progress_state), + count_collection_iterator(&collection, progress_state) ); } } From b9cb9311672babb938127385a102a645d86e4cac Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 09:05:58 +0000 Subject: [PATCH 0274/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index e0631ce879..50a477c087 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -302,6 +302,7 @@ authors. b1ue64
b1ue64

πŸ–‹ + lazywalker
lazywalker

πŸ–‹ From 33224370eb98da79a2a0b1d6a470cfa6edab83f2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 09:05:59 +0000 Subject: [PATCH 0275/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7978d245be..14ec3a9850 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2127,6 +2127,15 @@ "contributions": [ "content" ] + }, + { + "login": "lazywalker", + "name": "lazywalker", + "avatar_url": "https://avatars.githubusercontent.com/u/53956?v=4", + "profile": "https://github.com/lazywalker", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 7eef5d15eef780f93e22b1b4e0185f7708219ea0 Mon Sep 17 00:00:00 2001 From: Robert Fry Date: Mon, 29 May 2023 18:39:08 +0100 Subject: [PATCH 0276/1432] docs: cleanup the explanation paragraphs at the start of each exercise. --- exercises/clippy/clippy1.rs | 13 +++--- exercises/clippy/clippy2.rs | 4 +- exercises/clippy/clippy3.rs | 3 ++ exercises/conversions/as_ref_mut.rs | 12 ++++-- exercises/conversions/from_into.rs | 40 +++++++++++-------- exercises/conversions/from_str.rs | 30 ++++++++------ exercises/conversions/try_from_into.rs | 27 +++++++------ exercises/conversions/using_as.rs | 16 +++++--- exercises/enums/enums1.rs | 1 + exercises/enums/enums2.rs | 4 +- exercises/enums/enums3.rs | 11 +++-- exercises/error_handling/errors1.rs | 14 ++++--- exercises/error_handling/errors2.rs | 30 +++++++------- exercises/error_handling/errors3.rs | 5 ++- exercises/error_handling/errors4.rs | 4 +- exercises/error_handling/errors5.rs | 36 ++++++++++------- exercises/error_handling/errors6.rs | 13 +++--- exercises/functions/functions1.rs | 4 +- exercises/functions/functions2.rs | 4 +- exercises/functions/functions3.rs | 4 +- exercises/functions/functions4.rs | 15 +++---- exercises/functions/functions5.rs | 4 +- exercises/generics/generics1.rs | 11 +++-- exercises/generics/generics2.rs | 7 +++- exercises/hashmaps/hashmaps1.rs | 15 +++---- exercises/hashmaps/hashmaps2.rs | 27 +++++++------ exercises/hashmaps/hashmaps3.rs | 24 +++++------ exercises/if/if1.rs | 1 + exercises/if/if2.rs | 3 +- exercises/intro/intro1.rs | 12 ++++-- exercises/intro/intro2.rs | 5 ++- exercises/iterators/iterators1.rs | 11 ++--- exercises/iterators/iterators2.rs | 5 ++- exercises/iterators/iterators3.rs | 15 ++++--- exercises/iterators/iterators4.rs | 4 +- exercises/iterators/iterators5.rs | 5 ++- exercises/lifetimes/lifetimes1.rs | 9 +++-- exercises/lifetimes/lifetimes2.rs | 8 ++-- exercises/lifetimes/lifetimes3.rs | 3 +- exercises/macros/macros1.rs | 4 +- exercises/macros/macros2.rs | 4 +- exercises/macros/macros3.rs | 5 ++- exercises/macros/macros4.rs | 4 +- exercises/modules/modules1.rs | 4 +- exercises/modules/modules2.rs | 10 +++-- exercises/modules/modules3.rs | 13 +++--- exercises/move_semantics/move_semantics1.rs | 4 +- exercises/move_semantics/move_semantics2.rs | 6 ++- exercises/move_semantics/move_semantics3.rs | 9 +++-- exercises/move_semantics/move_semantics4.rs | 11 +++-- exercises/move_semantics/move_semantics5.rs | 9 +++-- exercises/move_semantics/move_semantics6.rs | 5 ++- exercises/options/options1.rs | 12 ++++-- exercises/options/options2.rs | 9 +++-- exercises/options/options3.rs | 4 +- exercises/primitive_types/primitive_types1.rs | 8 +++- exercises/primitive_types/primitive_types2.rs | 8 +++- exercises/primitive_types/primitive_types3.rs | 5 ++- exercises/primitive_types/primitive_types4.rs | 5 ++- exercises/primitive_types/primitive_types5.rs | 5 ++- exercises/primitive_types/primitive_types6.rs | 9 +++-- exercises/quiz1.rs | 9 +++-- exercises/quiz2.rs | 10 +++-- exercises/quiz3.rs | 20 +++++----- exercises/smart_pointers/arc1.rs | 19 +++++---- exercises/smart_pointers/box1.rs | 16 ++++---- exercises/smart_pointers/cow1.rs | 29 ++++++++------ exercises/smart_pointers/rc1.rs | 15 ++++--- exercises/strings/strings1.rs | 5 ++- exercises/strings/strings2.rs | 5 ++- exercises/strings/strings3.rs | 4 +- exercises/strings/strings4.rs | 3 +- exercises/structs/structs1.rs | 5 ++- exercises/structs/structs2.rs | 5 ++- exercises/structs/structs3.rs | 5 ++- exercises/tests/tests1.rs | 17 ++++---- exercises/tests/tests2.rs | 9 +++-- exercises/tests/tests3.rs | 9 +++-- exercises/tests/tests4.rs | 5 ++- exercises/threads/threads1.rs | 14 ++++--- exercises/threads/threads2.rs | 15 ++++--- exercises/threads/threads3.rs | 4 +- exercises/traits/traits1.rs | 12 +++--- exercises/traits/traits2.rs | 11 ++--- exercises/traits/traits3.rs | 9 +++-- exercises/traits/traits4.rs | 5 ++- exercises/traits/traits5.rs | 5 ++- exercises/variables/variables1.rs | 5 ++- exercises/variables/variables2.rs | 4 +- exercises/variables/variables3.rs | 4 +- exercises/variables/variables4.rs | 4 +- exercises/variables/variables5.rs | 4 +- exercises/variables/variables6.rs | 4 +- exercises/vecs/vecs1.rs | 7 +++- exercises/vecs/vecs2.rs | 5 ++- 95 files changed, 577 insertions(+), 337 deletions(-) diff --git a/exercises/clippy/clippy1.rs b/exercises/clippy/clippy1.rs index bad46891fe..95c0141f47 100644 --- a/exercises/clippy/clippy1.rs +++ b/exercises/clippy/clippy1.rs @@ -1,10 +1,13 @@ // 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. +// 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. // I AM NOT DONE diff --git a/exercises/clippy/clippy2.rs b/exercises/clippy/clippy2.rs index dac40dbef6..9b87a0b700 100644 --- a/exercises/clippy/clippy2.rs +++ b/exercises/clippy/clippy2.rs @@ -1,5 +1,7 @@ // clippy2.rs -// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/clippy/clippy3.rs b/exercises/clippy/clippy3.rs index b0159ebe66..35021f8412 100644 --- a/exercises/clippy/clippy3.rs +++ b/exercises/clippy/clippy3.rs @@ -1,5 +1,8 @@ // clippy3.rs +// // Here's a couple more easy Clippy fixes, so you can see its utility. +// +// Execute `rustlings hint clippy3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index e6a9d11472..626a36c45f 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -1,7 +1,11 @@ -// 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. +// 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. // I AM NOT DONE diff --git a/exercises/conversions/from_into.rs b/exercises/conversions/from_into.rs index 6c272c3bcd..aba471d92d 100644 --- a/exercises/conversions/from_into.rs +++ b/exercises/conversions/from_into.rs @@ -1,7 +1,11 @@ -// 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. +// 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 { @@ -20,20 +24,21 @@ impl Default for Person { } } -// Your task is to complete this implementation -// in order for the line `let p = Person::from("Mark,20")` to compile -// Please note that you'll need to parse the age component into a `usize` -// with something like `"4".parse::()`. The outcome of this needs to -// be handled appropriately. +// Your task is to complete this implementation in order for the line `let p = +// Person::from("Mark,20")` to compile Please note that you'll need to parse the +// age component into a `usize` with something like `"4".parse::()`. The +// outcome of this needs to be handled appropriately. // // Steps: -// 1. If the length of the provided string is 0, then return the default of Person -// 2. Split the given string on the commas present in it -// 3. Extract the first element from the split operation and use it as the name -// 4. If the name is empty, then return the default of Person -// 5. Extract the other element from the split operation and parse it into a `usize` as the age -// If while parsing the age, something goes wrong, then return the default of Person -// Otherwise, then return an instantiated Person object with the results +// 1. If the length of the provided string is 0, then return the default of +// Person. +// 2. Split the given string on the commas present in it. +// 3. Extract the first element from the split operation and use it as the name. +// 4. If the name is empty, then return the default of Person. +// 5. Extract the other element from the split operation and parse it into a +// `usize` as the age. +// If while parsing the age, something goes wrong, then return the default of +// Person Otherwise, then return an instantiated Person object with the results // I AM NOT DONE @@ -77,7 +82,8 @@ mod tests { } #[test] fn test_bad_age() { - // Test that "Mark,twenty" will return the default person due to an error in parsing age + // Test that "Mark,twenty" will return the default person due to an + // error in parsing age let p = Person::from("Mark,twenty"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); diff --git a/exercises/conversions/from_str.rs b/exercises/conversions/from_str.rs index fe168159b4..34472c32cc 100644 --- a/exercises/conversions/from_str.rs +++ b/exercises/conversions/from_str.rs @@ -1,10 +1,13 @@ // 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. +// +// 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; @@ -33,15 +36,18 @@ enum ParsePersonError { // Steps: // 1. If the length of the provided string is 0, an error should be returned // 2. Split the given string on the commas present in it -// 3. Only 2 elements should be returned from the split, otherwise return an error +// 3. Only 2 elements should be returned from the split, otherwise return an +// error // 4. Extract the first element from the split operation and use it as the name -// 5. Extract the other element from the split operation and parse it into a `usize` as the age -// with something like `"4".parse::()` -// 6. If while extracting the name and the age something goes wrong, an error should be returned +// 5. Extract the other element from the split operation and parse it into a +// `usize` as the age with something like `"4".parse::()` +// 6. If while extracting the name and the age something goes wrong, an error +// should be returned // If everything goes well, then return a Result of a Person object // -// As an aside: `Box` implements `From<&'_ str>`. This means that if you want to return a -// string error message, you can do so via just using return `Err("my error message".into())`. +// As an aside: `Box` implements `From<&'_ str>`. This means that if +// you want to return a string error message, you can do so via just using +// return `Err("my error message".into())`. impl FromStr for Person { type Err = ParsePersonError; diff --git a/exercises/conversions/try_from_into.rs b/exercises/conversions/try_from_into.rs index fa98bc90da..32d6ef39e1 100644 --- a/exercises/conversions/try_from_into.rs +++ b/exercises/conversions/try_from_into.rs @@ -1,9 +1,13 @@ // 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. +// +// 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}; @@ -25,14 +29,13 @@ enum IntoColorError { // I AM NOT DONE -// Your task is to complete this implementation -// and return an Ok result of inner type Color. -// You need to create an implementation for a tuple of three integers, -// an array of three integers, and a slice of integers. +// Your task is to complete this implementation and return an Ok result of inner +// type Color. You need to create an implementation for a tuple of three +// integers, an array of three integers, and a slice of integers. // -// Note that the implementation for tuple and array will be checked at compile time, -// but the slice implementation needs to check the slice length! -// Also note that correct RGB color values must be integers in the 0..=255 range. +// Note that the implementation for tuple and array will be checked at compile +// time, but the slice implementation needs to check the slice length! Also note +// that correct RGB color values must be integers in the 0..=255 range. // Tuple implementation impl TryFrom<(i16, i16, i16)> for Color { diff --git a/exercises/conversions/using_as.rs b/exercises/conversions/using_as.rs index 8c9b7113d8..414cef3a05 100644 --- a/exercises/conversions/using_as.rs +++ b/exercises/conversions/using_as.rs @@ -1,10 +1,14 @@ -// 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. +// using_as.rs // -// 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. +// 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. // I AM NOT DONE diff --git a/exercises/enums/enums1.rs b/exercises/enums/enums1.rs index 511ba740e2..25525b252d 100644 --- a/exercises/enums/enums1.rs +++ b/exercises/enums/enums1.rs @@ -1,4 +1,5 @@ // enums1.rs +// // No hints this time! ;) // I AM NOT DONE diff --git a/exercises/enums/enums2.rs b/exercises/enums/enums2.rs index 167a6b2e91..df93fe0f15 100644 --- a/exercises/enums/enums2.rs +++ b/exercises/enums/enums2.rs @@ -1,5 +1,7 @@ // enums2.rs -// Execute `rustlings hint enums2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint enums2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index a2a9d58684..69ba072866 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -1,6 +1,9 @@ // enums3.rs +// // Address all the TODOs to make the tests pass! -// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE @@ -37,8 +40,10 @@ impl State { } fn process(&mut self, message: Message) { - // TODO: create a match expression to process the different message variants - // Remember: When passing a tuple as a function argument, you'll need extra parentheses: fn function((t, u, p, l, e)) + // TODO: create a match expression to process the different message + // variants + // Remember: When passing a tuple as a function argument, you'll need + // extra parentheses: fn function((t, u, p, l, e)) } } diff --git a/exercises/error_handling/errors1.rs b/exercises/error_handling/errors1.rs index bcee9723e9..0ba59a57a6 100644 --- a/exercises/error_handling/errors1.rs +++ b/exercises/error_handling/errors1.rs @@ -1,9 +1,13 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs index 6971fcfb50..d86f326d09 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -1,21 +1,23 @@ // 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 the `total_cost` function will calculate the total cost of the tokens. -// Since the player typed in the quantity, though, we get it as a string-- and -// they might have typed anything, not just numbers! - +// token. A player of the game will type in how many items they want to buy, and +// the `total_cost` function will calculate the total cost of the tokens. Since +// the player typed in the quantity, though, we get it as a string-- and they +// might have typed anything, not just numbers! +// // Right now, this function isn't handling the error case at all (and isn't -// handling the success case properly either). What we want to do is: -// if we call the `parse` function on a string that is not a number, that -// function will return a `ParseIntError`, and in that case, we want to -// immediately return that error from our function and not try to multiply -// and add. - -// 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. +// handling the success case properly either). What we want to do is: if we call +// the `parse` function on a string that is not a number, that function will +// return a `ParseIntError`, and in that case, we want to immediately return +// that error from our function and not try to multiply and add. +// +// 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. // I AM NOT DONE diff --git a/exercises/error_handling/errors3.rs b/exercises/error_handling/errors3.rs index a2d2d1901d..d42d3b17ca 100644 --- a/exercises/error_handling/errors3.rs +++ b/exercises/error_handling/errors3.rs @@ -1,8 +1,11 @@ // 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. +// +// Execute `rustlings hint errors3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/error_handling/errors4.rs b/exercises/error_handling/errors4.rs index 0efe8ccd5a..e04bff77ad 100644 --- a/exercises/error_handling/errors4.rs +++ b/exercises/error_handling/errors4.rs @@ -1,5 +1,7 @@ // errors4.rs -// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs index eb5506cb5f..92461a7e01 100644 --- a/exercises/error_handling/errors5.rs +++ b/exercises/error_handling/errors5.rs @@ -1,20 +1,26 @@ // 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 course, like `Box` and the -// `From` trait. It's not important to understand them in detail right now, but you can read ahead if you like. -// For now, think of the `Box` type as an "I want anything that does ???" type, which, given -// Rust's usual standards for runtime safety, should strike you as somewhat lenient! - -// In short, this particular use case for boxes is for when you want to own a value and you care only that it is a -// type which implements a particular trait. To do so, The Box is declared as of type Box where Trait is the trait -// the compiler looks for on any value used in that context. For this exercise, that context is the potential errors -// which can be returned in a Result. - -// 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. +// +// This exercise uses some concepts that we won't get to until later in the +// course, like `Box` and the `From` trait. It's not important to understand +// them in detail right now, but you can read ahead if you like. For now, think +// of the `Box` type as an "I want anything that does ???" type, which, +// given Rust's usual standards for runtime safety, should strike you as +// somewhat lenient! +// +// In short, this particular use case for boxes is for when you want to own a +// value and you care only that it is a type which implements a particular +// trait. To do so, The Box is declared as of type Box where Trait is +// the trait the compiler looks for on any value used in that context. For this +// exercise, that context is the potential errors which can be returned in a +// Result. +// +// 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. // I AM NOT DONE diff --git a/exercises/error_handling/errors6.rs b/exercises/error_handling/errors6.rs index 8097b4902a..aaf0948ef7 100644 --- a/exercises/error_handling/errors6.rs +++ b/exercises/error_handling/errors6.rs @@ -1,12 +1,13 @@ // errors6.rs - +// // Using catch-all error types like `Box` isn't recommended // for library code, where callers might want to make decisions based on the -// error content, instead of printing it out or propagating it further. Here, -// we define a custom error type to make it possible for callers to decide -// what to do next when our function returns an error. - -// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a hint. +// 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. // I AM NOT DONE diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 03d8af7020..40ed9a0753 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,5 +1,7 @@ // functions1.rs -// Execute `rustlings hint functions1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint functions1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs index 7d40a578c7..5154f34d8c 100644 --- a/exercises/functions/functions2.rs +++ b/exercises/functions/functions2.rs @@ -1,5 +1,7 @@ // functions2.rs -// Execute `rustlings hint functions2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint functions2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs index 3b9e585b6c..74f44d6d08 100644 --- a/exercises/functions/functions3.rs +++ b/exercises/functions/functions3.rs @@ -1,5 +1,7 @@ // functions3.rs -// Execute `rustlings hint functions3` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint functions3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs index 65d5be4ff6..77c4b2aa3b 100644 --- a/exercises/functions/functions4.rs +++ b/exercises/functions/functions4.rs @@ -1,11 +1,12 @@ // functions4.rs -// Execute `rustlings hint functions4` or use the `hint` watch subcommand for a hint. - -// 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!) +// +// 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. // I AM NOT DONE diff --git a/exercises/functions/functions5.rs b/exercises/functions/functions5.rs index 5d7629617f..f1b63f480b 100644 --- a/exercises/functions/functions5.rs +++ b/exercises/functions/functions5.rs @@ -1,5 +1,7 @@ // functions5.rs -// Execute `rustlings hint functions5` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint functions5` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/generics/generics1.rs b/exercises/generics/generics1.rs index 4c34ae47d6..35c1d2feed 100644 --- a/exercises/generics/generics1.rs +++ b/exercises/generics/generics1.rs @@ -1,7 +1,10 @@ -// 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. +// 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. // I AM NOT DONE diff --git a/exercises/generics/generics2.rs b/exercises/generics/generics2.rs index aedbd55c94..074cd938c6 100644 --- a/exercises/generics/generics2.rs +++ b/exercises/generics/generics2.rs @@ -1,7 +1,10 @@ +// 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. +// +// Execute `rustlings hint generics2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/hashmaps/hashmaps1.rs b/exercises/hashmaps/hashmaps1.rs index fd8dd2f82c..80829eaa6c 100644 --- a/exercises/hashmaps/hashmaps1.rs +++ b/exercises/hashmaps/hashmaps1.rs @@ -1,14 +1,15 @@ // 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 types of fruits (e.g apple, banana, mango) -// in the basket and the total count of all the fruits should be at -// least five. +// +// 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 +// types of fruits (e.g apple, banana, mango) in the basket and the total count +// 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. +// Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/hashmaps/hashmaps2.rs b/exercises/hashmaps/hashmaps2.rs index a4f069a82b..f9138486b9 100644 --- a/exercises/hashmaps/hashmaps2.rs +++ b/exercises/hashmaps/hashmaps2.rs @@ -1,17 +1,18 @@ // 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 many of that particular fruit we have collected. -// Three types of fruits - Apple (4), Mango (2) and Lychee (5) are already -// in the basket hash map. -// You must add fruit to the basket so that there is at least -// one of each kind and more than 11 in total - we have a lot of mouths to feed. -// You are not allowed to insert any more of these fruits! +// +// 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 +// many of that particular fruit we have collected. Three types of fruits - +// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You +// must add fruit to the basket so that there is at least one of each kind and +// more than 11 in total - we have a lot of mouths to feed. You are not allowed +// 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. +// Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE @@ -36,9 +37,9 @@ fn fruit_basket(basket: &mut HashMap) { ]; for fruit in fruit_kinds { - // TODO: Insert new fruits if they are not already present in the basket. - // Note that you are not allowed to put any type of fruit that's already - // present! + // TODO: Insert new fruits if they are not already present in the + // basket. Note that you are not allowed to put any type of fruit that's + // already present! } } diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/hashmaps/hashmaps3.rs index ad3baa688a..4b48e09300 100644 --- a/exercises/hashmaps/hashmaps3.rs +++ b/exercises/hashmaps/hashmaps3.rs @@ -1,18 +1,18 @@ // hashmaps3.rs - -// A list of scores (one per line) of a soccer match is given. Each line -// is of the form : -// ,,, +// +// A list of scores (one per line) of a soccer match is given. Each line is of +// the form : ",,," // Example: England,France,4,2 (England scored 4 goals, France 2). - -// You have to build a scores table containing the name of the team, goals -// the team scored, and 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. - +// +// You have to build a scores table containing the name of the team, goals the +// team scored, and 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. +// +// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index 587e03f88a..d8108a0f9c 100644 --- a/exercises/if/if1.rs +++ b/exercises/if/if1.rs @@ -1,4 +1,5 @@ // if1.rs +// // Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/if/if2.rs b/exercises/if/if2.rs index effddbb6eb..f512f13ffc 100644 --- a/exercises/if/if2.rs +++ b/exercises/if/if2.rs @@ -1,7 +1,8 @@ // 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. // I AM NOT DONE diff --git a/exercises/intro/intro1.rs b/exercises/intro/intro1.rs index cfc55c306f..37fa011226 100644 --- a/exercises/intro/intro1.rs +++ b/exercises/intro/intro1.rs @@ -1,13 +1,17 @@ // intro1.rs +// // About this `I AM NOT DONE` thing: // We sometimes encourage you to keep trying things on a given exercise, even // after you already figured it out. If you got everything working and feel // ready for the next exercise, remove the `I AM NOT DONE` comment below. -// Execute `rustlings hint intro1` or use the `hint` watch subcommand for a hint. // -// If you're running this using `rustlings watch`: The exercise file will be reloaded -// when you change one of the lines below! Try adding a `println!` line, or try changing -// what it outputs in your terminal. Try removing a semicolon and see what happens! +// If you're running this using `rustlings watch`: The exercise file will be +// reloaded when you change one of the lines below! Try adding a `println!` +// line, or try changing what it outputs in your terminal. Try removing a +// semicolon and see what happens! +// +// Execute `rustlings hint intro1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/intro/intro2.rs b/exercises/intro/intro2.rs index efc1af2059..990b20f09c 100644 --- a/exercises/intro/intro2.rs +++ b/exercises/intro/intro2.rs @@ -1,6 +1,9 @@ // intro2.rs +// // Make the code print a greeting to the world. -// Execute `rustlings hint intro2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint intro2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/iterators/iterators1.rs b/exercises/iterators/iterators1.rs index f9cc3b399a..b3f698be35 100644 --- a/exercises/iterators/iterators1.rs +++ b/exercises/iterators/iterators1.rs @@ -1,12 +1,13 @@ // iterators1.rs // -// Make me compile by filling in the `???`s +// 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. // -// 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. +// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/iterators/iterators2.rs b/exercises/iterators/iterators2.rs index 29c53afb28..dda82a085f 100644 --- a/exercises/iterators/iterators2.rs +++ b/exercises/iterators/iterators2.rs @@ -1,7 +1,10 @@ // 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. +// +// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/iterators/iterators3.rs b/exercises/iterators/iterators3.rs index c97a625877..29fa23a3e3 100644 --- a/exercises/iterators/iterators3.rs +++ b/exercises/iterators/iterators3.rs @@ -1,10 +1,13 @@ // 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: +// +// 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. +// +// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE @@ -26,14 +29,16 @@ pub fn divide(a: i32, b: i32) -> Result { todo!(); } -// Complete the function and return a value of the correct type so the test passes. +// Complete the function and return a value of the correct type so the test +// passes. // Desired output: Ok([1, 11, 1426, 3]) fn result_with_list() -> () { let numbers = vec![27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); } -// Complete the function and return a value of the correct type so the test passes. +// Complete the function and return a value of the correct type so the test +// passes. // Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)] fn list_of_results() -> () { let numbers = vec![27, 297, 38502, 81]; diff --git a/exercises/iterators/iterators4.rs b/exercises/iterators/iterators4.rs index a02470ec14..79e1692bac 100644 --- a/exercises/iterators/iterators4.rs +++ b/exercises/iterators/iterators4.rs @@ -1,5 +1,7 @@ // iterators4.rs -// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/iterators/iterators5.rs b/exercises/iterators/iterators5.rs index d0fcc8cafa..a062ee4c74 100644 --- a/exercises/iterators/iterators5.rs +++ b/exercises/iterators/iterators5.rs @@ -1,4 +1,5 @@ // 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 @@ -6,7 +7,9 @@ // 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. +// +// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/lifetimes/lifetimes1.rs b/exercises/lifetimes/lifetimes1.rs index 0236470dbd..87bde490cc 100644 --- a/exercises/lifetimes/lifetimes1.rs +++ b/exercises/lifetimes/lifetimes1.rs @@ -1,11 +1,12 @@ // 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? +// 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. +// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/lifetimes/lifetimes2.rs b/exercises/lifetimes/lifetimes2.rs index b48feabcfa..4f3d8c185b 100644 --- a/exercises/lifetimes/lifetimes2.rs +++ b/exercises/lifetimes/lifetimes2.rs @@ -1,10 +1,10 @@ // 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? +// 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. +// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/lifetimes/lifetimes3.rs b/exercises/lifetimes/lifetimes3.rs index ea48370876..9c59f9c021 100644 --- a/exercises/lifetimes/lifetimes3.rs +++ b/exercises/lifetimes/lifetimes3.rs @@ -2,7 +2,8 @@ // // Lifetimes are also needed when structs hold references. // -// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a hint. +// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/macros/macros1.rs b/exercises/macros/macros1.rs index 634d0a7011..678de6eec2 100644 --- a/exercises/macros/macros1.rs +++ b/exercises/macros/macros1.rs @@ -1,5 +1,7 @@ // macros1.rs -// Execute `rustlings hint macros1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint macros1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/macros/macros2.rs b/exercises/macros/macros2.rs index f6092cab44..788fc16a92 100644 --- a/exercises/macros/macros2.rs +++ b/exercises/macros/macros2.rs @@ -1,5 +1,7 @@ // macros2.rs -// Execute `rustlings hint macros2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint macros2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/macros/macros3.rs b/exercises/macros/macros3.rs index 106f1c6dc8..b795c14939 100644 --- a/exercises/macros/macros3.rs +++ b/exercises/macros/macros3.rs @@ -1,6 +1,9 @@ // 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. +// +// Execute `rustlings hint macros3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index 4ee9803550..71b45a0955 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -1,5 +1,7 @@ // macros4.rs -// Execute `rustlings hint macros4` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint macros4` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/modules/modules1.rs b/exercises/modules/modules1.rs index 8dd0e40229..9eb5a48b72 100644 --- a/exercises/modules/modules1.rs +++ b/exercises/modules/modules1.rs @@ -1,5 +1,7 @@ // modules1.rs -// Execute `rustlings hint modules1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint modules1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/modules/modules2.rs b/exercises/modules/modules2.rs index c30a3897bb..0415454313 100644 --- a/exercises/modules/modules2.rs +++ b/exercises/modules/modules2.rs @@ -1,7 +1,11 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/modules/modules3.rs b/exercises/modules/modules3.rs index 35e07990ef..f2bb050389 100644 --- a/exercises/modules/modules3.rs +++ b/exercises/modules/modules3.rs @@ -1,9 +1,12 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index aac6dfc39c..710d20d8a2 100644 --- a/exercises/move_semantics/move_semantics1.rs +++ b/exercises/move_semantics/move_semantics1.rs @@ -1,5 +1,7 @@ // move_semantics1.rs -// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 93bb82efb9..5821b52b15 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,9 +1,11 @@ // move_semantics2.rs -// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. - +// // Expected output: // vec0 has length 3 content `[22, 44, 66]` // vec1 has length 4 content `[22, 44, 66, 88]` +// +// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index eaa30e3344..ea21493442 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -1,7 +1,10 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/move_semantics/move_semantics4.rs index 99834ec34e..75a3b6bd18 100644 --- a/exercises/move_semantics/move_semantics4.rs +++ b/exercises/move_semantics/move_semantics4.rs @@ -1,8 +1,11 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics5.rs b/exercises/move_semantics/move_semantics5.rs index 36eae127ac..68db09eb38 100644 --- a/exercises/move_semantics/move_semantics5.rs +++ b/exercises/move_semantics/move_semantics5.rs @@ -1,7 +1,10 @@ // move_semantics5.rs -// Make me compile only by reordering the lines in `main()`, but without -// adding, changing or removing any of them. -// Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand for a hint. +// +// Make me compile only by reordering the lines in `main()`, but without adding, +// changing or removing any of them. +// +// Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics6.rs b/exercises/move_semantics/move_semantics6.rs index eb52a848d3..cace4ca656 100644 --- a/exercises/move_semantics/move_semantics6.rs +++ b/exercises/move_semantics/move_semantics6.rs @@ -1,6 +1,9 @@ // move_semantics6.rs -// Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand for a hint. +// // You can't change anything except adding or removing references. +// +// Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/options/options1.rs b/exercises/options/options1.rs index 1f891b0eb2..e131b48b99 100644 --- a/exercises/options/options1.rs +++ b/exercises/options/options1.rs @@ -1,5 +1,7 @@ // options1.rs -// Execute `rustlings hint options1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint options1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE @@ -7,8 +9,9 @@ // If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them // all, so there'll be no more left :( fn maybe_icecream(time_of_day: u16) -> Option { - // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a value of 0 - // The Option output should gracefully handle cases where time_of_day > 23. + // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a + // value of 0 The Option output should gracefully handle cases where + // time_of_day > 23. // TODO: Complete the function body - remember to return an Option! ??? } @@ -28,7 +31,8 @@ mod tests { #[test] fn raw_value() { - // TODO: Fix this test. How do you get at the value contained in the Option? + // TODO: Fix this test. How do you get at the value contained in the + // Option? let icecreams = maybe_icecream(12); assert_eq!(icecreams, 5); } diff --git a/exercises/options/options2.rs b/exercises/options/options2.rs index 337c4261c5..4d998e7d02 100644 --- a/exercises/options/options2.rs +++ b/exercises/options/options2.rs @@ -1,5 +1,7 @@ // options2.rs -// Execute `rustlings hint options2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint options2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE @@ -27,8 +29,9 @@ mod tests { let mut cursor = range; - // TODO: make this a while let statement - remember that vector.pop also adds another layer of Option - // You can stack `Option`s into while let and if let + // TODO: make this a while let statement - remember that vector.pop also + // adds another layer of Option. You can stack `Option`s into + // while let and if let. integer = optional_integers.pop() { assert_eq!(integer, cursor); cursor -= 1; diff --git a/exercises/options/options3.rs b/exercises/options/options3.rs index 3ed76eebad..23c15eab80 100644 --- a/exercises/options/options3.rs +++ b/exercises/options/options3.rs @@ -1,5 +1,7 @@ // options3.rs -// Execute `rustlings hint options3` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint options3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types1.rs b/exercises/primitive_types/primitive_types1.rs index 091213925c..e1cf52a294 100644 --- a/exercises/primitive_types/primitive_types1.rs +++ b/exercises/primitive_types/primitive_types1.rs @@ -1,6 +1,10 @@ // 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! No hints, there's no +// tricks, just get used to typing these :) +// +// Execute `rustlings hint primitive_types1` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types2.rs b/exercises/primitive_types/primitive_types2.rs index 8730baab43..fcc9705aa8 100644 --- a/exercises/primitive_types/primitive_types2.rs +++ b/exercises/primitive_types/primitive_types2.rs @@ -1,6 +1,10 @@ // 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 :) +// +// Fill in the rest of the line that has code missing! No hints, there's no +// tricks, just get used to typing these :) +// +// Execute `rustlings hint primitive_types2` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs index fa7d019a44..06a7a621ae 100644 --- a/exercises/primitive_types/primitive_types3.rs +++ b/exercises/primitive_types/primitive_types3.rs @@ -1,6 +1,9 @@ // 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. +// +// Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs index 71fa243cfb..d44d8776fa 100644 --- a/exercises/primitive_types/primitive_types4.rs +++ b/exercises/primitive_types/primitive_types4.rs @@ -1,6 +1,9 @@ // 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. +// +// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs index 4fd9141fe6..f646986ea8 100644 --- a/exercises/primitive_types/primitive_types5.rs +++ b/exercises/primitive_types/primitive_types5.rs @@ -1,6 +1,9 @@ // 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. +// +// Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand +// for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs index ddf8b42351..07cc46c65a 100644 --- a/exercises/primitive_types/primitive_types6.rs +++ b/exercises/primitive_types/primitive_types6.rs @@ -1,7 +1,10 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index dbb5cdc9a1..a9904b8b17 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -1,14 +1,17 @@ // quiz1.rs +// // This is a quiz for the following sections: // - Variables // - Functions // - If - +// // Mary is buying apples. The price of an apple is calculated as follows: // - An apple costs 2 rustbucks. // - 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! +// Write a function that calculates the price of an order of apples given the +// quantity bought. No hints this time! +// +// No hints this time ;) // I AM NOT DONE diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 5c42dae083..29925cafcd 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -1,14 +1,15 @@ // quiz2.rs +// // This is a quiz for the following sections: // - Strings // - Vecs // - Move semantics // - Modules // - Enums - -// Let's build a little machine in the form of a function. -// As input, we're going to give a list of strings and commands. These commands -// determine what action is going to be applied to the string. It can either be: +// +// Let's build a little machine in the form of a function. As input, we're going +// to give a list of strings and commands. These commands determine what action +// is going to be applied to the string. It can either be: // - Uppercase the string // - Trim the string // - Append "bar" to the string a specified amount of times @@ -16,6 +17,7 @@ // - 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! // I AM NOT DONE diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs index 15dc46990e..3b01d31320 100644 --- a/exercises/quiz3.rs +++ b/exercises/quiz3.rs @@ -1,17 +1,19 @@ // quiz3.rs +// // This quiz tests: // - Generics // - Traits -// An imaginary magical school has a new report card generation system written in Rust! -// Currently the system only supports creating report cards where the student's grade -// is represented numerically (e.g. 1.0 -> 5.5). -// However, the school also issues alphabetical grades (A+ -> F-) and needs -// to be able to print both types of report card! - +// +// An imaginary magical school has a new report card generation system written +// in Rust! Currently the system only supports creating report cards where the +// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the +// school also issues alphabetical grades (A+ -> F-) and needs to be able to +// print both types of report card! +// // 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. - +// 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. // I AM NOT DONE diff --git a/exercises/smart_pointers/arc1.rs b/exercises/smart_pointers/arc1.rs index ffb306afb3..3526ddcb9a 100644 --- a/exercises/smart_pointers/arc1.rs +++ b/exercises/smart_pointers/arc1.rs @@ -1,21 +1,24 @@ // 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 going to get the sum of every eighth value, with an offset. +// +// 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 +// going to get the sum of every eighth value, with an offset. +// // The first thread (offset 0), will sum 0, 8, 16, ... // The second thread (offset 1), will sum 1, 9, 17, ... // The third thread (offset 2), will sum 2, 10, 18, ... // ... // The eighth thread (offset 7), will sum 7, 15, 23, ... - +// // Because we are using threads, our values need to be thread-safe. Therefore, // we are using Arc. We need to make a change in each of the two TODOs. - - +// // Make this code compile by filling in a value for `shared_numbers` where the // 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! +// 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. // I AM NOT DONE diff --git a/exercises/smart_pointers/box1.rs b/exercises/smart_pointers/box1.rs index 66cf00f329..513e7daa37 100644 --- a/exercises/smart_pointers/box1.rs +++ b/exercises/smart_pointers/box1.rs @@ -1,13 +1,15 @@ // 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 `Box` - a smart pointer used to store data on the heap, -// which also allows us to wrap a recursive type. +// 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 +// `Box` - a smart pointer used to store data on the heap, which also allows us +// to wrap a recursive type. // -// The recursive type we're implementing in this exercise is the `cons list` - a data structure -// frequently found in functional programming languages. Each item in a cons list contains two -// elements: the value of the current item and the next item. The last item is a value called `Nil`. +// The recursive type we're implementing in this exercise is the `cons list` - a +// data structure frequently found in functional programming languages. Each +// item in a cons list contains two elements: the value of the current item and +// the next item. The last item is a value called `Nil`. // // Step 1: use a `Box` in the enum definition to make the code compile // Step 2: create both empty and non-empty cons lists by replacing `todo!()` diff --git a/exercises/smart_pointers/cow1.rs b/exercises/smart_pointers/cow1.rs index bc5b28e56f..7ca916866a 100644 --- a/exercises/smart_pointers/cow1.rs +++ b/exercises/smart_pointers/cow1.rs @@ -1,12 +1,16 @@ // 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 required. -// The type is designed to work with general borrowed data via the Borrow trait. +// +// 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 +// required. The type is designed to work with general borrowed data via the +// Borrow trait. // // 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. +// 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. // I AM NOT DONE @@ -50,10 +54,9 @@ mod tests { #[test] fn owned_no_mutation() -> Result<(), &'static str> { - // We can also pass `slice` without `&` so Cow owns it directly. - // In this case no mutation occurs and thus also no clone, - // but the result is still owned because it was never borrowed - // or mutated. + // We can also pass `slice` without `&` so Cow owns it directly. In this + // case no mutation occurs and thus also no clone, but the result is + // still owned because it was never borrowed or mutated. let slice = vec![0, 1, 2]; let mut input = Cow::from(slice); match abs_all(&mut input) { @@ -63,9 +66,9 @@ mod tests { #[test] fn owned_mutation() -> Result<(), &'static str> { - // Of course this is also the case if a mutation does occur. - // In this case the call to `to_mut()` returns a reference to - // the same data as before. + // Of course this is also the case if a mutation does occur. In this + // case the call to `to_mut()` returns a reference to the same data as + // before. let slice = vec![-1, 0, 1]; let mut input = Cow::from(slice); match abs_all(&mut input) { diff --git a/exercises/smart_pointers/rc1.rs b/exercises/smart_pointers/rc1.rs index d62f36192f..ad3f1ce292 100644 --- a/exercises/smart_pointers/rc1.rs +++ b/exercises/smart_pointers/rc1.rs @@ -1,9 +1,14 @@ // rc1.rs -// In this exercise, we want to express the concept of multiple owners via the Rc type. -// This is a model of our solar system - there is a Sun type and multiple Planets. -// The Planets take ownership of the sun, indicating that they revolve around the sun. - -// Make this code compile by using the proper Rc primitives to express that the sun has multiple owners. +// +// In this exercise, we want to express the concept of multiple owners via the +// Rc type. This is a model of our solar system - there is a Sun type and +// multiple Planets. The Planets take ownership of the sun, indicating that they +// revolve around the sun. +// +// 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. // I AM NOT DONE diff --git a/exercises/strings/strings1.rs b/exercises/strings/strings1.rs index 0de86a1dbf..f50e1fa988 100644 --- a/exercises/strings/strings1.rs +++ b/exercises/strings/strings1.rs @@ -1,6 +1,9 @@ // strings1.rs +// // Make me compile without changing the function signature! -// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/strings/strings2.rs b/exercises/strings/strings2.rs index 0c48ec959d..4d95d16a16 100644 --- a/exercises/strings/strings2.rs +++ b/exercises/strings/strings2.rs @@ -1,6 +1,9 @@ // strings2.rs +// // Make me compile without changing the function signature! -// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/strings/strings3.rs b/exercises/strings/strings3.rs index e2353aecc9..b29f9325bc 100644 --- a/exercises/strings/strings3.rs +++ b/exercises/strings/strings3.rs @@ -1,5 +1,7 @@ // strings3.rs -// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/strings/strings4.rs b/exercises/strings/strings4.rs index c410b56252..e8c54acc55 100644 --- a/exercises/strings/strings4.rs +++ b/exercises/strings/strings4.rs @@ -1,9 +1,10 @@ // strings4.rs - +// // 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! // I AM NOT DONE diff --git a/exercises/structs/structs1.rs b/exercises/structs/structs1.rs index 0d91c469e5..5fa5821c5b 100644 --- a/exercises/structs/structs1.rs +++ b/exercises/structs/structs1.rs @@ -1,6 +1,9 @@ // structs1.rs +// // Address all the TODOs to make the tests pass! -// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/structs/structs2.rs b/exercises/structs/structs2.rs index 32e311fa9a..328567f03a 100644 --- a/exercises/structs/structs2.rs +++ b/exercises/structs/structs2.rs @@ -1,6 +1,9 @@ // structs2.rs +// // Address all the TODOs to make the tests pass! -// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs index 3536a4573e..4851317c5f 100644 --- a/exercises/structs/structs3.rs +++ b/exercises/structs/structs3.rs @@ -1,8 +1,11 @@ // 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. +// +// Execute `rustlings hint structs3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/tests/tests1.rs b/exercises/tests/tests1.rs index 8b6ea37469..810277ac3d 100644 --- a/exercises/tests/tests1.rs +++ b/exercises/tests/tests1.rs @@ -1,11 +1,14 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/tests/tests2.rs b/exercises/tests/tests2.rs index a5ac15b11f..f8024e9f22 100644 --- a/exercises/tests/tests2.rs +++ b/exercises/tests/tests2.rs @@ -1,7 +1,10 @@ // 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/tests/tests3.rs b/exercises/tests/tests3.rs index 196a81a0e5..4013e38415 100644 --- a/exercises/tests/tests3.rs +++ b/exercises/tests/tests3.rs @@ -1,8 +1,11 @@ // 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. +// 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. // I AM NOT DONE diff --git a/exercises/tests/tests4.rs b/exercises/tests/tests4.rs index 1f34a2b2a2..935d0db173 100644 --- a/exercises/tests/tests4.rs +++ b/exercises/tests/tests4.rs @@ -1,6 +1,9 @@ // 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. +// +// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index ae124eeb9f..80b6def3e9 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -1,10 +1,12 @@ // threads1.rs -// Execute `rustlings hint threads1` or use the `hint` watch subcommand for a hint. - -// 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. +// +// 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. // I AM NOT DONE diff --git a/exercises/threads/threads2.rs b/exercises/threads/threads2.rs index ada3d14a60..62dad80d62 100644 --- a/exercises/threads/threads2.rs +++ b/exercises/threads/threads2.rs @@ -1,7 +1,11 @@ // threads2.rs -// Execute `rustlings hint threads2` or use the `hint` watch subcommand for a hint. -// 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 +// +// 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. // I AM NOT DONE @@ -27,8 +31,9 @@ fn main() { } for handle in handles { handle.join().unwrap(); - // TODO: Print the value of the JobStatus.jobs_completed. Did you notice anything - // interesting in the output? Do you have to 'join' on all the handles? + // TODO: Print the value of the JobStatus.jobs_completed. Did you notice + // anything interesting in the output? Do you have to 'join' on all the + // handles? println!("jobs completed {}", ???); } } diff --git a/exercises/threads/threads3.rs b/exercises/threads/threads3.rs index 9e9f285aa7..db7d41ba6a 100644 --- a/exercises/threads/threads3.rs +++ b/exercises/threads/threads3.rs @@ -1,5 +1,7 @@ // threads3.rs -// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/traits/traits1.rs b/exercises/traits/traits1.rs index f5320a5a15..37dfcbfe85 100644 --- a/exercises/traits/traits1.rs +++ b/exercises/traits/traits1.rs @@ -1,13 +1,11 @@ // traits1.rs -// Time to implement some traits! // -// Your task is to implement the trait -// `AppendBar` for the type `String`. +// 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. // -// 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. +// Execute `rustlings hint traits1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/traits/traits2.rs b/exercises/traits/traits2.rs index 288b498363..3e35f8e11b 100644 --- a/exercises/traits/traits2.rs +++ b/exercises/traits/traits2.rs @@ -1,14 +1,11 @@ // 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"' +// 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! +// No boiler plate code this time, you can do this! +// // Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/traits/traits3.rs b/exercises/traits/traits3.rs index 6d2fd6c379..4e2b06b0e5 100644 --- a/exercises/traits/traits3.rs +++ b/exercises/traits/traits3.rs @@ -1,11 +1,12 @@ // 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. +// 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. +// +// Execute `rustlings hint traits3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/traits/traits4.rs b/exercises/traits/traits4.rs index 6b541665ff..4bda3e571e 100644 --- a/exercises/traits/traits4.rs +++ b/exercises/traits/traits4.rs @@ -1,8 +1,11 @@ // 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. +// +// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/traits/traits5.rs b/exercises/traits/traits5.rs index 0fbca28aec..df1838054a 100644 --- a/exercises/traits/traits5.rs +++ b/exercises/traits/traits5.rs @@ -1,8 +1,11 @@ // 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. +// +// Execute `rustlings hint traits5` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs index f4d182accf..b3e089a527 100644 --- a/exercises/variables/variables1.rs +++ b/exercises/variables/variables1.rs @@ -1,6 +1,9 @@ // variables1.rs +// // Make me compile! -// Execute `rustlings hint variables1` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint variables1` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs index 641aeb8e09..e1c23edf7d 100644 --- a/exercises/variables/variables2.rs +++ b/exercises/variables/variables2.rs @@ -1,5 +1,7 @@ // variables2.rs -// Execute `rustlings hint variables2` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint variables2` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs index 819b1bc791..86bed419f4 100644 --- a/exercises/variables/variables3.rs +++ b/exercises/variables/variables3.rs @@ -1,5 +1,7 @@ // variables3.rs -// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs index 54491b0a20..5394f39462 100644 --- a/exercises/variables/variables4.rs +++ b/exercises/variables/variables4.rs @@ -1,5 +1,7 @@ // variables4.rs -// Execute `rustlings hint variables4` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint variables4` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/variables/variables5.rs b/exercises/variables/variables5.rs index 0e670d2afe..a29b38be8e 100644 --- a/exercises/variables/variables5.rs +++ b/exercises/variables/variables5.rs @@ -1,5 +1,7 @@ // variables5.rs -// Execute `rustlings hint variables5` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint variables5` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/variables/variables6.rs b/exercises/variables/variables6.rs index a8520122c5..853183babc 100644 --- a/exercises/variables/variables6.rs +++ b/exercises/variables/variables6.rs @@ -1,5 +1,7 @@ // variables6.rs -// Execute `rustlings hint variables6` or use the `hint` watch subcommand for a hint. +// +// Execute `rustlings hint variables6` or use the `hint` watch subcommand for a +// hint. // I AM NOT DONE diff --git a/exercises/vecs/vecs1.rs b/exercises/vecs/vecs1.rs index 4e8c4cbbb7..65b7a7f835 100644 --- a/exercises/vecs/vecs1.rs +++ b/exercises/vecs/vecs1.rs @@ -1,7 +1,10 @@ // vecs1.rs -// Your task is to create a `Vec` which holds the exact same elements -// as in the array `a`. +// +// 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. // I AM NOT DONE diff --git a/exercises/vecs/vecs2.rs b/exercises/vecs/vecs2.rs index 1ea26071d4..e92c970aab 100644 --- a/exercises/vecs/vecs2.rs +++ b/exercises/vecs/vecs2.rs @@ -1,6 +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. +// +// 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! // From 1e02f194fdd1cb1ca99cf1d93d11455db8b1bce6 Mon Sep 17 00:00:00 2001 From: tajo48 Date: Wed, 31 May 2023 16:37:41 +0200 Subject: [PATCH 0277/1432] update hint for vecs2 to match with exercise as it was updated to fix clarity --- info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/info.toml b/info.toml index 2add5f0ccf..a7155d4844 100644 --- a/info.toml +++ b/info.toml @@ -260,8 +260,8 @@ name = "vecs2" path = "exercises/vecs/vecs2.rs" mode = "test" hint = """ -Hint 1: `i` is each element from the Vec as they are being iterated. Can you try -multiplying this? +Hint 1: In the code, the variable `element` represents an item from the Vec as it is being iterated. +Can you try multiplying this? Hint 2: For the first function, there's a way to directly access the numbers stored in the Vec, using the * dereference operator. You can both access and write to the From a96bbcd9675933b19454d627ab6291d4739f0e53 Mon Sep 17 00:00:00 2001 From: luhem7 Date: Sat, 3 Jun 2023 10:20:29 -0400 Subject: [PATCH 0278/1432] fix(threads, smart pointers): Swap order of threads and smart pointers exercises closes #1541 --- info.toml | 122 +++++++++++++++++++++++++++--------------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/info.toml b/info.toml index 2add5f0ccf..3abb2f8c55 100644 --- a/info.toml +++ b/info.toml @@ -905,67 +905,6 @@ The fold method can be useful in the count_collection_iterator function. For a further challenge, consult the documentation for Iterator to find a different method that could make your code more compact than using fold.""" -# THREADS - -[[exercises]] -name = "threads1" -path = "exercises/threads/threads1.rs" -mode = "compile" -hint = """ -`JoinHandle` is a struct that is returned from a spawned thread: -https://doc.rust-lang.org/std/thread/fn.spawn.html - -A challenge with multi-threaded applications is that the main thread can -finish before the spawned threads are completed. -https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles - -Use the JoinHandles to wait for each thread to finish and collect their results. -https://doc.rust-lang.org/std/thread/struct.JoinHandle.html -""" - -[[exercises]] -name = "threads2" -path = "exercises/threads/threads2.rs" -mode = "compile" -hint = """ -`Arc` is an Atomic Reference Counted pointer that allows safe, shared access -to **immutable** data. But we want to *change* the number of `jobs_completed` -so we'll need to also use another type that will only allow one thread to -mutate the data at a time. Take a look at this section of the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct -and keep reading if you'd like more hints :) - - -Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: -`let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` -Similar to the code in the example in the book that happens after the text -that says "We can use Arc to fix this.". If not, give that a try! If you -do and would like more hints, keep reading!! - - -Make sure neither of your threads are holding onto the lock of the mutex -while they are sleeping, since this will prevent the other thread from -being allowed to get the lock. Locks are automatically released when -they go out of scope. - -If you've learned from the sample solutions, I encourage you to come -back to this exercise and try it again in a few days to reinforce -what you've learned :)""" - -[[exercises]] -name = "threads3" -path = "exercises/threads/threads3.rs" -mode = "compile" -hint = """ -An alternate way to handle concurrency between threads is to use -a mpsc (multiple producer, single consumer) channel to communicate. -With both a sending end and a receiving end, it's possible to -send values in one thread and receive them in another. -Multiple producers are possible by using clone() to create a duplicate -of the original sending end. -See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. -""" - # SMART POINTERS [[exercises]] @@ -1028,6 +967,67 @@ Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation on the `Cow` type. """ +# THREADS + +[[exercises]] +name = "threads1" +path = "exercises/threads/threads1.rs" +mode = "compile" +hint = """ +`JoinHandle` is a struct that is returned from a spawned thread: +https://doc.rust-lang.org/std/thread/fn.spawn.html + +A challenge with multi-threaded applications is that the main thread can +finish before the spawned threads are completed. +https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles + +Use the JoinHandles to wait for each thread to finish and collect their results. +https://doc.rust-lang.org/std/thread/struct.JoinHandle.html +""" + +[[exercises]] +name = "threads2" +path = "exercises/threads/threads2.rs" +mode = "compile" +hint = """ +`Arc` is an Atomic Reference Counted pointer that allows safe, shared access +to **immutable** data. But we want to *change* the number of `jobs_completed` +so we'll need to also use another type that will only allow one thread to +mutate the data at a time. Take a look at this section of the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct +and keep reading if you'd like more hints :) + + +Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: +`let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` +Similar to the code in the example in the book that happens after the text +that says "We can use Arc to fix this.". If not, give that a try! If you +do and would like more hints, keep reading!! + + +Make sure neither of your threads are holding onto the lock of the mutex +while they are sleeping, since this will prevent the other thread from +being allowed to get the lock. Locks are automatically released when +they go out of scope. + +If you've learned from the sample solutions, I encourage you to come +back to this exercise and try it again in a few days to reinforce +what you've learned :)""" + +[[exercises]] +name = "threads3" +path = "exercises/threads/threads3.rs" +mode = "compile" +hint = """ +An alternate way to handle concurrency between threads is to use +a mpsc (multiple producer, single consumer) channel to communicate. +With both a sending end and a receiving end, it's possible to +send values in one thread and receive them in another. +Multiple producers are possible by using clone() to create a duplicate +of the original sending end. +See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. +""" + # MACROS [[exercises]] From 479574e88eef4a4299b005388bec74f07a9c57d5 Mon Sep 17 00:00:00 2001 From: "Florine W. Dekker" Date: Wed, 7 Jun 2023 16:58:02 +0200 Subject: [PATCH 0279/1432] fix(vecs): rename outdated variable name in hint --- info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/info.toml b/info.toml index 2add5f0ccf..8706ee51a6 100644 --- a/info.toml +++ b/info.toml @@ -260,8 +260,8 @@ name = "vecs2" path = "exercises/vecs/vecs2.rs" mode = "test" hint = """ -Hint 1: `i` is each element from the Vec as they are being iterated. Can you try -multiplying this? +Hint 1: `element` is each element from the Vec as they are being iterated. Can you +try multiplying this? Hint 2: For the first function, there's a way to directly access the numbers stored in the Vec, using the * dereference operator. You can both access and write to the From bbfb4c7e63dc59a4e8afbb8d2a10b35414782256 Mon Sep 17 00:00:00 2001 From: Bert Apperlo <91734527+b-apperlo@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:49:07 +0200 Subject: [PATCH 0280/1432] feat: added test function to hashmaps2.rs The existing test functions only check if a kind of fruit exists in the hashmap, but not if the amount of fruits is higher than zero. This new test function solves this. --- exercises/hashmaps/hashmaps2.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/exercises/hashmaps/hashmaps2.rs b/exercises/hashmaps/hashmaps2.rs index a4f069a82b..852c07601d 100644 --- a/exercises/hashmaps/hashmaps2.rs +++ b/exercises/hashmaps/hashmaps2.rs @@ -80,4 +80,13 @@ mod tests { let count = basket.values().sum::(); assert!(count > 11); } + + #[test] + fn all_fruit_types_in_basket() { + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + for amount in basket.values() { + assert_ne!(amount, &0); + } + } } From 8974e33f697abf55d322b7de051e8b41c219523d Mon Sep 17 00:00:00 2001 From: Bert Apperlo <91734527+b-apperlo@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:44:39 +0200 Subject: [PATCH 0281/1432] fix: update hashmaps3.rs --- exercises/hashmaps/hashmaps3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/hashmaps/hashmaps3.rs index ad3baa688a..982976c62e 100644 --- a/exercises/hashmaps/hashmaps3.rs +++ b/exercises/hashmaps/hashmaps3.rs @@ -20,7 +20,6 @@ use std::collections::HashMap; // A structure to store team name and its goal details. struct Team { - name: String, goals_scored: u8, goals_conceded: u8, } From a4fe3602b238f38e9fd245874c863852791129fb Mon Sep 17 00:00:00 2001 From: Bert Apperlo <91734527+b-apperlo@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:46:45 +0200 Subject: [PATCH 0282/1432] fix: updated comment for struct --- exercises/hashmaps/hashmaps3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/hashmaps/hashmaps3.rs index 982976c62e..0d0a43ad09 100644 --- a/exercises/hashmaps/hashmaps3.rs +++ b/exercises/hashmaps/hashmaps3.rs @@ -18,7 +18,7 @@ use std::collections::HashMap; -// A structure to store team name and its goal details. +// A structure to store the goal details of a team. struct Team { goals_scored: u8, goals_conceded: u8, From d0a17830831121fcdbc27a1833ccbbc17bc0c02d Mon Sep 17 00:00:00 2001 From: IVIURARY Date: Thu, 8 Jun 2023 22:14:25 +0100 Subject: [PATCH 0283/1432] fix(enums3): add test for message closes #1548 --- exercises/enums/enums3.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index a2a9d58684..e75d443383 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -17,6 +17,7 @@ struct State { color: (u8, u8, u8), position: Point, quit: bool, + message: String } impl State { @@ -28,9 +29,7 @@ impl State { self.quit = true; } - fn echo(&self, s: String) { - println!("{}", s); - } + fn echo(&mut self, s: String) { self.message = s } fn move_position(&mut self, p: Point) { self.position = p; @@ -52,6 +51,7 @@ mod tests { quit: false, position: Point { x: 0, y: 0 }, color: (0, 0, 0), + message: "hello world".to_string(), }; state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Echo(String::from("hello world"))); @@ -62,5 +62,6 @@ mod tests { assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); assert_eq!(state.quit, true); + assert_eq!(state.message, "hello world"); } } From f30f0bf148eab8b1a11fa7ee36f230e94aea417a Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 11 Jun 2023 16:30:01 -0500 Subject: [PATCH 0284/1432] chore(ci): add action step to check if lockfile is synced --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index bf2a041a4b..1b244b1a2e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - name: Fetch & maybe update Cargo.lock + run: cargo fetch --locked - name: Build run: cargo build --verbose - name: Run tests From 3cced07c1324a1d4e0b2f2e1e585cc1964d2bde3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 09:50:02 +0000 Subject: [PATCH 0285/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 50a477c087..8f8934e1f2 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -303,6 +303,7 @@ authors. b1ue64
b1ue64

πŸ–‹ lazywalker
lazywalker

πŸ–‹ + proofconstruction
proofconstruction

πŸš‡ From e0ea03dc567cd29c08012aa2da818c6f5599ab3b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 09:50:03 +0000 Subject: [PATCH 0286/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 14ec3a9850..15f9c56edf 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2136,6 +2136,15 @@ "contributions": [ "content" ] + }, + { + "login": "proofconstruction", + "name": "proofconstruction", + "avatar_url": "https://avatars.githubusercontent.com/u/74747193?v=4", + "profile": "https://github.com/proofconstruction", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, @@ -2144,5 +2153,6 @@ "repoType": "github", "repoHost": "https://github.com", "skipCi": true, - "commitConvention": "angular" + "commitConvention": "angular", + "commitType": "docs" } From 369ae2e63d06de6fee36aeebfd1ff3e8bcdfa25a Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 12 Jun 2023 12:07:18 +0200 Subject: [PATCH 0287/1432] feat(move_semantics2): rewrite hint --- exercises/move_semantics/move_semantics2.rs | 10 +++---- info.toml | 29 +++++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 93bb82efb9..66ddb4cdc6 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -2,23 +2,21 @@ // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. // Expected output: -// vec0 has length 3 content `[22, 44, 66]` -// vec1 has length 4 content `[22, 44, 66, 88]` +// vec0 has length 3, with contents `[22, 44, 66]` +// vec1 has length 4, with contents `[22, 44, 66, 88]` // I AM NOT DONE fn main() { let vec0 = Vec::new(); - // Do not move the following line! let mut vec1 = fill_vec(vec0); - // Do not change the following line! - println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0); + println!("{} has length {}, with contents: `{:?}`", "vec0", vec0.len(), vec0); vec1.push(88); - println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); + println!("{} has length {}, with contents `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: Vec) -> Vec { diff --git a/info.toml b/info.toml index 2add5f0ccf..8febf41d01 100644 --- a/info.toml +++ b/info.toml @@ -287,23 +287,24 @@ Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!"" [[exercises]] name = "move_semantics2" path = "exercises/move_semantics/move_semantics2.rs" -mode = "compile" +mode = "test" hint = """ -So, `vec0` is passed into the `fill_vec` function as an argument. In Rust, -when an argument is passed to a function and it's not explicitly returned, -you can't use the original variable anymore. We call this "moving" a variable. -Variables that are moved into a function (or block scope) and aren't explicitly -returned get "dropped" at the end of that function. This is also what happens here. -There's a few ways to fix this, try them all if you want: -1. Make another, separate version of the data that's in `vec0` and pass that +When running this exercise for the first time, you'll notice an error about +"borrow of moved value". In Rust, when an argument is passed to a function and +it's not explicitly returned, you can't use the original variable anymore. +We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's being +"moved" into `vec1`, meaning we can't access `vec0` anymore after the fact. +Rust provides a couple of different ways to mitigate this issue, feel free to try them all: +1. You could make another, separate version of the data that's in `vec0` and pass that to `fill_vec` instead. 2. Make `fill_vec` borrow its argument instead of taking ownership of it, - and then copy the data within the function in order to return an owned - `Vec` -3. Make `fill_vec` *mutably* borrow a reference to its argument (which will need to be - mutable), modify it directly, then not return anything. Then you can get rid - of `vec1` entirely -- note that this will change what gets printed by the - first `println!`""" + and then copy the data within the function (`vec.clone()`) in order to return an owned + `Vec`. +3. Or, you could make `fill_vec` *mutably* borrow a reference to its argument (which will need to be + mutable), modify it directly, then not return anything. This means that `vec0` will change over the + course of the function, and makes `vec1` redundant (make sure to change the parameters of the `println!` + statements if you go this route) +""" [[exercises]] name = "move_semantics3" From a1188ca0eea0e1a61217812bde3b085a7de998b5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:17:21 +0000 Subject: [PATCH 0288/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 8f8934e1f2..77f98ada16 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -304,6 +304,7 @@ authors. b1ue64
b1ue64

πŸ–‹ lazywalker
lazywalker

πŸ–‹ proofconstruction
proofconstruction

πŸš‡ + IVIURRAY
IVIURRAY

πŸ–‹ From 47fcab1e603e7147e68bdf03b110f8799f2d5460 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:17:22 +0000 Subject: [PATCH 0289/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 15f9c56edf..6c0cb1fc81 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2145,6 +2145,15 @@ "contributions": [ "infra" ] + }, + { + "login": "IVIURRAY", + "name": "IVIURRAY", + "avatar_url": "https://avatars.githubusercontent.com/u/16007179?v=4", + "profile": "https://www.youtube.com/channel/UCQCjA6qUutAtWqkCA4Z36CQ", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 4fd52390eec5e16b24cf26c1efaa961d4f0a365e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:18:54 +0000 Subject: [PATCH 0290/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 77f98ada16..4d15136391 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -305,6 +305,7 @@ authors. lazywalker
lazywalker

πŸ–‹ proofconstruction
proofconstruction

πŸš‡ IVIURRAY
IVIURRAY

πŸ–‹ + Bert Apperlo
Bert Apperlo

πŸ–‹ From d35a352fe6f252217c65560e3c1addcd23e9a7f0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:18:55 +0000 Subject: [PATCH 0291/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 6c0cb1fc81..760aa3a6ee 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2154,6 +2154,15 @@ "contributions": [ "content" ] + }, + { + "login": "b-apperlo", + "name": "Bert Apperlo", + "avatar_url": "https://avatars.githubusercontent.com/u/91734527?v=4", + "profile": "https://github.com/b-apperlo", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From bd05bab4bfb14a7ef65424cdcd7af8f20d4a3dd1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:32:25 +0000 Subject: [PATCH 0292/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4d15136391..a2219b2d0c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -306,6 +306,7 @@ authors. proofconstruction
proofconstruction

πŸš‡ IVIURRAY
IVIURRAY

πŸ–‹ Bert Apperlo
Bert Apperlo

πŸ–‹ + Florine W. Dekker
Florine W. Dekker

πŸ–‹ From 5921948de8cd15e20e5812c0d1712ec4fc3e8127 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:32:26 +0000 Subject: [PATCH 0293/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 760aa3a6ee..81e5f4f286 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2163,6 +2163,15 @@ "contributions": [ "content" ] + }, + { + "login": "FWDekker", + "name": "Florine W. Dekker", + "avatar_url": "https://avatars.githubusercontent.com/u/13442533?v=4", + "profile": "https://fwdekker.com/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From a14f0c3f55ce326a6dfe5b7ed63cb0052985d1c4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:33:44 +0000 Subject: [PATCH 0294/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index a2219b2d0c..23dfa65fd6 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -307,6 +307,7 @@ authors. IVIURRAY
IVIURRAY

πŸ–‹ Bert Apperlo
Bert Apperlo

πŸ–‹ Florine W. Dekker
Florine W. Dekker

πŸ–‹ + Mehul Gangavelli
Mehul Gangavelli

πŸ–‹ From 521962159592adc2031fc5ff513ce6385bb72a45 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:33:45 +0000 Subject: [PATCH 0295/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 81e5f4f286..5e03877442 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2172,6 +2172,15 @@ "contributions": [ "content" ] + }, + { + "login": "luhem7", + "name": "Mehul Gangavelli", + "avatar_url": "https://avatars.githubusercontent.com/u/4008215?v=4", + "profile": "https://github.com/luhem7", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From eb3c9658b166eb6aae1d05ea89d0863a554663b4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:36:22 +0000 Subject: [PATCH 0296/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 23dfa65fd6..723cc4a841 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -308,6 +308,7 @@ authors. Bert Apperlo
Bert Apperlo

πŸ–‹ Florine W. Dekker
Florine W. Dekker

πŸ–‹ Mehul Gangavelli
Mehul Gangavelli

πŸ–‹ + Mikael Frosthage
Mikael Frosthage

πŸ–‹ From a2f99ad00db939e63b8bdbbfbb9948a737fe0f5c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:36:23 +0000 Subject: [PATCH 0297/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 5e03877442..ab5d14241a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2181,6 +2181,15 @@ "contributions": [ "content" ] + }, + { + "login": "Frosthage", + "name": "Mikael Frosthage", + "avatar_url": "https://avatars.githubusercontent.com/u/14823314?v=4", + "profile": "https://github.com/Frosthage", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 33cf4f7eca5b309996cee25bced7dc76936f09b8 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:39:26 +0000 Subject: [PATCH 0298/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 723cc4a841..7930652397 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -310,6 +310,9 @@ authors. Mehul Gangavelli
Mehul Gangavelli

πŸ–‹ Mikael Frosthage
Mikael Frosthage

πŸ–‹ + + Robert Fry
Robert Fry

πŸ–‹ + From 846e68e1aa601826e18f9e5e7b0fbf3fd0afd3e1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:39:27 +0000 Subject: [PATCH 0299/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index ab5d14241a..393e59e210 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2190,6 +2190,15 @@ "contributions": [ "content" ] + }, + { + "login": "robertefry", + "name": "Robert Fry", + "avatar_url": "https://avatars.githubusercontent.com/u/43712054?v=4", + "profile": "https://robertfry.xyz", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c236a80a37c46cc59d5fc6ba619a23229c9f816c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 08:42:01 +0000 Subject: [PATCH 0300/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 7930652397..ba208034f8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -312,6 +312,7 @@ authors. Robert Fry
Robert Fry

πŸ–‹ + tajo48
tajo48

πŸ–‹ From 0cb63cdd01d133a1244ff18db448591fb3a40cd6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 08:42:02 +0000 Subject: [PATCH 0301/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 393e59e210..a8b82aebb3 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2199,6 +2199,15 @@ "contributions": [ "content" ] + }, + { + "login": "tajo48", + "name": "tajo48", + "avatar_url": "https://avatars.githubusercontent.com/u/55502906?v=4", + "profile": "https://github.com/tajo48", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c2264cabae976cf534be08942ff33f3a58f8795e Mon Sep 17 00:00:00 2001 From: liv Date: Thu, 15 Jun 2023 10:56:53 +0200 Subject: [PATCH 0302/1432] fix: install into $home on windows for now Stopgap measure so that people stop installing into System32 (since that's the default Powershell elevated prompt directory for some reason). --- install.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.ps1 b/install.ps1 index 97980c5bee..7bab21f6f7 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,7 +1,7 @@ #!/usr/bin/env pwsh #Requires -Version 5 -param($path = "$pwd/rustlings") +param($path = "$home/rustlings") Write-Host "Let's get you set up with Rustlings!" @@ -91,4 +91,4 @@ if (!$clippy) { rustup component add clippy } -Write-Host "All done! Run 'rustlings' to get started." +Write-Host "All done! Navigate to $path and run 'rustlings' to get started!" From c5231f0ce34092a8e92ee0e2eac157f9e0655e33 Mon Sep 17 00:00:00 2001 From: Mohammed Sadiq Date: Fri, 16 Jun 2023 13:50:54 +0530 Subject: [PATCH 0303/1432] fix(enums3): modify message string in test Otherwise it won't actually test the change of state.message and it would fail to check if the user missed changing state.message This happened to me as I had a catch-all line in `match` --- exercises/enums/enums3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index 5d284417e8..98da16917f 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -59,7 +59,7 @@ mod tests { message: "hello world".to_string(), }; state.process(Message::ChangeColor(255, 0, 255)); - state.process(Message::Echo(String::from("hello world"))); + state.process(Message::Echo(String::from("Hello world!"))); state.process(Message::Move(Point { x: 10, y: 15 })); state.process(Message::Quit); @@ -67,6 +67,6 @@ mod tests { assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); assert_eq!(state.quit, true); - assert_eq!(state.message, "hello world"); + assert_eq!(state.message, "Hello world!"); } } From adc7ca56901ed1f53659fb8fa139a65423affd13 Mon Sep 17 00:00:00 2001 From: liv Date: Thu, 22 Jun 2023 10:10:44 +0200 Subject: [PATCH 0304/1432] fix(move_semantics2): change type back to compile --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index ba3e585a96..b4fd8ffa08 100644 --- a/info.toml +++ b/info.toml @@ -287,7 +287,7 @@ Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!"" [[exercises]] name = "move_semantics2" path = "exercises/move_semantics/move_semantics2.rs" -mode = "test" +mode = "compile" hint = """ When running this exercise for the first time, you'll notice an error about "borrow of moved value". In Rust, when an argument is passed to a function and From eacf7db6f34f7c884b2a736a35fa997299b34485 Mon Sep 17 00:00:00 2001 From: Anish <98446102+novanish@users.noreply.github.com> Date: Fri, 23 Jun 2023 17:18:17 +0545 Subject: [PATCH 0305/1432] refactor: Update comment to use correct construct name 'Result' instead of 'Option' Refactor the comment in the code to provide a more accurate description of the construct being used. Replace the mention of Option with Result. --- exercises/error_handling/errors1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/error_handling/errors1.rs b/exercises/error_handling/errors1.rs index 0ba59a57a6..13d2724cb2 100644 --- a/exercises/error_handling/errors1.rs +++ b/exercises/error_handling/errors1.rs @@ -3,7 +3,7 @@ // 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 +// construct to `Result` that can be used to express error conditions. Let's use // it! // // Execute `rustlings hint errors1` or use the `hint` watch subcommand for a From f03a915c750a47cfbabb13413e200d82f0e45405 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 07:58:23 +0000 Subject: [PATCH 0306/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index ba208034f8..c7ef5209b4 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -313,6 +313,7 @@ authors. Robert Fry
Robert Fry

πŸ–‹ tajo48
tajo48

πŸ–‹ + Anish
Anish

πŸ–‹ From 2e4022dd730869c9065f8b9f1aa031cbb0b2f0fa Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 07:58:24 +0000 Subject: [PATCH 0307/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index a8b82aebb3..0c985add04 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2208,6 +2208,15 @@ "contributions": [ "content" ] + }, + { + "login": "novanish", + "name": "Anish", + "avatar_url": "https://avatars.githubusercontent.com/u/98446102?v=4", + "profile": "https://anishchhetri.com.np", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 405b32e77a6643f08e5bc895d6d2d1e684d47324 Mon Sep 17 00:00:00 2001 From: vnprc <9425366+vnprc@users.noreply.github.com> Date: Mon, 26 Jun 2023 09:17:39 -0400 Subject: [PATCH 0308/1432] chore: use correct line number in strings2.rs hint --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index b4fd8ffa08..e4863cbf89 100644 --- a/info.toml +++ b/info.toml @@ -439,7 +439,7 @@ mode = "compile" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way to add one character to line -9, though, that will coerce the `String` into a string slice. +12, though, that will coerce the `String` into a string slice. Side note: If you're interested in learning about how this kind of reference conversion works, you can jump ahead in the book and read this part in the smart pointers chapter: https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" From 47b3a531325932a0051b0dba91c3a8fd86aa2fe1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:19:52 +0000 Subject: [PATCH 0309/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index c7ef5209b4..4e7771747f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -314,6 +314,7 @@ authors. Robert Fry
Robert Fry

πŸ–‹ tajo48
tajo48

πŸ–‹ Anish
Anish

πŸ–‹ + vnprc
vnprc

πŸ–‹ From a46d42f45e7052fbadb457cd303ff62f5a641555 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:19:53 +0000 Subject: [PATCH 0310/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0c985add04..d02fe784bd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2217,6 +2217,15 @@ "contributions": [ "content" ] + }, + { + "login": "vnprc", + "name": "vnprc", + "avatar_url": "https://avatars.githubusercontent.com/u/9425366?v=4", + "profile": "https://github.com/vnprc", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From b99c7b8c37b7f95d450ad3fa5461a9398c0f7819 Mon Sep 17 00:00:00 2001 From: Will Hack <18036720+willhack@users.noreply.github.com> Date: Sun, 2 Jul 2023 13:46:59 -0400 Subject: [PATCH 0311/1432] chore: update line reference in strings2 hint --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index ba3e585a96..05012cf92a 100644 --- a/info.toml +++ b/info.toml @@ -439,7 +439,7 @@ mode = "compile" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way to add one character to line -9, though, that will coerce the `String` into a string slice. +12, though, that will coerce the `String` into a string slice. Side note: If you're interested in learning about how this kind of reference conversion works, you can jump ahead in the book and read this part in the smart pointers chapter: https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" From c5b0627180ae98c41f67e345b2a16ca14a6cffd1 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 3 Jul 2023 18:50:50 +0200 Subject: [PATCH 0312/1432] chore: update oranda config for 0.1.0 --- oranda.json | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/oranda.json b/oranda.json index 18ef8c68a1..603e474e91 100644 --- a/oranda.json +++ b/oranda.json @@ -1,10 +1,25 @@ { - "homepage": "https://rustlings.cool", - "repository": "https://github.com/rust-lang/rustlings", - "analytics": { - "plausible": { - "domain": "rustlings.cool" + "project": { + "homepage": "https://rustlings.cool", + "repository": "https://github.com/rust-lang/rustlings" + }, + "marketing": { + "analytics": { + "plausible": { + "domain": "rustlings.cool" + } } }, - "changelog": false + "components": { + "artifacts": { + "cargo_dist": false, + "package_managers": { + "preferred": { + "macos/linux/unix": "curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash", + "windows": "Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1" + } + } + }, + "changelog": true + } } From 0ab781c7a7e5240f86033a5c9d242ef5aca391aa Mon Sep 17 00:00:00 2001 From: Will Hack <18036720+willhack@users.noreply.github.com> Date: Mon, 3 Jul 2023 14:20:38 -0400 Subject: [PATCH 0313/1432] chore: remove line reference from strings2 hint --- info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/info.toml b/info.toml index 05012cf92a..9885cdce5f 100644 --- a/info.toml +++ b/info.toml @@ -438,8 +438,8 @@ path = "exercises/strings/strings2.rs" mode = "compile" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a -string slice instead of a `String`, wouldn't it?? There is a way to add one character to line -12, though, that will coerce the `String` into a string slice. +string slice instead of a `String`, wouldn't it?? There is a way to add one character to the +if statement, though, that will coerce the `String` into a string slice. Side note: If you're interested in learning about how this kind of reference conversion works, you can jump ahead in the book and read this part in the smart pointers chapter: https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" From 287172698aea106723682542e011b64f3d6d218a Mon Sep 17 00:00:00 2001 From: Joshua Carlson Date: Mon, 3 Jul 2023 14:52:13 -0400 Subject: [PATCH 0314/1432] added if3 based on: `Using if in a let Statement` --- exercises/if/if3.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++ info.toml | 7 ++++++ 2 files changed, 62 insertions(+) create mode 100644 exercises/if/if3.rs diff --git a/exercises/if/if3.rs b/exercises/if/if3.rs new file mode 100644 index 0000000000..73a7025bcc --- /dev/null +++ b/exercises/if/if3.rs @@ -0,0 +1,55 @@ +// if3.rs +// +// Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +pub fn animal_habitat(animal: &str) -> &'static str { + let identifier = if animal == "crab" { + 1 + } else if animal == "gopher" { + 2.0 + } else if animal == "snake" { + 3 + } else { + "Unknown" + }; + + // DO NOT CHANGE THIS STATEMENT BELOW + let habitat = if identifier == 1 { + "Beach" + } else if identifier == 2 { + "Burrow" + } else if identifier == 3 { + "Desert" + } else { + "Unknown" + }; + + habitat +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn gopher_lives_in_burrow() { + assert_eq!(animal_habitat("gopher"), "Burrow") + } + + #[test] + fn snake_lives_in_desert() { + assert_eq!(animal_habitat("snake"), "Desert") + } + + #[test] + fn crab_lives_on_beach() { + assert_eq!(animal_habitat("crab"), "Beach") + } + + #[test] + fn unknown_animal() { + assert_eq!(animal_habitat("dinosaur"), "Unknown") + } +} diff --git a/info.toml b/info.toml index e4863cbf89..e8a28cbc63 100644 --- a/info.toml +++ b/info.toml @@ -167,6 +167,13 @@ For that first compiler error, it's important in Rust that each conditional block returns the same type! To get the tests passing, you will need a couple conditions checking different input values.""" +[[exercises]] +name = "if3" +path = "exercises/if/if3.rs" +mode = "test" +hint = """ +In Rust, every arm of an `if` expression has to return the same type of value. Make sure the type is consistent across all arms.""" + # QUIZ 1 [[exercises]] From 0e0c5dfe99b99b7723ea9ec77733214c69b2a8ae Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 09:57:30 +0000 Subject: [PATCH 0315/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4e7771747f..6c1ee1e3a4 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -315,6 +315,7 @@ authors. tajo48
tajo48

πŸ–‹ Anish
Anish

πŸ–‹ vnprc
vnprc

πŸ–‹ + Joshua Carlson
Joshua Carlson

πŸ–‹ From 176fb7c447e9310c5c5ccc511ee8f61ad14adc9b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 09:57:31 +0000 Subject: [PATCH 0316/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d02fe784bd..37b86693ee 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2226,6 +2226,15 @@ "contributions": [ "content" ] + }, + { + "login": "jrcarl624", + "name": "Joshua Carlson", + "avatar_url": "https://avatars.githubusercontent.com/u/61999256?v=4", + "profile": "http://androecia.net", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 2934d062a30be177056fcded950bd5339b636b8f Mon Sep 17 00:00:00 2001 From: "Nicholas R. Smith" Date: Fri, 14 Jul 2023 10:47:42 -0700 Subject: [PATCH 0317/1432] fix: run cargo update to build proc-macro2 on nightly --- Cargo.lock | 184 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a09d98f7ca..a8268d90a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -77,15 +77,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "console" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -114,14 +114,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -176,11 +176,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "home" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] @@ -226,9 +226,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "kernel32-sys" @@ -254,18 +254,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -318,9 +315,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.38" +version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" dependencies = [ "cfg-if 0.1.10", "libc", @@ -397,18 +394,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -424,9 +421,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.2" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -435,9 +444,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rustlings" @@ -459,9 +468,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "same-file" @@ -474,29 +483,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.158" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.25", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" dependencies = [ "itoa", "ryu", @@ -525,9 +534,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2", "quote", @@ -551,9 +560,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unicode-width" @@ -616,26 +625,20 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets 0.42.2", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -644,13 +647,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -659,42 +677,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "ws2_32-sys" version = "0.2.1" From f4d86f384c396ed0741e93f71c314c5ed503f12c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 10:31:15 +0000 Subject: [PATCH 0318/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 6c1ee1e3a4..04ef1fc6b1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -316,6 +316,7 @@ authors. Anish
Anish

πŸ–‹ vnprc
vnprc

πŸ–‹ Joshua Carlson
Joshua Carlson

πŸ–‹ + Nicholas R. Smith
Nicholas R. Smith

πŸ’» From 2c7dec73275a12f8e47e0f60c2f1686e579bdc7a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 10:31:16 +0000 Subject: [PATCH 0319/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 37b86693ee..ac074d39bb 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2235,6 +2235,15 @@ "contributions": [ "content" ] + }, + { + "login": "johnDeSilencio", + "name": "Nicholas R. Smith", + "avatar_url": "https://avatars.githubusercontent.com/u/20136554?v=4", + "profile": "https://johndesilencio.me", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 662e5bddd7faf7ba383d964ef3c6a2d33168e66c Mon Sep 17 00:00:00 2001 From: Yamila Moreno Date: Mon, 17 Jul 2023 15:58:29 +0200 Subject: [PATCH 0320/1432] fix(primitives-4.rs): update hint so it's less confusing --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index e8a28cbc63..f484ecd2b0 100644 --- a/info.toml +++ b/info.toml @@ -216,7 +216,7 @@ mode = "test" hint = """ Take a look at the Understanding Ownership -> Slices -> Other Slices section of the book: https://doc.rust-lang.org/book/ch04-03-slices.html -and use the starting and ending indices of the items in the Array +and use the starting and ending (plus one) indices of the items in the Array that you want to end up in the slice. If you're curious why the first argument of `assert_eq!` does not From 7f9f897945fbe764b240d51389cf933488b74910 Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Thu, 20 Jul 2023 08:28:18 +0300 Subject: [PATCH 0321/1432] add test-case to if/if1 to check equal values --- exercises/if/if1.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index d8108a0f9c..4734d78fdd 100644 --- a/exercises/if/if1.rs +++ b/exercises/if/if1.rs @@ -25,4 +25,9 @@ mod tests { fn fortytwo_is_bigger_than_thirtytwo() { assert_eq!(42, bigger(32, 42)); } + + #[test] + fn equal_numbers() { + assert_eq!(42, bigger(42, 42)); + } } From ef8f1f108ba2ed109eea93684ea29861085cea31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gonz=C3=A1lez?= Date: Fri, 21 Jul 2023 14:42:19 +0200 Subject: [PATCH 0322/1432] docs: dedup repeated sentence --- exercises/quiz1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index a9904b8b17..4ee5ada7d8 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -9,7 +9,7 @@ // - An apple costs 2 rustbucks. // - 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! +// quantity bought. // // No hints this time ;) From 8283ae8c4c90acde71a98037107b018ec8fc454b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 08:29:17 +0000 Subject: [PATCH 0323/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 04ef1fc6b1..3dde84070e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -317,6 +317,7 @@ authors. vnprc
vnprc

πŸ–‹ Joshua Carlson
Joshua Carlson

πŸ–‹ Nicholas R. Smith
Nicholas R. Smith

πŸ’» + Alexander GonzΓ‘lez
Alexander GonzΓ‘lez

πŸ–‹ From bb5fa3f1e8b867f03d5db4d6ab53a1fdfdfad062 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 08:29:18 +0000 Subject: [PATCH 0324/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index ac074d39bb..8a3034da73 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2244,6 +2244,15 @@ "contributions": [ "code" ] + }, + { + "login": "alexfertel", + "name": "Alexander GonzΓ‘lez", + "avatar_url": "https://avatars.githubusercontent.com/u/22298999?v=4", + "profile": "https://alexfertel.me", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From be8d1df8b90ad6f15c529e8460a6de72bba52de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20H=C3=B6jvall?= Date: Fri, 28 Jul 2023 23:05:01 +0200 Subject: [PATCH 0325/1432] chore(errors4): improved comment --- exercises/error_handling/errors4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/error_handling/errors4.rs b/exercises/error_handling/errors4.rs index e04bff77ad..d6d6fcb6ab 100644 --- a/exercises/error_handling/errors4.rs +++ b/exercises/error_handling/errors4.rs @@ -16,7 +16,7 @@ enum CreationError { impl PositiveNonzeroInteger { fn new(value: i64) -> Result { - // Hmm...? Why is this only returning an Ok value? + // Hmm... Why is this always returning an Ok value? Ok(PositiveNonzeroInteger(value as u64)) } } From f39df76215fb06013e3d2fbbaf1b709de4729867 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:50:13 +0000 Subject: [PATCH 0326/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 3dde84070e..250e092941 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -318,6 +318,7 @@ authors. Joshua Carlson
Joshua Carlson

πŸ–‹ Nicholas R. Smith
Nicholas R. Smith

πŸ’» Alexander GonzΓ‘lez
Alexander GonzΓ‘lez

πŸ–‹ + Marcus HΓΆjvall
Marcus HΓΆjvall

πŸ–‹ From 62adbdf7f2d32981e3c44792008c23e5ac700739 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:50:14 +0000 Subject: [PATCH 0327/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8a3034da73..b5f81cee6e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2253,6 +2253,15 @@ "contributions": [ "content" ] + }, + { + "login": "softarn", + "name": "Marcus HΓΆjvall", + "avatar_url": "https://avatars.githubusercontent.com/u/517619?v=4", + "profile": "https://github.com/softarn", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 828e7246598d2d55ea4b379194120367163e397e Mon Sep 17 00:00:00 2001 From: Joshua Carlson <61999256+jrcarl624@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:33:32 -0400 Subject: [PATCH 0328/1432] Added note related to tests. --- exercises/if/if3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/if/if3.rs b/exercises/if/if3.rs index 73a7025bcc..0040023618 100644 --- a/exercises/if/if3.rs +++ b/exercises/if/if3.rs @@ -28,7 +28,7 @@ pub fn animal_habitat(animal: &str) -> &'static str { habitat } - +// No test changes needed. #[cfg(test)] mod tests { use super::*; From 2691a35102b0eab111706da0c27fc39541b64e0c Mon Sep 17 00:00:00 2001 From: Alon Hearter Date: Mon, 7 Aug 2023 18:22:49 +0300 Subject: [PATCH 0329/1432] Fix from_into.rs tests --- exercises/conversions/from_into.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/conversions/from_into.rs b/exercises/conversions/from_into.rs index aba471d92d..60911f3e21 100644 --- a/exercises/conversions/from_into.rs +++ b/exercises/conversions/from_into.rs @@ -127,14 +127,14 @@ mod tests { #[test] fn test_trailing_comma() { let p: Person = Person::from("Mike,32,"); - assert_eq!(p.name, "John"); - assert_eq!(p.age, 30); + assert_eq!(p.name, "Mike"); + assert_eq!(p.age, 32); } #[test] fn test_trailing_comma_and_some_string() { let p: Person = Person::from("Mike,32,man"); - assert_eq!(p.name, "John"); - assert_eq!(p.age, 30); + assert_eq!(p.name, "Mike"); + assert_eq!(p.age, 32); } } From 24c838bc0b8bbcc8d61d1af4fc611a7d1c7fbe80 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 15:38:51 +0000 Subject: [PATCH 0330/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 250e092941..6d73d4791f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -320,6 +320,9 @@ authors. Alexander GonzΓ‘lez
Alexander GonzΓ‘lez

πŸ–‹ Marcus HΓΆjvall
Marcus HΓΆjvall

πŸ–‹ + + Alon Hearter
Alon Hearter

πŸ–‹ + From 1ace9795f728562fecc548858172320d65c2029e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 15:38:52 +0000 Subject: [PATCH 0331/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b5f81cee6e..632a633522 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2262,6 +2262,15 @@ "contributions": [ "content" ] + }, + { + "login": "barlevalon", + "name": "Alon Hearter", + "avatar_url": "https://avatars.githubusercontent.com/u/3397911?v=4", + "profile": "https://github.com/barlevalon", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 720f33eee642eafab3c03bc7caed5f8690b481e8 Mon Sep 17 00:00:00 2001 From: Mate Kovacs Date: Thu, 10 Aug 2023 19:56:47 +0900 Subject: [PATCH 0332/1432] add .to_mut() in test owned_mutation() --- exercises/smart_pointers/cow1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/smart_pointers/cow1.rs b/exercises/smart_pointers/cow1.rs index 7ca916866a..5ab5115255 100644 --- a/exercises/smart_pointers/cow1.rs +++ b/exercises/smart_pointers/cow1.rs @@ -70,7 +70,7 @@ mod tests { // case the call to `to_mut()` returns a reference to the same data as // before. let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice); + let mut input = Cow::from(slice).to_mut(); match abs_all(&mut input) { // TODO } From ad1c29c512cbd39c05ebd07b9013f08fe8721017 Mon Sep 17 00:00:00 2001 From: Kevin C Date: Mon, 14 Aug 2023 12:49:28 -0700 Subject: [PATCH 0333/1432] Fix comment in errors2 --- exercises/error_handling/errors2.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs index d86f326d09..d4a5477b23 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -9,9 +9,9 @@ // // Right now, this function isn't handling the error case at all (and isn't // handling the success case properly either). What we want to do is: if we call -// the `parse` function on a string that is not a number, that function will -// return a `ParseIntError`, and in that case, we want to immediately return -// that error from our function and not try to multiply and add. +// the `total_cost` function on a string that is not a number, that function +// will return a `ParseIntError`, and in that case, we want to immediately +// return that error from our function and not try to multiply and add. // // There are at least two ways to implement this that are both correct-- but one // is a lot shorter! From 33a45d0f898317dbbb7bc9e9e057f54c36b62471 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 09:20:01 +0000 Subject: [PATCH 0334/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 6d73d4791f..28fa51a9d0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -322,6 +322,7 @@ authors. Alon Hearter
Alon Hearter

πŸ–‹ + shirts
shirts

πŸ–‹ From d67084730841399466089b867bc41f780e8c0281 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 09:20:02 +0000 Subject: [PATCH 0335/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 632a633522..5d1c22b6e4 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2271,6 +2271,15 @@ "contributions": [ "content" ] + }, + { + "login": "shirts", + "name": "shirts", + "avatar_url": "https://avatars.githubusercontent.com/u/4952151?v=4", + "profile": "https://github.com/shirts", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From af76794627ac9252650c8d3ec41d9afe251ca5e5 Mon Sep 17 00:00:00 2001 From: Roi Gabay Date: Sun, 20 Aug 2023 21:09:10 +0300 Subject: [PATCH 0336/1432] info.toml: update threads2 text. the previous text does not appear in the provided link (https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct). --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index e8a28cbc63..05bb0d5f4f 100644 --- a/info.toml +++ b/info.toml @@ -1009,7 +1009,7 @@ and keep reading if you'd like more hints :) Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: `let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` Similar to the code in the example in the book that happens after the text -that says "We can use Arc to fix this.". If not, give that a try! If you +that says "Sharing a Mutex Between Multiple Threads". If not, give that a try! If you do and would like more hints, keep reading!! From fa0463b3b219f3d10e8ae2e04bc9fbb67e360e9b Mon Sep 17 00:00:00 2001 From: Ivan Vasiunyk Date: Fri, 25 Aug 2023 21:49:22 +0200 Subject: [PATCH 0337/1432] fix(errors1): change Result to Option in exersise description --- exercises/error_handling/errors1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/error_handling/errors1.rs b/exercises/error_handling/errors1.rs index 13d2724cb2..0ba59a57a6 100644 --- a/exercises/error_handling/errors1.rs +++ b/exercises/error_handling/errors1.rs @@ -3,7 +3,7 @@ // 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 `Result` that can be used to express error conditions. Let's use +// 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 From e7ad540618048d4f4c1fee9666a7d49df0ccaed3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 26 Aug 2023 20:27:32 +0000 Subject: [PATCH 0338/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 28fa51a9d0..e01c19c325 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -323,6 +323,7 @@ authors. Alon Hearter
Alon Hearter

πŸ–‹ shirts
shirts

πŸ–‹ + Ivan Vasiunyk
Ivan Vasiunyk

πŸ–‹ From 77f0e177e67bddd3e5ed5a3460800cb1749e6b94 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 26 Aug 2023 20:27:33 +0000 Subject: [PATCH 0339/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 5d1c22b6e4..22d6841891 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2280,6 +2280,15 @@ "contributions": [ "content" ] + }, + { + "login": "eLVas", + "name": "Ivan Vasiunyk", + "avatar_url": "https://avatars.githubusercontent.com/u/6797156?v=4", + "profile": "https://github.com/eLVas", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 571bab20c1136adf1443b0fc1778341e923e06e5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 26 Aug 2023 23:07:20 +0200 Subject: [PATCH 0340/1432] Run clippy --fix --- src/exercise.rs | 18 +++++++++--------- src/main.rs | 12 ++++++------ src/project.rs | 2 +- src/verify.rs | 4 ++-- tests/integration_tests.rs | 34 +++++++++++++++++----------------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 2cde4e1592..07251dba54 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -110,12 +110,12 @@ impl Exercise { pub fn compile(&self) -> Result { let cmd = match self.mode { Mode::Compile => Command::new("rustc") - .args(&[self.path.to_str().unwrap(), "-o", &temp_file()]) + .args([self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) .args(RUSTC_EDITION_ARGS) .output(), Mode::Test => Command::new("rustc") - .args(&["--test", self.path.to_str().unwrap(), "-o", &temp_file()]) + .args(["--test", self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) .args(RUSTC_EDITION_ARGS) .output(), @@ -141,7 +141,7 @@ path = "{}.rs""#, // compilation failure, this would silently fail. But we expect // clippy to reflect the same failure while compiling later. Command::new("rustc") - .args(&[self.path.to_str().unwrap(), "-o", &temp_file()]) + .args([self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) .args(RUSTC_EDITION_ARGS) .output() @@ -151,14 +151,14 @@ path = "{}.rs""#, // This is already fixed on Clippy's master branch. See this issue to track merging into Cargo: // https://github.com/rust-lang/rust-clippy/issues/3837 Command::new("cargo") - .args(&["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) + .args(["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(RUSTC_COLOR_ARGS) .output() .expect("Failed to run 'cargo clean'"); Command::new("cargo") - .args(&["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) + .args(["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(RUSTC_COLOR_ARGS) - .args(&["--", "-D", "warnings", "-D", "clippy::float_cmp"]) + .args(["--", "-D", "warnings", "-D", "clippy::float_cmp"]) .output() } } @@ -183,7 +183,7 @@ path = "{}.rs""#, Mode::Test => "--show-output", _ => "", }; - let cmd = Command::new(&temp_file()) + let cmd = Command::new(temp_file()) .arg(arg) .output() .expect("Failed to run 'run' command"); @@ -260,7 +260,7 @@ impl Display for Exercise { #[inline] fn clean() { - let _ignored = remove_file(&temp_file()); + let _ignored = remove_file(temp_file()); } #[cfg(test)] @@ -270,7 +270,7 @@ mod test { #[test] fn test_clean() { - File::create(&temp_file()).unwrap(); + File::create(temp_file()).unwrap(); let exercise = Exercise { name: String::from("example"), path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), diff --git a/src/main.rs b/src/main.rs index 0a9af2ec09..1a15086718 100644 --- a/src/main.rs +++ b/src/main.rs @@ -169,7 +169,7 @@ fn main() { let filter_cond = filters .split(',') .filter(|f| !f.trim().is_empty()) - .any(|f| e.name.contains(&f) || fname.contains(&f)); + .any(|f| e.name.contains(f) || fname.contains(f)); let status = if e.looks_done() { exercises_done += 1; "Done" @@ -429,7 +429,7 @@ fn watch( fn rustc_exists() -> bool { Command::new("rustc") - .args(&["--version"]) + .args(["--version"]) .stdout(Stdio::null()) .spawn() .and_then(|mut child| child.wait()) @@ -465,7 +465,7 @@ started, here's a couple of notes about how Rustlings operates: Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. Make sure to have your editor open!"#; -const FENISH_LINE: &str = r#"+----------------------------------------------------+ +const FENISH_LINE: &str = r"+----------------------------------------------------+ | You made it to the Fe-nish line! | +-------------------------- ------------------------+ \\/ @@ -490,12 +490,12 @@ If you noticed any issues, please don't hesitate to report them to our repo. You can also contribute your own exercises to help the greater community! Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"#; +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; -const WELCOME: &str = r#" welcome to... +const WELCOME: &str = r" welcome to... _ _ _ _ __ _ _ ___| |_| (_)_ __ __ _ ___ | '__| | | / __| __| | | '_ \ / _` / __| | | | |_| \__ \ |_| | | | | | (_| \__ \ |_| \__,_|___/\__|_|_|_| |_|\__, |___/ - |___/"#; + |___/"; diff --git a/src/project.rs b/src/project.rs index ebebe27d3a..bcbd7ada84 100644 --- a/src/project.rs +++ b/src/project.rs @@ -86,7 +86,7 @@ impl RustAnalyzerProject { println!("Determined toolchain: {}\n", &toolchain); - self.sysroot_src = (std::path::Path::new(&*toolchain) + self.sysroot_src = (std::path::Path::new(toolchain) .join("lib") .join("rustlib") .join("src") diff --git a/src/verify.rs b/src/verify.rs index f3f3b564a5..4e0df867bb 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -123,9 +123,9 @@ fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, succe // Compile the given Exercise and return an object with information // about the state of the compilation -fn compile<'a, 'b>( +fn compile<'a>( exercise: &'a Exercise, - progress_bar: &'b ProgressBar, + progress_bar: &ProgressBar, ) -> Result, ()> { let compilation_result = exercise.compile(); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 1a729232f6..4c236fd067 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -44,7 +44,7 @@ fn verify_fails_if_some_fails() { fn run_single_compile_success() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "compSuccess"]) + .args(["run", "compSuccess"]) .current_dir("tests/fixture/success/") .assert() .success(); @@ -54,7 +54,7 @@ fn run_single_compile_success() { fn run_single_compile_failure() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "compFailure"]) + .args(["run", "compFailure"]) .current_dir("tests/fixture/failure/") .assert() .code(1); @@ -64,7 +64,7 @@ fn run_single_compile_failure() { fn run_single_test_success() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "testSuccess"]) + .args(["run", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .success(); @@ -74,7 +74,7 @@ fn run_single_test_success() { fn run_single_test_failure() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "testFailure"]) + .args(["run", "testFailure"]) .current_dir("tests/fixture/failure/") .assert() .code(1); @@ -84,7 +84,7 @@ fn run_single_test_failure() { fn run_single_test_not_passed() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "testNotPassed.rs"]) + .args(["run", "testNotPassed.rs"]) .current_dir("tests/fixture/failure/") .assert() .code(1); @@ -104,7 +104,7 @@ fn run_single_test_no_filename() { fn run_single_test_no_exercise() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "compNoExercise.rs"]) + .args(["run", "compNoExercise.rs"]) .current_dir("tests/fixture/failure") .assert() .code(1); @@ -114,7 +114,7 @@ fn run_single_test_no_exercise() { fn reset_single_exercise() { Command::cargo_bin("rustlings") .unwrap() - .args(&["reset", "intro1"]) + .args(["reset", "intro1"]) .assert() .code(0); } @@ -135,7 +135,7 @@ fn reset_no_exercise() { fn get_hint_for_single_test() { Command::cargo_bin("rustlings") .unwrap() - .args(&["hint", "testFailure"]) + .args(["hint", "testFailure"]) .current_dir("tests/fixture/failure") .assert() .code(0) @@ -171,7 +171,7 @@ fn all_exercises_require_confirmation() { fn run_compile_exercise_does_not_prompt() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "pending_exercise"]) + .args(["run", "pending_exercise"]) .current_dir("tests/fixture/state") .assert() .code(0) @@ -182,7 +182,7 @@ fn run_compile_exercise_does_not_prompt() { fn run_test_exercise_does_not_prompt() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "pending_test_exercise"]) + .args(["run", "pending_test_exercise"]) .current_dir("tests/fixture/state") .assert() .code(0) @@ -193,7 +193,7 @@ fn run_test_exercise_does_not_prompt() { fn run_single_test_success_with_output() { Command::cargo_bin("rustlings") .unwrap() - .args(&["--nocapture", "run", "testSuccess"]) + .args(["--nocapture", "run", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .code(0) @@ -204,7 +204,7 @@ fn run_single_test_success_with_output() { fn run_single_test_success_without_output() { Command::cargo_bin("rustlings") .unwrap() - .args(&["run", "testSuccess"]) + .args(["run", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .code(0) @@ -215,7 +215,7 @@ fn run_single_test_success_without_output() { fn run_rustlings_list() { Command::cargo_bin("rustlings") .unwrap() - .args(&["list"]) + .args(["list"]) .current_dir("tests/fixture/success") .assert() .success(); @@ -225,7 +225,7 @@ fn run_rustlings_list() { fn run_rustlings_list_no_pending() { Command::cargo_bin("rustlings") .unwrap() - .args(&["list"]) + .args(["list"]) .current_dir("tests/fixture/success") .assert() .success() @@ -236,7 +236,7 @@ fn run_rustlings_list_no_pending() { fn run_rustlings_list_both_done_and_pending() { Command::cargo_bin("rustlings") .unwrap() - .args(&["list"]) + .args(["list"]) .current_dir("tests/fixture/state") .assert() .success() @@ -247,7 +247,7 @@ fn run_rustlings_list_both_done_and_pending() { fn run_rustlings_list_without_pending() { Command::cargo_bin("rustlings") .unwrap() - .args(&["list", "--solved"]) + .args(["list", "--solved"]) .current_dir("tests/fixture/state") .assert() .success() @@ -258,7 +258,7 @@ fn run_rustlings_list_without_pending() { fn run_rustlings_list_without_done() { Command::cargo_bin("rustlings") .unwrap() - .args(&["list", "--unsolved"]) + .args(["list", "--unsolved"]) .current_dir("tests/fixture/state") .assert() .success() From 3cc9be0d115c8fd614dff9ab982c84c3d645173c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 26 Aug 2023 23:25:12 +0200 Subject: [PATCH 0341/1432] Avoid line numbers in hints --- info.toml | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/info.toml b/info.toml index e8a28cbc63..1afe3382f5 100644 --- a/info.toml +++ b/info.toml @@ -22,8 +22,8 @@ name = "variables1" path = "exercises/variables/variables1.rs" mode = "compile" hint = """ -The declaration on line 8 is missing a keyword that is needed in Rust -to create a new variable binding.""" +The declaration in the first line in the main function is missing a keyword +that is needed in Rust to create a new variable binding.""" [[exercises]] name = "variables2" @@ -32,7 +32,7 @@ mode = "compile" hint = """ The compiler message is saying that Rust cannot infer the type that the variable binding `x` has with what is given here. -What happens if you annotate line 7 with a type annotation? +What happens if you annotate the first line in the main function with a type annotation? What if you give x a value? What if you do both? What type should x be, anyway? @@ -44,8 +44,9 @@ path = "exercises/variables/variables3.rs" mode = "compile" hint = """ Oops! In this exercise, we have a variable binding that we've created on -line 7, and we're trying to use it on line 8, but we haven't given it a -value. We can't print out something that isn't there; try giving x a value! +in the first line in the main function, and we're trying to use it in the next line, +but we haven't given it a value. +We can't print out something that isn't there; try giving x a value! This is an error that can cause bugs that's very easy to make in any programming language -- thankfully the Rust compiler has caught this for us!""" @@ -123,8 +124,8 @@ name = "functions4" path = "exercises/functions/functions4.rs" mode = "compile" hint = """ -The error message points to line 17 and says it expects a type after the -`->`. This is where the function's return type should be -- take a look at +The error message points to the function `sale_price` and says it expects a type +after the `->`. This is where the function's return type should be -- take a look at the `is_even` function for an example! Also: Did you figure out that, technically, u32 would be the more fitting type @@ -285,9 +286,10 @@ name = "move_semantics1" path = "exercises/move_semantics/move_semantics1.rs" mode = "compile" hint = """ -So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 13, -right? The fix for this is going to be adding one keyword, and the addition is NOT on line 13 -where the error is. +So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on the line +where we push an element to the vector, right? +The fix for this is going to be adding one keyword, and the addition is NOT on the line where +we push to the vector (where the error is). Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!""" @@ -445,8 +447,9 @@ path = "exercises/strings/strings2.rs" mode = "compile" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a -string slice instead of a `String`, wouldn't it?? There is a way to add one character to line -12, though, that will coerce the `String` into a string slice. +string slice instead of a `String`, wouldn't it?? There is a way to add one character to the +line with the function call `is_a_color_word`, though, that will coerce the `String` into a +string slice. Side note: If you're interested in learning about how this kind of reference conversion works, you can jump ahead in the book and read this part in the smart pointers chapter: https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" @@ -825,7 +828,6 @@ To handle that you need to add a special attribute to the test function. You can refer to the docs: https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" - # STANDARD LIBRARY TYPES [[exercises]] From c655612d2d151cbde90a8d054f3b72df2d5d7b59 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 26 Aug 2023 23:34:40 +0200 Subject: [PATCH 0342/1432] Update deps --- Cargo.lock | 317 ++++++++++++++++++++++++++++++++++++----------------- Cargo.toml | 18 +-- 2 files changed, 224 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8268d90a0..5a40b6f168 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,24 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" dependencies = [ "memchr", ] +[[package]] +name = "anstyle" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" + [[package]] name = "argh" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" dependencies = [ "argh_derive", "argh_shared", @@ -23,32 +29,38 @@ dependencies = [ [[package]] name = "argh_derive" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "argh_shared" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +dependencies = [ + "serde", +] [[package]] name = "assert_cmd" -version = "0.11.1" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc477793bd82ec39799b6f6b3df64938532fdf2ab0d49ef817eac65856a5a1e" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ - "escargot", + "anstyle", + "bstr", + "doc-comment", "predicates", "predicates-core", "predicates-tree", + "wait-timeout", ] [[package]] @@ -63,6 +75,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -89,10 +112,22 @@ dependencies = [ ] [[package]] -name = "difference" -version = "2.0.0" +name = "difflib" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encode_unicode" @@ -101,22 +136,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "escargot" -version = "0.4.0" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceb9adbf9874d5d028b5e4c5739d22b71988252b25c9c98fe7cf9738bee84597" -dependencies = [ - "lazy_static", - "log", - "serde", - "serde_json", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if 1.0.0", "libc", @@ -126,9 +155,9 @@ dependencies = [ [[package]] name = "float-cmp" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] @@ -174,6 +203,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "home" version = "0.5.5" @@ -183,16 +218,27 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "indicatif" -version = "0.16.2" +version = "0.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" +checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" dependencies = [ "console", - "lazy_static", + "instant", "number_prefix", - "regex", + "portable-atomic", + "unicode-width", ] [[package]] @@ -215,6 +261,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -224,11 +279,20 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "kernel32-sys" @@ -260,9 +324,9 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" @@ -350,9 +414,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -363,14 +427,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "portable-atomic" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" + [[package]] name = "predicates" -version = "1.0.8" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" dependencies = [ - "difference", + "anstyle", + "difflib", "float-cmp", + "itertools", "normalize-line-endings", "predicates-core", "regex", @@ -394,36 +466,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.9.1" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" dependencies = [ "aho-corasick", "memchr", @@ -433,9 +505,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" dependencies = [ "aho-corasick", "memchr", @@ -444,9 +516,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rustlings" @@ -468,9 +540,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" @@ -483,29 +555,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn", ] [[package]] name = "serde_json" -version = "1.0.102" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -513,30 +585,28 @@ dependencies = [ ] [[package]] -name = "slab" -version = "0.4.8" +name = "serde_spanned" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ - "autocfg", + "serde", ] [[package]] -name = "syn" -version = "1.0.109" +name = "slab" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "autocfg", ] [[package]] name = "syn" -version = "2.0.25" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -551,18 +621,43 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "toml" -version = "0.5.11" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ + "indexmap", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-width" @@ -570,6 +665,15 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.3.3" @@ -638,7 +742,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -658,17 +762,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -679,9 +783,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -691,9 +795,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -703,9 +807,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -715,9 +819,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -727,9 +831,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -739,9 +843,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -751,9 +855,18 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr", +] [[package]] name = "ws2_32-sys" diff --git a/Cargo.toml b/Cargo.toml index eca091f429..f627dd794f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,21 +10,21 @@ edition = "2021" [dependencies] argh = "0.1" -indicatif = "0.16" +indicatif = "0.17" console = "0.15" notify = "4.0" -toml = "0.5" -regex = "1.5" +toml = "0.7" +regex = "1.9" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.81" -home = "0.5.3" -glob = "0.3.0" +serde_json = "1.0.105" +home = "0.5.5" +glob = "0.3.1" [[bin]] name = "rustlings" path = "src/main.rs" [dev-dependencies] -assert_cmd = "0.11.0" -predicates = "1.0.1" -glob = "0.3.0" +assert_cmd = "2.0.12" +predicates = "3.0.3" +glob = "0.3.1" From c0b8af2c42c5e116cb9f91b42da4dd5d92baeaed Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 26 Aug 2023 23:35:07 +0200 Subject: [PATCH 0343/1432] Fix indicatif --- src/run.rs | 3 ++- src/verify.rs | 35 +++++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/run.rs b/src/run.rs index 1e2e56cfad..e0ada4c5c3 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,4 +1,5 @@ use std::process::Command; +use std::time::Duration; use crate::exercise::{Exercise, Mode}; use crate::verify::test; @@ -36,7 +37,7 @@ pub fn reset(exercise: &Exercise) -> Result<(), ()> { fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Compiling {exercise}...")); - progress_bar.enable_steady_tick(100); + progress_bar.enable_steady_tick(Duration::from_millis(100)); let compilation_result = exercise.compile(); let compilation = match compilation_result { diff --git a/src/verify.rs b/src/verify.rs index f3f3b564a5..01dd87fa02 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,7 +1,7 @@ use crate::exercise::{CompiledExercise, Exercise, Mode, State}; use console::style; use indicatif::{ProgressBar, ProgressStyle}; -use std::env; +use std::{env, time::Duration}; // Verify that the provided container of Exercise objects // can be compiled and run without any failures. @@ -17,9 +17,11 @@ pub fn verify<'a>( let (num_done, total) = progress; let bar = ProgressBar::new(total as u64); let mut percentage = num_done as f32 / total as f32 * 100.0; - bar.set_style(ProgressStyle::default_bar() - .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") - .progress_chars("#>-") + bar.set_style( + ProgressStyle::default_bar() + .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") + .expect("Progressbar template should be valid!") + .progress_chars("#>-"), ); bar.set_position(num_done as u64); bar.set_message(format!("({:.1} %)", percentage)); @@ -55,7 +57,7 @@ pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> { fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Compiling {exercise}...")); - progress_bar.enable_steady_tick(100); + progress_bar.enable_steady_tick(Duration::from_millis(100)); let _ = compile(exercise, &progress_bar)?; progress_bar.finish_and_clear(); @@ -67,7 +69,7 @@ fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Compiling {exercise}...")); - progress_bar.enable_steady_tick(100); + progress_bar.enable_steady_tick(Duration::from_millis(100)); let compilation = compile(exercise, &progress_bar)?; @@ -85,15 +87,24 @@ fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Re } }; - Ok(prompt_for_completion(exercise, Some(output.stdout), success_hints)) + Ok(prompt_for_completion( + exercise, + Some(output.stdout), + success_hints, + )) } // Compile the given Exercise as a test harness and display // the output if verbose is set to true -fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, success_hints: bool) -> Result { +fn compile_and_test( + exercise: &Exercise, + run_mode: RunMode, + verbose: bool, + success_hints: bool, +) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Testing {exercise}...")); - progress_bar.enable_steady_tick(100); + progress_bar.enable_steady_tick(Duration::from_millis(100)); let compilation = compile(exercise, &progress_bar)?; let result = compilation.run(); @@ -143,7 +154,11 @@ fn compile<'a, 'b>( } } -fn prompt_for_completion(exercise: &Exercise, prompt_output: Option, success_hints: bool) -> bool { +fn prompt_for_completion( + exercise: &Exercise, + prompt_output: Option, + success_hints: bool, +) -> bool { let context = match exercise.state() { State::Done => return true, State::Pending(context) => context, From 64224d3918820ebcdd2ee23fe2e7678f94573345 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 27 Aug 2023 00:46:48 +0200 Subject: [PATCH 0344/1432] Make move_semantics5 a test --- exercises/move_semantics/move_semantics5.rs | 1 + info.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/move_semantics/move_semantics5.rs b/exercises/move_semantics/move_semantics5.rs index 68db09eb38..267bdccc24 100644 --- a/exercises/move_semantics/move_semantics5.rs +++ b/exercises/move_semantics/move_semantics5.rs @@ -8,6 +8,7 @@ // I AM NOT DONE +#[test] fn main() { let mut x = 100; let y = &mut x; diff --git a/info.toml b/info.toml index e8a28cbc63..9267d467f1 100644 --- a/info.toml +++ b/info.toml @@ -340,7 +340,7 @@ So the end goal is to: [[exercises]] name = "move_semantics5" path = "exercises/move_semantics/move_semantics5.rs" -mode = "compile" +mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of referent (x) immediately after From 16936d95d13df800f06c7af22069b5daf0c672e4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 27 Aug 2023 00:47:03 +0200 Subject: [PATCH 0345/1432] Fix typo --- exercises/move_semantics/move_semantics3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index ea21493442..16118f7b02 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -1,6 +1,6 @@ // move_semantics3.rs // -// Make me compile without adding new lines-- just changing existing lines! (no +// 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 From 193b600382ead22a24d2c26ca3a9117e7ad18be8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 27 Aug 2023 01:06:01 +0200 Subject: [PATCH 0346/1432] Convert other exercises that have assertions to test mode --- exercises/iterators/iterators1.rs | 1 + exercises/smart_pointers/rc1.rs | 1 + exercises/threads/threads3.rs | 1 + info.toml | 6 +++--- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/exercises/iterators/iterators1.rs b/exercises/iterators/iterators1.rs index b3f698be35..31076bb9da 100644 --- a/exercises/iterators/iterators1.rs +++ b/exercises/iterators/iterators1.rs @@ -11,6 +11,7 @@ // I AM NOT DONE +#[test] fn main() { let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; diff --git a/exercises/smart_pointers/rc1.rs b/exercises/smart_pointers/rc1.rs index ad3f1ce292..1b903469aa 100644 --- a/exercises/smart_pointers/rc1.rs +++ b/exercises/smart_pointers/rc1.rs @@ -35,6 +35,7 @@ impl Planet { } } +#[test] fn main() { let sun = Rc::new(Sun {}); println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference diff --git a/exercises/threads/threads3.rs b/exercises/threads/threads3.rs index db7d41ba6a..91006bbcab 100644 --- a/exercises/threads/threads3.rs +++ b/exercises/threads/threads3.rs @@ -48,6 +48,7 @@ fn send_tx(q: Queue, tx: mpsc::Sender) -> () { }); } +#[test] fn main() { let (tx, rx) = mpsc::channel(); let queue = Queue::new(); diff --git a/info.toml b/info.toml index 9267d467f1..452e47875f 100644 --- a/info.toml +++ b/info.toml @@ -831,7 +831,7 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa [[exercises]] name = "iterators1" path = "exercises/iterators/iterators1.rs" -mode = "compile" +mode = "test" hint = """ Step 1: We need to apply something to the collection `my_fav_fruits` before we start to go through @@ -936,7 +936,7 @@ and try other types! [[exercises]] name = "rc1" path = "exercises/smart_pointers/rc1.rs" -mode = "compile" +mode = "test" hint = """ This is a straightforward exercise to use the Rc type. Each Planet has ownership of the Sun, and uses Rc::clone() to increment the reference count of the Sun. @@ -1025,7 +1025,7 @@ what you've learned :)""" [[exercises]] name = "threads3" path = "exercises/threads/threads3.rs" -mode = "compile" +mode = "test" hint = """ An alternate way to handle concurrency between threads is to use a mpsc (multiple producer, single consumer) channel to communicate. From 7bb69f864188906b3731725df08157131bfe9e57 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:37:33 +0000 Subject: [PATCH 0347/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index e01c19c325..ed165b75cd 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -324,6 +324,7 @@ authors. Alon Hearter
Alon Hearter

πŸ–‹ shirts
shirts

πŸ–‹ Ivan Vasiunyk
Ivan Vasiunyk

πŸ–‹ + Mo
Mo

πŸ’» From d258c30406ff17cb06b18128eac8eff44a594e99 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:37:34 +0000 Subject: [PATCH 0348/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 22d6841891..c3df1ad013 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2289,6 +2289,15 @@ "contributions": [ "content" ] + }, + { + "login": "mo8it", + "name": "Mo", + "avatar_url": "https://avatars.githubusercontent.com/u/76752051?v=4", + "profile": "https://mo8it.com", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 9c0581bc0faea12b790e0fa0cafe8d08a5ed028e Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 29 Aug 2023 00:52:11 +0200 Subject: [PATCH 0349/1432] Use u32 instead of i32 --- exercises/structs/structs3.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs index 4851317c5f..7cda5af103 100644 --- a/exercises/structs/structs3.rs +++ b/exercises/structs/structs3.rs @@ -13,13 +13,15 @@ struct Package { sender_country: String, recipient_country: String, - weight_in_grams: i32, + weight_in_grams: u32, } impl Package { - fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package { - if weight_in_grams <= 0 { - panic!("Can not ship a weightless package.") + fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Package { + if weight_in_grams < 10 { + // This is not how you should handle errors in Rust, + // but we will learn about error handling later. + panic!("Can not ship a package with weight below 10 grams.") } else { Package { sender_country, @@ -33,7 +35,7 @@ impl Package { // Something goes here... } - fn get_fees(&self, cents_per_gram: i32) -> ??? { + fn get_fees(&self, cents_per_gram: u32) -> ??? { // Something goes here... } } @@ -48,7 +50,7 @@ mod tests { let sender_country = String::from("Spain"); let recipient_country = String::from("Austria"); - Package::new(sender_country, recipient_country, -2210); + Package::new(sender_country, recipient_country, 5); } #[test] From 013f22c89cba7dbd935b58392a1a7d8cdcdc7eb7 Mon Sep 17 00:00:00 2001 From: x10an14 Date: Wed, 30 Aug 2023 18:03:45 +0200 Subject: [PATCH 0350/1432] improvement(development): Add nix-direnv integration So as to automatically open a nix devShell for those who use direnv/want to use direnv to make their lives more automated. --- .envrc | 4 ++++ .gitignore | 1 + 2 files changed, 5 insertions(+) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..48541064fd --- /dev/null +++ b/.envrc @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# Automatically Load nix devShell w/dotenv +use flake diff --git a/.gitignore b/.gitignore index 88bf2b6c7b..f319d39dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ rust-project.json *.iml *.o public/ +.direnv/ # Local Netlify folder .netlify From 60b848760033ee3f7a6522929f7b4aa21064c5e2 Mon Sep 17 00:00:00 2001 From: x10an14 Date: Wed, 30 Aug 2023 18:09:41 +0200 Subject: [PATCH 0351/1432] feat(flake): Add defaults to commands in flake So that: - `nix build .#`, and - `nix run .#` both work. --- flake.nix | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/flake.nix b/flake.nix index 5dbca5c2ac..791c36ff9c 100644 --- a/flake.nix +++ b/flake.nix @@ -60,5 +60,18 @@ clippy ] ++ cargoBuildInputs; }; + apps = let + rustlings-app = { + type = "app"; + program = "${rustlings}/bin/rustlings"; + }; + in { + default = rustlings-app; + rustlings = rustlings-app; + }; + packages = { + inherit rustlings; + default = rustlings; + }; }); } From e7bdc83a8f910b239a0d910410a9c95ff5fd99d3 Mon Sep 17 00:00:00 2001 From: x10an14 Date: Wed, 30 Aug 2023 19:41:29 +0200 Subject: [PATCH 0352/1432] fix(nix/tests): Add `git` to Nix's rust build/test sandbox The integration test `reset_single_exercise` depends on Git... --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 5dbca5c2ac..c6360eec82 100644 --- a/flake.nix +++ b/flake.nix @@ -25,6 +25,7 @@ version = "5.5.1"; buildInputs = cargoBuildInputs; + nativeBuildInputs = [pkgs.git]; src = with pkgs.lib; cleanSourceWith { src = self; From 5a93f2a4f14d83d34aac69254262da7aaa5b752c Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 25 Aug 2023 23:18:01 +0200 Subject: [PATCH 0353/1432] Port to Clap --- Cargo.lock | 131 +++++++++++++++++++++++++++++++---- Cargo.toml | 15 ++-- src/main.rs | 195 +++++++++++++++++++++------------------------------- 3 files changed, 207 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a40b6f168..e5b8553082 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,61 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "argh" version = "0.1.12" @@ -77,9 +119,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bstr" -version = "1.6.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", "regex-automata", @@ -98,6 +140,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "console" version = "0.15.7" @@ -209,6 +297,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "home" version = "0.5.5" @@ -330,9 +424,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "mio" @@ -493,9 +587,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", @@ -505,9 +599,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", @@ -526,6 +620,7 @@ version = "5.5.1" dependencies = [ "argh", "assert_cmd", + "clap", "console", "glob", "home", @@ -602,11 +697,17 @@ dependencies = [ "autocfg", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" -version = "2.0.29" +version = "2.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" dependencies = [ "proc-macro2", "quote", @@ -665,6 +766,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index f627dd794f..2c2a92b220 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,15 +10,16 @@ edition = "2021" [dependencies] argh = "0.1" -indicatif = "0.17" +indicatif = "0.17.6" console = "0.15" notify = "4.0" -toml = "0.7" -regex = "1.9" +toml = "0.7.6" +regex = "1.5" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.105" -home = "0.5.5" -glob = "0.3.1" +serde_json = "1.0.81" +home = "0.5.3" +glob = "0.3.0" +clap = { version = "4.4.0", features = ["derive"] } [[bin]] name = "rustlings" @@ -27,4 +28,4 @@ path = "src/main.rs" [dev-dependencies] assert_cmd = "2.0.12" predicates = "3.0.3" -glob = "0.3.1" +glob = "0.3.0" diff --git a/src/main.rs b/src/main.rs index 1a15086718..a4b764d764 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use crate::exercise::{Exercise, ExerciseList}; use crate::project::RustAnalyzerProject; use crate::run::{reset, run}; use crate::verify::verify; -use argh::FromArgs; +use clap::{Parser, Subcommand}; use console::Emoji; use notify::DebouncedEvent; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; @@ -25,111 +25,69 @@ mod project; mod run; mod verify; -// In sync with crate version -const VERSION: &str = "5.5.1"; - -#[derive(FromArgs, PartialEq, Debug)] /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code +#[derive(Parser)] +#[command(version)] struct Args { - /// show outputs from the test exercises - #[argh(switch)] + /// Show outputs from the test exercises + #[arg(long)] nocapture: bool, - /// show the executable version - #[argh(switch, short = 'v')] - version: bool, - #[argh(subcommand)] - nested: Option, + #[command(subcommand)] + command: Option, } -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand)] +#[derive(Subcommand)] enum Subcommands { - Verify(VerifyArgs), - Watch(WatchArgs), - Run(RunArgs), - Reset(ResetArgs), - Hint(HintArgs), - List(ListArgs), - Lsp(LspArgs), -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "verify")] -/// Verifies all exercises according to the recommended order -struct VerifyArgs {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "watch")] -/// Reruns `verify` when files were edited -struct WatchArgs { - /// show hints on success - #[argh(switch)] - success_hints: bool, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "run")] -/// Runs/Tests a single exercise -struct RunArgs { - #[argh(positional)] - /// the name of the exercise - name: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "reset")] -/// Resets a single exercise using "git stash -- " -struct ResetArgs { - #[argh(positional)] - /// the name of the exercise - name: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "hint")] -/// Returns a hint for the given exercise -struct HintArgs { - #[argh(positional)] - /// the name of the exercise - name: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "lsp")] -/// Enable rust-analyzer for exercises -struct LspArgs {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "list")] -/// Lists the exercises available in Rustlings -struct ListArgs { - #[argh(switch, short = 'p')] - /// show only the paths of the exercises - paths: bool, - #[argh(switch, short = 'n')] - /// show only the names of the exercises - names: bool, - #[argh(option, short = 'f')] - /// provide a string to match exercise names - /// comma separated patterns are acceptable - filter: Option, - #[argh(switch, short = 'u')] - /// display only exercises not yet solved - unsolved: bool, - #[argh(switch, short = 's')] - /// display only exercises that have been solved - solved: bool, + /// Verify all exercises according to the recommended order + Verify, + /// Rerun `verify` when files were edited + Watch { + /// Show hints on success + #[arg(long)] + success_hints: bool, + }, + /// Run/Test a single exercise + Run { + /// The name of the exercise + name: String, + }, + /// Reset a single exercise using "git stash -- " + Reset { + /// The name of the exercise + name: String, + }, + /// Return a hint for the given exercise + Hint { + /// The name of the exercise + name: String, + }, + /// List the exercises available in Rustlings + List { + /// Show only the paths of the exercises + #[arg(short, long)] + paths: bool, + /// Show only the names of the exercises + #[arg(short, long)] + names: bool, + /// Provide a string to match exercise names. + /// Comma separated patterns are accepted + #[arg(short, long)] + filter: Option, + /// Display only exercises not yet solved + #[arg(short, long)] + unsolved: bool, + /// Display only exercises that have been solved + #[arg(short, long)] + solved: bool, + }, + /// Enable rust-analyzer for exercises + Lsp, } fn main() { - let args: Args = argh::from_env(); + let args = Args::parse(); - if args.version { - println!("v{VERSION}"); - std::process::exit(0); - } - - if args.nested.is_none() { + if args.command.is_none() { println!("\n{WELCOME}\n"); } @@ -153,17 +111,24 @@ fn main() { let exercises = toml::from_str::(toml_str).unwrap().exercises; let verbose = args.nocapture; - let command = args.nested.unwrap_or_else(|| { + let command = args.command.unwrap_or_else(|| { println!("{DEFAULT_OUT}\n"); std::process::exit(0); }); + match command { - Subcommands::List(subargs) => { - if !subargs.paths && !subargs.names { + Subcommands::List { + paths, + names, + filter, + unsolved, + solved, + } => { + if !paths && !names { println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); } let mut exercises_done: u16 = 0; - let filters = subargs.filter.clone().unwrap_or_default().to_lowercase(); + let filters = filter.clone().unwrap_or_default().to_lowercase(); exercises.iter().for_each(|e| { let fname = format!("{}", e.path.display()); let filter_cond = filters @@ -177,14 +142,14 @@ fn main() { "Pending" }; let solve_cond = { - (e.looks_done() && subargs.solved) - || (!e.looks_done() && subargs.unsolved) - || (!subargs.solved && !subargs.unsolved) + (e.looks_done() && solved) + || (!e.looks_done() && unsolved) + || (!solved && !unsolved) }; - if solve_cond && (filter_cond || subargs.filter.is_none()) { - let line = if subargs.paths { + if solve_cond && (filter_cond || filter.is_none()) { + let line = if paths { format!("{fname}\n") - } else if subargs.names { + } else if names { format!("{}\n", e.name) } else { format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name) @@ -214,30 +179,30 @@ fn main() { std::process::exit(0); } - Subcommands::Run(subargs) => { - let exercise = find_exercise(&subargs.name, &exercises); + Subcommands::Run { name } => { + let exercise = find_exercise(&name, &exercises); run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1)); } - Subcommands::Reset(subargs) => { - let exercise = find_exercise(&subargs.name, &exercises); + Subcommands::Reset { name } => { + let exercise = find_exercise(&name, &exercises); reset(exercise).unwrap_or_else(|_| std::process::exit(1)); } - Subcommands::Hint(subargs) => { - let exercise = find_exercise(&subargs.name, &exercises); + Subcommands::Hint { name } => { + let exercise = find_exercise(&name, &exercises); println!("{}", exercise.hint); } - Subcommands::Verify(_subargs) => { + Subcommands::Verify => { verify(&exercises, (0, exercises.len()), verbose, false) .unwrap_or_else(|_| std::process::exit(1)); } - Subcommands::Lsp(_subargs) => { + Subcommands::Lsp => { let mut project = RustAnalyzerProject::new(); project .get_sysroot_src() @@ -256,7 +221,7 @@ fn main() { } } - Subcommands::Watch(_subargs) => match watch(&exercises, verbose, _subargs.success_hints) { + Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Err(e) => { println!( "Error: Could not watch your progress. Error message was {:?}.", From 362318a6e04a1ffa80fe85e1f4c1cf03686d1109 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 26 Aug 2023 00:00:56 +0200 Subject: [PATCH 0354/1432] Adapt tests --- tests/integration_tests.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 4c236fd067..d1694a396f 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -97,7 +97,10 @@ fn run_single_test_no_filename() { .arg("run") .current_dir("tests/fixture/") .assert() - .code(1); + .code(2) + .stderr(predicates::str::contains( + "required arguments were not provided", + )); } #[test] @@ -125,9 +128,9 @@ fn reset_no_exercise() { .unwrap() .arg("reset") .assert() - .code(1) + .code(2) .stderr(predicates::str::contains( - "positional arguments not provided", + "required arguments were not provided", )); } From 11f4ec93aceab5491e57cf10390b6838baaa2f98 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:37:09 +0000 Subject: [PATCH 0355/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index ed165b75cd..8cfab68aca 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -325,6 +325,7 @@ authors. shirts
shirts

πŸ–‹ Ivan Vasiunyk
Ivan Vasiunyk

πŸ–‹ Mo
Mo

πŸ’» + x10an14
x10an14

πŸš‡ From 4a9699226a1777a5a98cb37eaf1b09e3d2427d6f Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:37:10 +0000 Subject: [PATCH 0356/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index c3df1ad013..656bf1d544 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2298,6 +2298,15 @@ "contributions": [ "code" ] + }, + { + "login": "x10an14", + "name": "x10an14", + "avatar_url": "https://avatars.githubusercontent.com/u/710608?v=4", + "profile": "https://github.com/x10an14", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From a059ded709b838a62dc2544553d749409e0c0b7e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:46:41 +0000 Subject: [PATCH 0357/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 8cfab68aca..4b673e6fe9 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -326,6 +326,7 @@ authors. Ivan Vasiunyk
Ivan Vasiunyk

πŸ–‹ Mo
Mo

πŸ’» x10an14
x10an14

πŸš‡ + Roi Gabay
Roi Gabay

πŸ–‹ From 7d53abdb0af4771ccb0917550636ef2e4100bb80 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:46:42 +0000 Subject: [PATCH 0358/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 656bf1d544..c4aabed6b9 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2307,6 +2307,15 @@ "contributions": [ "infra" ] + }, + { + "login": "gabay", + "name": "Roi Gabay", + "avatar_url": "https://avatars.githubusercontent.com/u/5773610?v=4", + "profile": "https://github.com/gabay", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 27881a83d4b569871845261d57c9a65d0b1be440 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:52:11 +0000 Subject: [PATCH 0359/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4b673e6fe9..7bc44689f8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -327,6 +327,7 @@ authors. Mo
Mo

πŸ’» x10an14
x10an14

πŸš‡ Roi Gabay
Roi Gabay

πŸ–‹ + MΓ‘tΓ© KovΓ‘cs
MΓ‘tΓ© KovΓ‘cs

πŸ–‹ From 4aa5ca28183eb76b4e06bd6cfcfe1c0a80a93e48 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:52:13 +0000 Subject: [PATCH 0360/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index c4aabed6b9..6547936763 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2316,6 +2316,15 @@ "contributions": [ "content" ] + }, + { + "login": "mkovaxx", + "name": "MΓ‘tΓ© KovΓ‘cs", + "avatar_url": "https://avatars.githubusercontent.com/u/481354?v=4", + "profile": "https://github.com/mkovaxx", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From a09a0c47aec37215ed3ff5260ee77f732fc866de Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 13:53:54 +0200 Subject: [PATCH 0361/1432] fix: add extra line in if3 comment --- exercises/if/if3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/if/if3.rs b/exercises/if/if3.rs index 0040023618..169627406c 100644 --- a/exercises/if/if3.rs +++ b/exercises/if/if3.rs @@ -28,6 +28,7 @@ pub fn animal_habitat(animal: &str) -> &'static str { habitat } + // No test changes needed. #[cfg(test)] mod tests { From bb550497d5211ff71d1fa4867a4e4ce2e1a67d04 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:55:27 +0000 Subject: [PATCH 0362/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 7bc44689f8..cbf7436ae0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -328,6 +328,7 @@ authors. x10an14
x10an14

πŸš‡ Roi Gabay
Roi Gabay

πŸ–‹ MΓ‘tΓ© KovΓ‘cs
MΓ‘tΓ© KovΓ‘cs

πŸ–‹ + GΓ‘bor SzabΓ³
GΓ‘bor SzabΓ³

πŸ–‹ From 8ca60f2cbbe635d921b471f0e9b7965e000e7a08 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:55:28 +0000 Subject: [PATCH 0363/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 6547936763..f1b1fe11e7 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2325,6 +2325,15 @@ "contributions": [ "content" ] + }, + { + "login": "szabgab", + "name": "GΓ‘bor SzabΓ³", + "avatar_url": "https://avatars.githubusercontent.com/u/48833?v=4", + "profile": "https://szabgab.com/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 47e14cff8da2de6f5bb2d328ac56275e65d00dde Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:58:24 +0000 Subject: [PATCH 0364/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index cbf7436ae0..76d3a58c78 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -330,6 +330,9 @@ authors. MΓ‘tΓ© KovΓ‘cs
MΓ‘tΓ© KovΓ‘cs

πŸ–‹ GΓ‘bor SzabΓ³
GΓ‘bor SzabΓ³

πŸ–‹ + + Yamila Moreno
Yamila Moreno

πŸ–‹ + From dce89aefb99bc6d774c98e45121989253fa8c5a3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:58:25 +0000 Subject: [PATCH 0365/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f1b1fe11e7..8ceb8990de 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2334,6 +2334,15 @@ "contributions": [ "content" ] + }, + { + "login": "yamila-moreno", + "name": "Yamila Moreno", + "avatar_url": "https://avatars.githubusercontent.com/u/3340793?v=4", + "profile": "https://moduslaborandi.net", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 51e237d5f97610294798710ef8ba5349c2fd50c7 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 14:20:37 +0200 Subject: [PATCH 0366/1432] fix: refactor move semantics 1-4 into tests --- exercises/move_semantics/move_semantics1.rs | 15 +++++---------- exercises/move_semantics/move_semantics2.rs | 18 ++++++------------ exercises/move_semantics/move_semantics3.rs | 13 ++++--------- exercises/move_semantics/move_semantics4.rs | 16 ++++++---------- info.toml | 16 ++++++---------- 5 files changed, 27 insertions(+), 51 deletions(-) diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index 710d20d8a2..e063937539 100644 --- a/exercises/move_semantics/move_semantics1.rs +++ b/exercises/move_semantics/move_semantics1.rs @@ -5,24 +5,19 @@ // I AM NOT DONE +#[test] fn main() { - let vec0 = Vec::new(); + let vec0 = vec![22, 44, 66]; let vec1 = fill_vec(vec0); - println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); - - vec1.push(88); - - println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); + assert_eq!(vec1, vec![22, 44, 66, 88]); } fn fill_vec(vec: Vec) -> Vec { - let mut vec = vec; + let vec = vec; - vec.push(22); - vec.push(44); - vec.push(66); + vec.push(88); vec } diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 72d37fa413..baf6bcc953 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,32 +1,26 @@ // move_semantics2.rs // -// Expected output: -// vec0 has length 3, with contents `[22, 44, 66]` -// vec1 has length 4, with contents `[22, 44, 66, 88]` +// 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. // I AM NOT DONE +#[test] fn main() { - let vec0 = Vec::new(); + let vec0 = vec![22, 44, 66]; let mut vec1 = fill_vec(vec0); - println!("{} has length {}, with contents: `{:?}`", "vec0", vec0.len(), vec0); - - vec1.push(88); - - println!("{} has length {}, with contents `{:?}`", "vec1", vec1.len(), vec1); + assert_eq!(vec0, vec![22, 44, 66]); + assert_eq!(vec1, vec![22, 44, 66, 88]); } fn fill_vec(vec: Vec) -> Vec { let mut vec = vec; - vec.push(22); - vec.push(44); - vec.push(66); + vec.push(88); vec } diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index ea21493442..69e564abcd 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -8,22 +8,17 @@ // I AM NOT DONE +#[test] fn main() { - let vec0 = Vec::new(); + let vec0 = vec![22, 44, 66]; let mut vec1 = fill_vec(vec0); - println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); - - vec1.push(88); - - println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); + assert_eq!(vec1, vec![22, 44, 66, 88]); } fn fill_vec(vec: Vec) -> Vec { - vec.push(22); - vec.push(44); - vec.push(66); + vec.push(88); vec } diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/move_semantics/move_semantics4.rs index 75a3b6bd18..80b49dba26 100644 --- a/exercises/move_semantics/move_semantics4.rs +++ b/exercises/move_semantics/move_semantics4.rs @@ -9,25 +9,21 @@ // I AM NOT DONE +#[test] fn main() { - let vec0 = Vec::new(); + let vec0 = vec![22, 44, 66]; let mut vec1 = fill_vec(vec0); - println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); - - vec1.push(88); - - println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); + assert_eq!(vec1, vec![22, 44, 66, 88]); } -// `fill_vec()` no longer takes `vec: Vec` as argument +// `fill_vec()` no longer takes `vec: Vec` as argument - don't change this! fn fill_vec() -> Vec { + // Instead, let's create and fill the Vec in here - how do you do that? let mut vec = vec; - vec.push(22); - vec.push(44); - vec.push(66); + vec.push(88); vec } diff --git a/info.toml b/info.toml index 0aca02203e..b41985e153 100644 --- a/info.toml +++ b/info.toml @@ -284,9 +284,9 @@ better. What do you think is the more commonly used pattern under Rust developer [[exercises]] name = "move_semantics1" path = "exercises/move_semantics/move_semantics1.rs" -mode = "compile" +mode = "test" hint = """ -So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on the line +So you've got the "cannot borrow immutable local variable `vec` as mutable" error on the line where we push an element to the vector, right? The fix for this is going to be adding one keyword, and the addition is NOT on the line where we push to the vector (where the error is). @@ -296,7 +296,7 @@ Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!"" [[exercises]] name = "move_semantics2" path = "exercises/move_semantics/move_semantics2.rs" -mode = "compile" +mode = "test" hint = """ When running this exercise for the first time, you'll notice an error about "borrow of moved value". In Rust, when an argument is passed to a function and @@ -309,16 +309,12 @@ Rust provides a couple of different ways to mitigate this issue, feel free to tr 2. Make `fill_vec` borrow its argument instead of taking ownership of it, and then copy the data within the function (`vec.clone()`) in order to return an owned `Vec`. -3. Or, you could make `fill_vec` *mutably* borrow a reference to its argument (which will need to be - mutable), modify it directly, then not return anything. This means that `vec0` will change over the - course of the function, and makes `vec1` redundant (make sure to change the parameters of the `println!` - statements if you go this route) """ [[exercises]] name = "move_semantics3" path = "exercises/move_semantics/move_semantics3.rs" -mode = "compile" +mode = "test" hint = """ The difference between this one and the previous ones is that the first line of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, @@ -328,7 +324,7 @@ an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" path = "exercises/move_semantics/move_semantics4.rs" -mode = "compile" +mode = "test" hint = """ Stop reading whenever you feel like you have enough direction :) Or try doing one step and then fixing the compiler errors that result! @@ -337,7 +333,7 @@ So the end goal is to: - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` - `fill_vec` has had its signature changed, which our call should reflect - since we're not creating a new vec in `main` anymore, we need to create - a new vec in `fill_vec`, similarly to the way we did in `main`""" + a new vec in `fill_vec`, and fill it with the expected values""" [[exercises]] name = "move_semantics5" From e8c2d27b4af5d385ecad7d22cbf5763099e15f41 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:34:37 +0000 Subject: [PATCH 0367/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 76d3a58c78..831963a7b1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -332,6 +332,7 @@ authors. Yamila Moreno
Yamila Moreno

πŸ–‹ + Will Hack
Will Hack

πŸ–‹ From fcac2b553cef016344196019949f3ed27144d25a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:34:38 +0000 Subject: [PATCH 0368/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8ceb8990de..9ea25f2e51 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2343,6 +2343,15 @@ "contributions": [ "content" ] + }, + { + "login": "willhack", + "name": "Will Hack", + "avatar_url": "https://avatars.githubusercontent.com/u/18036720?v=4", + "profile": "https://github.com/willhack", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 9a0e5bd00355d56527ab9cb04c0bede2d117bbfc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:35:34 +0000 Subject: [PATCH 0369/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 831963a7b1..9c5b1166c8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -333,6 +333,7 @@ authors. Yamila Moreno
Yamila Moreno

πŸ–‹ Will Hack
Will Hack

πŸ–‹ + Michael
Michael

πŸ–‹ From b5473218685fef0d4ef027ce5781aeb8efa71ac1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:35:35 +0000 Subject: [PATCH 0370/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9ea25f2e51..40cebc1137 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2352,6 +2352,15 @@ "contributions": [ "content" ] + }, + { + "login": "bean5", + "name": "Michael", + "avatar_url": "https://avatars.githubusercontent.com/u/2052646?v=4", + "profile": "http://cancompute.tech", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 3f7ef6fe33ac5e898115157e249802f7f8511843 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:37:56 +0000 Subject: [PATCH 0371/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 9c5b1166c8..54ead91717 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -334,6 +334,7 @@ authors. Yamila Moreno
Yamila Moreno

πŸ–‹ Will Hack
Will Hack

πŸ–‹ Michael
Michael

πŸ–‹ + Mohammed Sadiq
Mohammed Sadiq

πŸ–‹ From d5525794f893714a01ccb28be24d84e1c992a64c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:37:58 +0000 Subject: [PATCH 0372/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 40cebc1137..1ad4d589b8 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2361,6 +2361,15 @@ "contributions": [ "content" ] + }, + { + "login": "pksadiq", + "name": "Mohammed Sadiq", + "avatar_url": "https://avatars.githubusercontent.com/u/1289514?v=4", + "profile": "https://www.sadiqpk.org", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 951dde4e949f6c176c11dd77dcb1a9021f2fdcbf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:39:45 +0000 Subject: [PATCH 0373/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 54ead91717..4e34f56f69 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -335,6 +335,7 @@ authors. Will Hack
Will Hack

πŸ–‹ Michael
Michael

πŸ–‹ Mohammed Sadiq
Mohammed Sadiq

πŸ–‹ + Jakob
Jakob

πŸ–‹ From 680a32a85c265b81ab7b84a79e90b677482ff170 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:39:46 +0000 Subject: [PATCH 0374/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 1ad4d589b8..b1db5fa470 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2370,6 +2370,15 @@ "contributions": [ "content" ] + }, + { + "login": "Jak-Ch-ll", + "name": "Jakob", + "avatar_url": "https://avatars.githubusercontent.com/u/56225668?v=4", + "profile": "https://github.com/Jak-Ch-ll", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 6c0c397507dd2e244167654f5ee46b68b5921a37 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 14:49:52 +0200 Subject: [PATCH 0375/1432] fix: absolut-ize readme links --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 300aee37f7..a7b7a9af0b 100644 --- a/README.md +++ b/README.md @@ -173,12 +173,8 @@ Now you should be done! ## Contributing -See [CONTRIBUTING.md](./CONTRIBUTING.md). - -Development-focused discussion about Rustlings happens in the [**rustlings** stream](https://rust-lang.zulipchat.com/#narrow/stream/334454-rustlings) -on the [Rust Project Zulip](https://rust-lang.zulipchat.com). Feel free to start a new thread there -if you have ideas or suggestions! +See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md). ## Contributors ✨ -Thanks goes to the wonderful people listed in [AUTHORS.md](./AUTHORS.md) πŸŽ‰ +Thanks goes to the wonderful people listed in [AUTHORS.md](https://github.com/rust-lang/rustlings/blob/main/AUTHORS.md) πŸŽ‰ From 3ad30308ec39dc6f108493fdca7dd133a8b28b8e Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 14:50:31 +0200 Subject: [PATCH 0376/1432] feat: add oranda deploy workflow --- .github/workflows/web.yml | 98 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/web.yml diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml new file mode 100644 index 0000000000..f20e24e452 --- /dev/null +++ b/.github/workflows/web.yml @@ -0,0 +1,98 @@ +# Workflow to build your docs with oranda (and mdbook) +# and deploy them to Github Pages +name: Web + +# We're going to push to the gh-pages branch, so we need that permission +permissions: + contents: write + +# What situations do we want to build docs in? +# All of these work independently and can be removed / commented out +# if you don't want oranda/mdbook running in that situation +on: + # Check that a PR didn't break docs! + # + # Note that the "Deploy to Github Pages" step won't run in this mode, + # so this won't have any side-effects. But it will tell you if a PR + # completely broke oranda/mdbook. Sadly we don't provide previews (yet)! + pull_request: + + # Whenever something gets pushed to main, update the docs! + # This is great for getting docs changes live without cutting a full release. + # + # Note that if you're using cargo-dist, this will "race" the Release workflow + # that actually builds the Github Release that oranda tries to read (and + # this will almost certainly complete first). As a result you will publish + # docs for the latest commit but the oranda landing page won't know about + # the latest release. The workflow_run trigger below will properly wait for + # cargo-dist, and so this half-published state will only last for ~10 minutes. + # + # If you only want docs to update with releases, disable this, or change it to + # a "release" branch. You can, of course, also manually trigger a workflow run + # when you want the docs to update. + push: + branches: + - main + + # Whenever a workflow called "Release" completes, update the docs! + # + # If you're using cargo-dist, this is recommended, as it will ensure that + # oranda always sees the latest release right when it's available. Note + # however that Github's UI is wonky when you use workflow_run, and won't + # show this workflow as part of any commit. You have to go to the "actions" + # tab for your repo to see this one running (the gh-pages deploy will also + # only show up there). + workflow_run: + workflows: [ "Release" ] + types: + - completed + +# Alright, let's do it! +jobs: + web: + name: Build and deploy site and docs + runs-on: ubuntu-latest + steps: + # Setup + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: dtolnay/rust-toolchain@stable + - uses: swatinem/rust-cache@v2 + + # If you use any mdbook plugins, here's the place to install them! + + # Install and run oranda (and mdbook) + # This will write all output to ./public/ (including copying mdbook's output to there) + - name: Install and run oranda + run: | + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/download/v0.3.1/oranda-installer.sh | sh + oranda build + + - name: Prepare HTML for link checking + # untitaker/hyperlink supports no site prefixes, move entire site into + # a subfolder + run: mkdir /tmp/public/ && cp -R public /tmp/public/oranda + + - name: Check HTML for broken internal links + uses: untitaker/hyperlink@0.1.29 + with: + args: /tmp/public/ --sources docs/ + + # Deploy to our gh-pages branch (creating it if it doesn't exist) + # the "public" dir that oranda made above will become the root dir + # of this branch. + # + # Note that once the gh-pages branch exists, you must + # go into repo's settings > pages and set "deploy from branch: gh-pages" + # the other defaults work fine. + - name: Deploy to Github Pages + uses: JamesIves/github-pages-deploy-action@v4.4.1 + # ONLY if we're on main (so no PRs or feature branches allowed!) + if: ${{ github.ref == 'refs/heads/main' }} + with: + branch: gh-pages + # Gotta tell the action where to find oranda's output + folder: public + token: ${{ secrets.GITHUB_TOKEN }} + single-commit: true \ No newline at end of file From f31a18429b051c265a3ffcdc1888dd4053e6a572 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 14:57:16 +0200 Subject: [PATCH 0377/1432] chore: consolidate CI workflows --- .github/workflows/lint.yml | 18 ------------------ .github/workflows/rust.yml | 31 +++++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 26 deletions(-) delete mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 67339d1b12..0000000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Lint - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: DavidAnson/markdownlint-cli2-action@v9 - with: - globs: "exercises/**/*.md" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1b244b1a2e..226d413726 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,13 +10,28 @@ env: CARGO_TERM_COLOR: always jobs: - build: + fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Fetch & maybe update Cargo.lock - run: cargo fetch --locked - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - uses: DavidAnson/markdownlint-cli2-action@v9 + with: + globs: "exercises/**/*.md" + - name: Run cargo fmt + run: | + cargo fmt --all -- --check + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - uses: swatinem/rust-cache@v2 + - name: Run cargo test + run: | + cargo test From 0d36050b364a2dfa342d4c00f820e67c38738726 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 14:58:13 +0200 Subject: [PATCH 0378/1432] chore: remove link checker --- .github/workflows/web.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index f20e24e452..5d9abe4f4e 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -68,17 +68,7 @@ jobs: run: | curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/download/v0.3.1/oranda-installer.sh | sh oranda build - - - name: Prepare HTML for link checking - # untitaker/hyperlink supports no site prefixes, move entire site into - # a subfolder - run: mkdir /tmp/public/ && cp -R public /tmp/public/oranda - - name: Check HTML for broken internal links - uses: untitaker/hyperlink@0.1.29 - with: - args: /tmp/public/ --sources docs/ - # Deploy to our gh-pages branch (creating it if it doesn't exist) # the "public" dir that oranda made above will become the root dir # of this branch. @@ -95,4 +85,4 @@ jobs: # Gotta tell the action where to find oranda's output folder: public token: ${{ secrets.GITHUB_TOKEN }} - single-commit: true \ No newline at end of file + single-commit: true From de45998f69ac95cd81175fef9e054c2c77ce82ab Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 15:02:02 +0200 Subject: [PATCH 0379/1432] chore: remove argh --- Cargo.lock | 32 -------------------------------- Cargo.toml | 1 - 2 files changed, 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5b8553082..f7c7a86bbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,37 +59,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "argh" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" -dependencies = [ - "argh_derive", - "argh_shared", -] - -[[package]] -name = "argh_derive" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" -dependencies = [ - "argh_shared", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "argh_shared" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" -dependencies = [ - "serde", -] - [[package]] name = "assert_cmd" version = "2.0.12" @@ -618,7 +587,6 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" name = "rustlings" version = "5.5.1" dependencies = [ - "argh", "assert_cmd", "clap", "console", diff --git a/Cargo.toml b/Cargo.toml index 2c2a92b220..85017f4907 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ authors = [ edition = "2021" [dependencies] -argh = "0.1" indicatif = "0.17.6" console = "0.15" notify = "4.0" From 58cabf2ebd9092fecab3cfe75c16cad08591eeec Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 15:23:04 +0200 Subject: [PATCH 0380/1432] release: 5.6.0 --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 8 ++++---- flake.nix | 2 +- oranda.json | 5 ++--- 6 files changed, 49 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a802faaff2..69262b3410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,43 @@ + +## 5.6.0 (2023-09-04) + +#### Added + +- New exercise: `if3`, teaching the user about `if let` statements. +- `hashmaps2`: Added an extra test function to check if the amount of fruits is higher than zero. +- `enums3`: Added a test for `Message`. +- `if1`: Added a test case to check equal values. +- `if3`: Added a note specifying that there are no test changes needed. + +#### Changed + +- Swapped the order of threads and smart pointer exercises. +- Rewrote the CLI to use `clap` - it's matured much since we switched to `argh` :) +- `structs3`: Switched from i32 to u32. +- `move_semantics`: Switched 1-4 to tests, and rewrote them to be way simpler, while still teaching about the same + concepts. + +#### Fixed + +- `iterators5`: + - Removed an outdated part of the hint. + - Renamed variables to use snake_case. +- `vecs2`: Updated the hint to reference the renamed loop variable. +- `enums3`: Changed message string in test so that it gets properly tested. +- `strings2`: Corrected line number in hint, then removed it (this both happened as part of this release cycle). +- `primitive_types4`: Updated hint to the correct ending index. +- `quiz1`: Removed duplicated sentence from exercise comments. +- `errors4`: Improved comment. +- `from_into`: Fixed test values. +- `cow1`: Added `.to_mut()` to distinguish from the previous test case. +- `threads2`: Updated hint text to reference the correct book heading. + +#### Housekeeping + +- Cleaned up the explanation paragraphs at the start of each exercise. +- Lots of Nix housekeeping that I don't feel qualified to write about! +- Improved CI workflows, we're now testing on multiple platforms at once. + ## 5.5.1 (2023-05-17) diff --git a/Cargo.lock b/Cargo.lock index f7c7a86bbf..a8408edf6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -585,7 +585,7 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rustlings" -version = "5.5.1" +version = "5.6.0" dependencies = [ "assert_cmd", "clap", diff --git a/Cargo.toml b/Cargo.toml index 85017f4907..d64ef7568a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" -version = "5.5.1" +version = "5.6.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", diff --git a/README.md b/README.md index a7b7a9af0b..a16f7534d5 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1) -git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.0) +git clone -b 5.6.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings # if nix version > 2.3 nix develop @@ -79,8 +79,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1) -git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.0) +git clone -b 5.6.0 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/flake.nix b/flake.nix index f65c29077c..94f4177506 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; - version = "5.5.1"; + version = "5.6.0"; buildInputs = cargoBuildInputs; nativeBuildInputs = [pkgs.git]; diff --git a/oranda.json b/oranda.json index 603e474e91..be88e5e015 100644 --- a/oranda.json +++ b/oranda.json @@ -12,14 +12,13 @@ }, "components": { "artifacts": { - "cargo_dist": false, + "auto": true, "package_managers": { "preferred": { "macos/linux/unix": "curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash", "windows": "Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1" } } - }, - "changelog": true + } } } From 847b57423f2a2500b666ec0085cbd897b7b7139f Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 4 Sep 2023 15:26:22 +0200 Subject: [PATCH 0381/1432] update for page build From a5e41335161601acc3e86e0b9425c66c9af354c3 Mon Sep 17 00:00:00 2001 From: Jon Erling Hustadnes Date: Fri, 8 Sep 2023 16:42:16 +0200 Subject: [PATCH 0382/1432] Make primitive_types3 require at least 100 elements Made the function panic if it's not long enough --- exercises/primitive_types/primitive_types3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs index 06a7a621ae..8b0de44e96 100644 --- a/exercises/primitive_types/primitive_types3.rs +++ b/exercises/primitive_types/primitive_types3.rs @@ -14,5 +14,6 @@ fn main() { println!("Wow, that's a big array!"); } else { println!("Meh, I eat arrays like that for breakfast."); + panic!("Array not big enough, more elements needed") } } From 33a4f4e454031ea7b318c855625553b413b8afcd Mon Sep 17 00:00:00 2001 From: Oscar Bonilla <6f6231@gmail.com> Date: Fri, 8 Sep 2023 09:49:11 -0700 Subject: [PATCH 0383/1432] Fix compiler error and clarify instructions --- exercises/smart_pointers/cow1.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/smart_pointers/cow1.rs b/exercises/smart_pointers/cow1.rs index 5ab5115255..fcd3e0bb14 100644 --- a/exercises/smart_pointers/cow1.rs +++ b/exercises/smart_pointers/cow1.rs @@ -67,10 +67,10 @@ mod tests { #[test] fn owned_mutation() -> Result<(), &'static str> { // Of course this is also the case if a mutation does occur. In this - // case the call to `to_mut()` returns a reference to the same data as - // before. + // case the call to `to_mut()` in the abs_all() function returns a + // reference to the same data as before. let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice).to_mut(); + let mut input = Cow::from(slice); match abs_all(&mut input) { // TODO } From 47fbd6d16045023121672b8792764a5c6c6d3e01 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 23:23:31 +0000 Subject: [PATCH 0384/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4e34f56f69..1312a05640 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -336,6 +336,7 @@ authors. Michael
Michael

πŸ–‹ Mohammed Sadiq
Mohammed Sadiq

πŸ–‹ Jakob
Jakob

πŸ–‹ + Oscar Bonilla
Oscar Bonilla

πŸ–‹ From e17d603201458029defd88ebf61e24ed2e639bfb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 23:23:32 +0000 Subject: [PATCH 0385/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b1db5fa470..9f644f1f14 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2379,6 +2379,15 @@ "contributions": [ "content" ] + }, + { + "login": "ob", + "name": "Oscar Bonilla", + "avatar_url": "https://avatars.githubusercontent.com/u/4950?v=4", + "profile": "http://oscarbonilla.com", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From dceba07e829ab27c95600696b9972f8f82fb954d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 9 Sep 2023 12:58:35 +0000 Subject: [PATCH 0386/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 1312a05640..8887dc220e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -337,6 +337,7 @@ authors. Mohammed Sadiq
Mohammed Sadiq

πŸ–‹ Jakob
Jakob

πŸ–‹ Oscar Bonilla
Oscar Bonilla

πŸ–‹ + Jon Erling Hustadnes
Jon Erling Hustadnes

πŸ–‹ From b33ef03ac6ef38a31d5c22583b9eeaf71ee4acc6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 9 Sep 2023 12:58:36 +0000 Subject: [PATCH 0387/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9f644f1f14..95b50a7df8 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2388,6 +2388,15 @@ "contributions": [ "content" ] + }, + { + "login": "husjon", + "name": "Jon Erling Hustadnes", + "avatar_url": "https://avatars.githubusercontent.com/u/554229?v=4", + "profile": "https://github.com/husjon", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 4a1290cb669fe324439c8a58a4437f141f5b1641 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Mon, 11 Sep 2023 19:56:25 -0700 Subject: [PATCH 0388/1432] chore: update flake.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: β€’ Updated input 'flake-compat': 'github:edolstra/flake-compat/b4a34015c698c7793d592d66adbab377907a2be8' (2022-04-19) β†’ 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9' (2023-01-17) β€’ Updated input 'flake-utils': 'github:numtide/flake-utils/c0e246b9b83f637f4681389ecabcb2681b4f3af0' (2022-08-07) β†’ 'github:numtide/flake-utils/f9e7cf818399d17d347f847525c5a5a8032e4e44' (2023-08-23) β€’ Added input 'flake-utils/systems': 'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e' (2023-04-09) β€’ Updated input 'nixpkgs': 'github:nixos/nixpkgs/b39fd6e4edef83cb4a135ebef98751ce23becc33' (2022-10-24) β†’ 'github:nixos/nixpkgs/db9208ab987cdeeedf78ad9b4cf3c55f5ebd269b' (2023-09-08) --- flake.lock | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index ceb62c6d33..15238981d8 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1650374568, - "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", "owner": "edolstra", "repo": "flake-compat", - "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", "type": "github" }, "original": { @@ -17,12 +17,15 @@ } }, "flake-utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1692799911, + "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", "type": "github" }, "original": { @@ -33,11 +36,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1666629043, - "narHash": "sha256-Yoq6Ut2F3Ol73yO9hG93x6ts5c4F5BhKTbcF3DtBEAw=", + "lastModified": 1694183432, + "narHash": "sha256-YyPGNapgZNNj51ylQMw9lAgvxtM2ai1HZVUu3GS8Fng=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b39fd6e4edef83cb4a135ebef98751ce23becc33", + "rev": "db9208ab987cdeeedf78ad9b4cf3c55f5ebd269b", "type": "github" }, "original": { @@ -53,6 +56,21 @@ "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", From 241889159ad80e5a5ed636a9e071fb598e366627 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 08:57:11 +0000 Subject: [PATCH 0389/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 8887dc220e..2afa56a3e0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -338,6 +338,7 @@ authors. Jakob
Jakob

πŸ–‹ Oscar Bonilla
Oscar Bonilla

πŸ–‹ Jon Erling Hustadnes
Jon Erling Hustadnes

πŸ–‹ + Charles Hall
Charles Hall

πŸš‡ From 95640cba232aadf5c12c25d0acc94f24b5ee0191 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 08:57:12 +0000 Subject: [PATCH 0390/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 95b50a7df8..55bd126bf5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2397,6 +2397,15 @@ "contributions": [ "content" ] + }, + { + "login": "CobaltCause", + "name": "Charles Hall", + "avatar_url": "https://avatars.githubusercontent.com/u/7003738?v=4", + "profile": "https://github.com/CobaltCause", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From 0ee9c5b776cf5d196013fb49d885c4dd67e8cd98 Mon Sep 17 00:00:00 2001 From: Jon Erling Hustadnes Date: Wed, 13 Sep 2023 20:26:47 +0200 Subject: [PATCH 0391/1432] Fixed formatting with rust-analyzer --- exercises/enums/enums3.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index 98da16917f..2dcdad06dd 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -20,7 +20,7 @@ struct State { color: (u8, u8, u8), position: Point, quit: bool, - message: String + message: String, } impl State { @@ -32,7 +32,9 @@ impl State { self.quit = true; } - fn echo(&mut self, s: String) { self.message = s } + fn echo(&mut self, s: String) { + self.message = s + } fn move_position(&mut self, p: Point) { self.position = p; From a0699bd91765c0702d925de6a176f4bdabdfd8d7 Mon Sep 17 00:00:00 2001 From: Jurglic Date: Thu, 14 Sep 2023 17:10:06 +0200 Subject: [PATCH 0392/1432] fix: test name typo --- exercises/conversions/as_ref_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index 626a36c45f..2ba9e3f0f9 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -57,7 +57,7 @@ mod tests { } #[test] - fn mult_box() { + fn mut_box() { let mut num: Box = Box::new(3); num_sq(&mut num); assert_eq!(*num, 9); From eb84eaf15123acb5c014ef579a480984a543e26c Mon Sep 17 00:00:00 2001 From: Luka Krmpotic Date: Fri, 15 Sep 2023 22:29:55 +0200 Subject: [PATCH 0393/1432] remove hint comments when no hint exists --- exercises/clippy/clippy3.rs | 3 +-- exercises/primitive_types/primitive_types1.rs | 3 --- exercises/primitive_types/primitive_types2.rs | 3 --- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/exercises/clippy/clippy3.rs b/exercises/clippy/clippy3.rs index 35021f8412..5a95f5b821 100644 --- a/exercises/clippy/clippy3.rs +++ b/exercises/clippy/clippy3.rs @@ -1,8 +1,7 @@ // clippy3.rs // // Here's a couple more easy Clippy fixes, so you can see its utility. -// -// Execute `rustlings hint clippy3` or use the `hint` watch subcommand for a hint. +// No hints. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types1.rs b/exercises/primitive_types/primitive_types1.rs index e1cf52a294..36633400b5 100644 --- a/exercises/primitive_types/primitive_types1.rs +++ b/exercises/primitive_types/primitive_types1.rs @@ -2,9 +2,6 @@ // // Fill in the rest of the line that has code missing! No hints, there's no // tricks, just get used to typing these :) -// -// Execute `rustlings hint primitive_types1` or use the `hint` watch subcommand -// for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types2.rs b/exercises/primitive_types/primitive_types2.rs index fcc9705aa8..f1616ed3b2 100644 --- a/exercises/primitive_types/primitive_types2.rs +++ b/exercises/primitive_types/primitive_types2.rs @@ -2,9 +2,6 @@ // // Fill in the rest of the line that has code missing! No hints, there's no // tricks, just get used to typing these :) -// -// Execute `rustlings hint primitive_types2` or use the `hint` watch subcommand -// for a hint. // I AM NOT DONE From 8716558696cc4149220a5bd67b7dd9c8e883e731 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 18 Sep 2023 09:54:08 +0200 Subject: [PATCH 0394/1432] Revert "Fix 1611" From 62415f758b40ff8bd027ca1ec493bfd4904e848a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:03:48 +0000 Subject: [PATCH 0395/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 2afa56a3e0..329887ab13 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -340,6 +340,9 @@ authors. Jon Erling Hustadnes
Jon Erling Hustadnes

πŸ–‹ Charles Hall
Charles Hall

πŸš‡ + + Luka KrmpotiΔ‡
Luka Krmpotić

πŸ–‹ + From 746180b153744c63fa57783b1bb7db61fb6caa7d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:03:49 +0000 Subject: [PATCH 0396/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 55bd126bf5..7d57486710 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2406,6 +2406,15 @@ "contributions": [ "infra" ] + }, + { + "login": "krmpotic", + "name": "Luka KrmpotiΔ‡", + "avatar_url": "https://avatars.githubusercontent.com/u/10350645?v=4", + "profile": "https://github.com/krmpotic", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From a38840ae92969fff2e972311ea797a3434c269b9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:05:51 +0000 Subject: [PATCH 0397/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 329887ab13..fb249acb3b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -342,6 +342,7 @@ authors. Luka KrmpotiΔ‡
Luka Krmpotić

πŸ–‹ + Jurglic
Jurglic

πŸ–‹ From 8e8c74c6c03017a9d0bbb089354b50a2a143e2fa Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:05:52 +0000 Subject: [PATCH 0398/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7d57486710..92bf2a09c9 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2415,6 +2415,15 @@ "contributions": [ "content" ] + }, + { + "login": "jurglic", + "name": "Jurglic", + "avatar_url": "https://avatars.githubusercontent.com/u/112600?v=4", + "profile": "https://github.com/jurglic", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 9a743f80c57cc6bf27819589a8ddb5a5579ab1a4 Mon Sep 17 00:00:00 2001 From: liv Date: Mon, 18 Sep 2023 10:16:05 +0200 Subject: [PATCH 0399/1432] release: 5.6.1 --- CHANGELOG.md | 16 +++++++ Cargo.lock | 132 ++++++++++++++++++++++++++++----------------------- Cargo.toml | 2 +- README.md | 8 ++-- flake.nix | 2 +- 5 files changed, 94 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69262b3410..a7226a47af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ + +## 5.6.1 (2023-09-18) + +#### Changed + +- Converted all exercises with assertions to test mode. + +#### Fixed + +- `cow1`: Reverted regression introduced by calling `to_mut` where it + shouldn't have been called, and clarified comment. +- `primitive_types3`: Require at least an array of 100 elements. +- Removed hint comments when no hint exists for the exercise. +- `as_ref_mut`: Fixed a typo in a test function name. +- `enums3`: Fixed formatting with `rustfmt`. + ## 5.6.0 (2023-09-04) diff --git a/Cargo.lock b/Cargo.lock index a8408edf6f..418032a71d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.0.5" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" @@ -111,9 +111,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.2" +version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" dependencies = [ "clap_builder", "clap_derive", @@ -157,15 +157,15 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "console" -version = "0.15.7" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.45.0", + "windows-sys 0.42.0", ] [[package]] @@ -200,14 +200,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -274,11 +274,11 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "home" -version = "0.5.5" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" dependencies = [ - "windows-sys 0.48.0", + "winapi 0.3.9", ] [[package]] @@ -353,9 +353,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "kernel32-sys" @@ -381,15 +381,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", +] [[package]] name = "memchr" @@ -442,9 +445,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.39" +version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" dependencies = [ "cfg-if 0.1.10", "libc", @@ -477,9 +480,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -529,40 +532,39 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" dependencies = [ "aho-corasick", "memchr", - "regex-automata", "regex-syntax", ] @@ -571,21 +573,16 @@ name = "regex-automata" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustlings" -version = "5.6.0" +version = "5.6.1" dependencies = [ "assert_cmd", "clap", @@ -603,9 +600,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" @@ -618,18 +615,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.188" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", @@ -638,9 +635,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -658,9 +655,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -673,9 +670,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.31" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" dependencies = [ "proc-macro2", "quote", @@ -690,9 +687,9 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "toml" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", "serde_spanned", @@ -711,9 +708,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "serde", @@ -724,9 +721,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-width" @@ -802,6 +799,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index d64ef7568a..a055d4f348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" -version = "5.6.0" +version = "5.6.1" authors = [ "Liv ", "Carol (Nichols || Goulding) ", diff --git a/README.md b/README.md index a16f7534d5..8fac7a28bb 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.0) -git clone -b 5.6.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.1) +git clone -b 5.6.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings # if nix version > 2.3 nix develop @@ -79,8 +79,8 @@ If you get a permission denied message, you might have to exclude the directory Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.0) -git clone -b 5.6.0 --depth 1 https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.1) +git clone -b 5.6.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings cargo install --force --path . ``` diff --git a/flake.nix b/flake.nix index 94f4177506..152d38e61a 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ rustlings = pkgs.rustPlatform.buildRustPackage { name = "rustlings"; - version = "5.6.0"; + version = "5.6.1"; buildInputs = cargoBuildInputs; nativeBuildInputs = [pkgs.git]; From 83ac243c00d0b99c6d038546a4ffda2d5b23ca07 Mon Sep 17 00:00:00 2001 From: Ofir Lauber Date: Thu, 21 Sep 2023 01:32:46 +0300 Subject: [PATCH 0400/1432] chore: fix comment in enums3.rs --- exercises/enums/enums3.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index 2dcdad06dd..92d18c46a4 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -41,10 +41,9 @@ impl State { } fn process(&mut self, message: Message) { - // TODO: create a match expression to process the different message - // variants - // Remember: When passing a tuple as a function argument, you'll need - // extra parentheses: fn function((t, u, p, l, e)) + // TODO: create a match expression to process the different message variants + // Remember: When passing a tuple as a function argument, you'll need extra parentheses: + // fn function((t, u, p, l, e)) } } From 666857dc4eb7f1187f40d5631dbf1a10d63ac2cb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:01:30 +0000 Subject: [PATCH 0401/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index fb249acb3b..51255a9a94 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -343,6 +343,7 @@ authors. Luka KrmpotiΔ‡
Luka Krmpotić

πŸ–‹ Jurglic
Jurglic

πŸ–‹ + Ofir Lauber
Ofir Lauber

πŸ–‹ From f86a3c5ddcf29c01c9c4308e2556cf3cc35917de Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:01:31 +0000 Subject: [PATCH 0402/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 92bf2a09c9..d2ad252992 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2424,6 +2424,15 @@ "contributions": [ "content" ] + }, + { + "login": "OfirLauber", + "name": "Ofir Lauber", + "avatar_url": "https://avatars.githubusercontent.com/u/5631030?v=4", + "profile": "https://github.com/OfirLauber", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From c63c44ac70667c0696997a6f40efbf9239789d18 Mon Sep 17 00:00:00 2001 From: Chris Rose Date: Thu, 21 Sep 2023 11:00:04 -0600 Subject: [PATCH 0403/1432] Remove .envrc --- .envrc | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .envrc diff --git a/.envrc b/.envrc deleted file mode 100644 index 48541064fd..0000000000 --- a/.envrc +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -# Automatically Load nix devShell w/dotenv -use flake From 6aa47be78e9b80beec3bdd5b96a8f985138358b2 Mon Sep 17 00:00:00 2001 From: d1t2 <507502+dieterplex@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:32:39 +0800 Subject: [PATCH 0404/1432] fix(installation): bump MinRustVersion to 1.70 Since #1633 porting to Clap, min Rust version reqirement changes. --- install.ps1 | 2 +- install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install.ps1 b/install.ps1 index 7bab21f6f7..844b0134cd 100644 --- a/install.ps1 +++ b/install.ps1 @@ -53,7 +53,7 @@ function vercomp($v1, $v2) { } $rustVersion = $(rustc --version).Split(" ")[1] -$minRustVersion = "1.56" +$minRustVersion = "1.70" if ((vercomp $rustVersion $minRustVersion) -eq 2) { Write-Host "WARNING: Rust version is too old: $rustVersion - needs at least $minRustVersion" Write-Host "Please update Rust with 'rustup update'" diff --git a/install.sh b/install.sh index 4ee56bb1fe..9aca5b6843 100755 --- a/install.sh +++ b/install.sh @@ -124,7 +124,7 @@ function vercomp() { } RustVersion=$(rustc --version | cut -d " " -f 2) -MinRustVersion=1.58 +MinRustVersion=1.70 vercomp "$RustVersion" $MinRustVersion || ec=$? if [ ${ec:-0} -eq 2 ] then From b88c23897f27ff5aa7d700f0bc16a289068445d5 Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 25 Sep 2023 03:36:43 -0400 Subject: [PATCH 0405/1432] Give a more helpful error when a file is missing Previously, this would just say "missing file". Now it shows the path of the file that was missing, which should make it easier to debug what went wrong. --- src/exercise.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 07251dba54..2c740fbf8a 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -201,14 +201,21 @@ path = "{}.rs""#, } pub fn state(&self) -> State { - let mut source_file = - File::open(&self.path).expect("We were unable to open the exercise file!"); + let mut source_file = File::open(&self.path).unwrap_or_else(|e| { + panic!( + "We were unable to open the exercise file {}! {e}", + self.path.display() + ) + }); let source = { let mut s = String::new(); - source_file - .read_to_string(&mut s) - .expect("We were unable to read the exercise file!"); + source_file.read_to_string(&mut s).unwrap_or_else(|e| { + panic!( + "We were unable to read the exercise file {}! {e}", + self.path.display() + ) + }); s }; From be78831ac02f969f02f4c2f35193dd2a2bb3aa62 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 08:58:45 +0000 Subject: [PATCH 0406/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 51255a9a94..09d7480981 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -344,6 +344,7 @@ authors. Luka KrmpotiΔ‡
Luka Krmpotić

πŸ–‹ Jurglic
Jurglic

πŸ–‹ Ofir Lauber
Ofir Lauber

πŸ–‹ + Chris Rose
Chris Rose

πŸš‡ From e58c8322e90f8435b9e5a7adaaebaeec2fe10c3d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 08:58:46 +0000 Subject: [PATCH 0407/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d2ad252992..0239dd4277 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2433,6 +2433,15 @@ "contributions": [ "content" ] + }, + { + "login": "offbyone", + "name": "Chris Rose", + "avatar_url": "https://avatars.githubusercontent.com/u/181693?v=4", + "profile": "https://github.com/offbyone", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From 463e23e006a314d80139afe6a9144a34d6de358a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 08:59:25 +0000 Subject: [PATCH 0408/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 51255a9a94..1ee1bf7e12 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -344,6 +344,7 @@ authors. Luka KrmpotiΔ‡
Luka Krmpotić

πŸ–‹ Jurglic
Jurglic

πŸ–‹ Ofir Lauber
Ofir Lauber

πŸ–‹ + d1t2
d1t2

πŸš‡ From 00e16b49e576adfebf34b45c4d7492595656246c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 08:59:26 +0000 Subject: [PATCH 0409/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d2ad252992..0bf80fce71 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2433,6 +2433,15 @@ "contributions": [ "content" ] + }, + { + "login": "dieterplex", + "name": "d1t2", + "avatar_url": "https://avatars.githubusercontent.com/u/507502?v=4", + "profile": "https://github.com/dieterplex", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From cc658eb28840e63f69f89c8562bbf10333461355 Mon Sep 17 00:00:00 2001 From: DocWilco Date: Wed, 27 Sep 2023 21:33:49 +0200 Subject: [PATCH 0410/1432] fix(cli): make debugging in windows work On windows, if `stderr` or `stdin` aren't also set to `Stdio::null()` the `spawn()` fails with `The handle is invalid`, and `rustlings` thinks that there's no `rustc` installed. --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index a4b764d764..89bd444f3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -396,6 +396,8 @@ fn rustc_exists() -> bool { Command::new("rustc") .args(["--version"]) .stdout(Stdio::null()) + .stderr(Stdio::null()) + .stdin(Stdio::null()) .spawn() .and_then(|mut child| child.wait()) .map(|status| status.success()) From 511e3343650a880435f8fb3e96eecbc674d6ebfa Mon Sep 17 00:00:00 2001 From: DocWilco Date: Wed, 27 Sep 2023 22:02:14 +0200 Subject: [PATCH 0411/1432] fix(cli): stop littering pdb files on windows --- src/exercise.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/exercise.rs b/src/exercise.rs index 2c740fbf8a..c7b5672d35 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -9,6 +9,7 @@ use std::process::{self, Command}; const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; +const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE"; const CONTEXT: usize = 2; const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/clippy/Cargo.toml"; @@ -113,11 +114,13 @@ impl Exercise { .args([self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) .args(RUSTC_EDITION_ARGS) + .args(RUSTC_NO_DEBUG_ARGS) .output(), Mode::Test => Command::new("rustc") .args(["--test", self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) .args(RUSTC_EDITION_ARGS) + .args(RUSTC_NO_DEBUG_ARGS) .output(), Mode::Clippy => { let cargo_toml = format!( @@ -144,6 +147,7 @@ path = "{}.rs""#, .args([self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) .args(RUSTC_EDITION_ARGS) + .args(RUSTC_NO_DEBUG_ARGS) .output() .expect("Failed to compile!"); // Due to an issue with Clippy, a cargo clean is required to catch all lints. @@ -289,6 +293,24 @@ mod test { assert!(!Path::new(&temp_file()).exists()); } + #[test] + #[cfg(target_os = "windows")] + fn test_no_pdb_file() { + [Mode::Compile, Mode::Test] // Clippy doesn't like to test + .iter() + .for_each(|mode| { + let exercise = Exercise { + name: String::from("example"), + // We want a file that does actually compile + path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), + mode: *mode, + hint: String::from(""), + }; + let _ = exercise.compile().unwrap(); + assert!(!Path::new(&format!("{}.pdb", temp_file())).exists()); + }); + } + #[test] fn test_pending_state() { let exercise = Exercise { From 177981d1cd4812dd7b67d4fc7f74227e21f1691b Mon Sep 17 00:00:00 2001 From: liv Date: Thu, 28 Sep 2023 11:02:25 +0200 Subject: [PATCH 0412/1432] chore: fix missing opening brace --- .all-contributorsrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 27f69c3bdd..d8aeff5432 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2443,6 +2443,7 @@ "infra" ] }, + { "login": "dieterplex", "name": "d1t2", "avatar_url": "https://avatars.githubusercontent.com/u/507502?v=4", From 11227403cecec2cb27fc9d3758ccb2962b352002 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 28 Sep 2023 09:02:43 +0000 Subject: [PATCH 0413/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index a2decea404..3dcc3e6f97 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -346,6 +346,7 @@ authors. Ofir Lauber
Ofir Lauber

πŸ–‹ Chris Rose
Chris Rose

πŸš‡ d1t2
d1t2

πŸš‡ + docwilco
docwilco

πŸ’» From 2fcafc66020fbe169efd4ff948d751f7be9a1b98 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 28 Sep 2023 09:02:44 +0000 Subject: [PATCH 0414/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d8aeff5432..d31607fdef 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2451,6 +2451,15 @@ "contributions": [ "infra" ] + }, + { + "login": "docwilco", + "name": "docwilco", + "avatar_url": "https://avatars.githubusercontent.com/u/66911096?v=4", + "profile": "https://github.com/docwilco", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 8e6c83b4cf8e61ad8a0fbbb42bdf968e93278736 Mon Sep 17 00:00:00 2001 From: Rogier 'DocWilco' Mulhuijzen Date: Fri, 29 Sep 2023 00:39:51 +0200 Subject: [PATCH 0415/1432] chore: make hints proper markdown Also rewrapped some hints to 80 columns so that they also look good in a terminal. closes #1698 --- info.toml | 610 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 377 insertions(+), 233 deletions(-) diff --git a/info.toml b/info.toml index 9930917c83..e3f66e28c5 100644 --- a/info.toml +++ b/info.toml @@ -5,7 +5,7 @@ name = "intro1" path = "exercises/intro/intro1.rs" mode = "compile" hint = """ -Remove the I AM NOT DONE comment in the exercises/intro/intro1.rs file +Remove the `I AM NOT DONE` comment in the `exercises/intro/intro1.rs` file to move on to the next exercise.""" [[exercises]] @@ -32,21 +32,29 @@ mode = "compile" hint = """ The compiler message is saying that Rust cannot infer the type that the variable binding `x` has with what is given here. -What happens if you annotate the first line in the main function with a type annotation? -What if you give x a value? + +What happens if you annotate the first line in the main function with a type +annotation? + +What if you give `x` a value? + What if you do both? -What type should x be, anyway? -What if x is the same type as 10? What if it's a different type?""" + +What type should `x` be, anyway? + +What if `x` is the same type as `10`? What if it's a different type?""" [[exercises]] name = "variables3" path = "exercises/variables/variables3.rs" mode = "compile" hint = """ -Oops! In this exercise, we have a variable binding that we've created on -in the first line in the main function, and we're trying to use it in the next line, +Oops! In this exercise, we have a variable binding that we've created on in the +first line in the `main` function, and we're trying to use it in the next line, but we haven't given it a value. -We can't print out something that isn't there; try giving x a value! + +We can't print out something that isn't there; try giving `x` a value! + This is an error that can cause bugs that's very easy to make in any programming language -- thankfully the Rust compiler has caught this for us!""" @@ -56,7 +64,7 @@ path = "exercises/variables/variables4.rs" mode = "compile" hint = """ In Rust, variable bindings are immutable by default. But here we're trying -to reassign a different value to x! There's a keyword we can use to make +to reassign a different value to `x`! There's a keyword we can use to make a variable binding mutable instead.""" [[exercises]] @@ -64,14 +72,17 @@ name = "variables5" path = "exercises/variables/variables5.rs" mode = "compile" hint = """ -In variables4 we already learned how to make an immutable variable mutable -using a special keyword. Unfortunately this doesn't help us much in this exercise -because we want to assign a different typed value to an existing variable. Sometimes -you may also like to reuse existing variable names because you are just converting -values to different types like in this exercise. +In `variables4` we already learned how to make an immutable variable mutable +using a special keyword. Unfortunately this doesn't help us much in this +exercise because we want to assign a different typed value to an existing +variable. Sometimes you may also like to reuse existing variable names because +you are just converting values to different types like in this exercise. + Fortunately Rust has a powerful solution to this problem: 'Shadowing'! -You can read more about 'Shadowing' in the book's section 'Variables and Mutability': +You can read more about 'Shadowing' in the book's section 'Variables and +Mutability': https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing + Try to solve this exercise afterwards using this technique.""" [[exercises]] @@ -81,11 +92,14 @@ mode = "compile" hint = """ We know about variables and mutability, but there is another important type of variable available: constants. -Constants are always immutable and they are declared with keyword 'const' rather -than keyword 'let'. + +Constants are always immutable and they are declared with keyword `const` rather +than keyword `let`. + Constants types must also always be annotated. -Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability': +Read more about constants and the differences between variables and constants +under 'Constants' in the book's section 'Variables and Mutability': https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants """ @@ -116,8 +130,10 @@ mode = "compile" hint = """ This time, the function *declaration* is okay, but there's something wrong with the place where we're calling the function. + As a reminder, you can freely play around with different solutions in Rustlings! -Watch mode will only jump to the next exercise if you remove the I AM NOT DONE comment.""" +Watch mode will only jump to the next exercise if you remove the `I AM NOT +DONE` comment.""" [[exercises]] name = "functions4" @@ -125,10 +141,10 @@ path = "exercises/functions/functions4.rs" mode = "compile" hint = """ The error message points to the function `sale_price` and says it expects a type -after the `->`. This is where the function's return type should be -- take a look at -the `is_even` function for an example! +after the `->`. This is where the function's return type should be -- take a +look at the `is_even` function for an example! -Also: Did you figure out that, technically, u32 would be the more fitting type +Also: Did you figure out that, technically, `u32` would be the more fitting type for the prices here, since they can't be negative? If so, kudos!""" [[exercises]] @@ -137,8 +153,13 @@ path = "exercises/functions/functions5.rs" mode = "compile" hint = """ This is a really common error that can be fixed by removing one character. -It happens because Rust distinguishes between expressions and statements: expressions return a value based on their operand(s), and statements simply return a () type which behaves just like `void` in C/C++ language. -We want to return a value of `i32` type from the `square` function, but it is returning a `()` type... +It happens because Rust distinguishes between expressions and statements: +expressions return a value based on their operand(s), and statements simply +return a `()` type which behaves just like `void` in C/C++ language. + +We want to return a value of `i32` type from the `square` function, but it is +returning a `()` type... + They are not the same. There are two solutions: 1. Add a `return` ahead of `num * num;` 2. remove `;`, make it to be `num * num`""" @@ -151,9 +172,11 @@ path = "exercises/if/if1.rs" mode = "test" hint = """ It's possible to do this in one line if you would like! + Some similar examples from other languages: - In C(++) this would be: `a > b ? a : b` - In Python this would be: `a if a > b else b` + Remember in Rust that: - the `if` condition does not need to be surrounded by parentheses - `if`/`else` conditionals are expressions @@ -173,7 +196,8 @@ name = "if3" path = "exercises/if/if3.rs" mode = "test" hint = """ -In Rust, every arm of an `if` expression has to return the same type of value. Make sure the type is consistent across all arms.""" +In Rust, every arm of an `if` expression has to return the same type of value. +Make sure the type is consistent across all arms.""" # QUIZ 1 @@ -204,10 +228,13 @@ mode = "compile" hint = """ There's a shorthand to initialize Arrays with a certain size that does not require you to type in 100 items (but you certainly can if you want!). + For example, you can do: +``` let array = ["Are we there yet?"; 10]; +``` -Bonus: what are some other things you could have that would return true +Bonus: what are some other things you could have that would return `true` for `a.len() >= 100`?""" [[exercises]] @@ -215,14 +242,14 @@ name = "primitive_types4" path = "exercises/primitive_types/primitive_types4.rs" mode = "test" hint = """ -Take a look at the Understanding Ownership -> Slices -> Other Slices section of the book: -https://doc.rust-lang.org/book/ch04-03-slices.html -and use the starting and ending (plus one) indices of the items in the Array -that you want to end up in the slice. +Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section +of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the +starting and ending (plus one) indices of the items in the `Array` that you +want to end up in the slice. -If you're curious why the first argument of `assert_eq!` does not -have an ampersand for a reference since the second argument is a -reference, take a look at the coercion chapter of the nomicon: +If you're curious why the first argument of `assert_eq!` does not have an +ampersand for a reference since the second argument is areference, take a look +at the coercion chapter of the nomicon: https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] @@ -230,9 +257,11 @@ name = "primitive_types5" path = "exercises/primitive_types/primitive_types5.rs" mode = "compile" hint = """ -Take a look at the Data Types -> The Tuple Type section of the book: +Take a look at the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Particularly the part about destructuring (second to last example in the section). +Particularly the part about destructuring (second to last example in the +section). + You'll need to make a pattern to bind `name` and `age` to the appropriate parts of the tuple. You can do it!!""" @@ -243,7 +272,7 @@ mode = "test" hint = """ While you could use a destructuring `let` for the tuple here, try indexing into it instead, as explained in the last example of the -Data Types -> The Tuple Type section of the book: +'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type Now you have another tool in your toolbox!""" @@ -256,9 +285,10 @@ mode = "test" hint = """ In Rust, there are two ways to define a Vector. 1. One way is to use the `Vec::new()` function to create a new vector - and fill it with the `push()` method. + and fill it with the `push()` method. 2. The second way, which is simpler is to use the `vec![]` macro and - define your elements inside the square brackets. + define your elements inside the square brackets. + Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html of the Rust book to learn more. """ @@ -268,13 +298,19 @@ name = "vecs2" path = "exercises/vecs/vecs2.rs" mode = "test" hint = """ -In the first function we are looping over the Vector and getting a reference to one `element` at a time. -To modify the value of that `element` we need to use the * dereference operator. You can learn more in this chapter of the Rust book: +In the first function we are looping over the Vector and getting a reference to +one `element` at a time. + +To modify the value of that `element` we need to use the `*` dereference +operator. You can learn more in this chapter of the Rust book: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector -In the second function this dereferencing is not necessary, because the map function expects the new value to be returned. +In the second function this dereferencing is not necessary, because the `map` +function expects the new value to be returned. + +After you've completed both functions, decide for yourself which approach you +like better. -After you've completed both functions, decide for yourself which approach you like better. What do you think is the more commonly used pattern under Rust developers? """ @@ -285,12 +321,14 @@ name = "move_semantics1" path = "exercises/move_semantics/move_semantics1.rs" mode = "test" hint = """ -So you've got the "cannot borrow immutable local variable `vec` as mutable" error on the line -where we push an element to the vector, right? -The fix for this is going to be adding one keyword, and the addition is NOT on the line where -we push to the vector (where the error is). +So you've got the "cannot borrow immutable local variable `vec` as mutable" +error on the line where we push an element to the vector, right? + +The fix for this is going to be adding one keyword, and the addition is NOT on +the line where we push to the vector (where the error is). -Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!""" +Also: Try accessing `vec0` after having called `fill_vec()`. See what +happens!""" [[exercises]] name = "move_semantics2" @@ -300,14 +338,17 @@ hint = """ When running this exercise for the first time, you'll notice an error about "borrow of moved value". In Rust, when an argument is passed to a function and it's not explicitly returned, you can't use the original variable anymore. -We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's being -"moved" into `vec1`, meaning we can't access `vec0` anymore after the fact. -Rust provides a couple of different ways to mitigate this issue, feel free to try them all: -1. You could make another, separate version of the data that's in `vec0` and pass that - to `fill_vec` instead. +We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's +being "moved" into `vec1`, meaning we can't access `vec0` anymore after the +fact. + +Rust provides a couple of different ways to mitigate this issue, feel free to +try them all: +1. You could make another, separate version of the data that's in `vec0` and + pass that to `fill_vec` instead. 2. Make `fill_vec` borrow its argument instead of taking ownership of it, - and then copy the data within the function (`vec.clone()`) in order to return an owned - `Vec`. + and then copy the data within the function (`vec.clone()`) in order to + return an owned `Vec`. """ [[exercises]] @@ -340,9 +381,9 @@ path = "exercises/move_semantics/move_semantics5.rs" mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in -scope. Does it help to update the value of referent (x) immediately after +scope. Does it help to update the value of referent (`x`) immediately after the mutable reference is taken? Read more about 'Mutable References' -in the book's section References and Borrowing': +in the book's section 'References and Borrowing': https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. """ @@ -353,10 +394,14 @@ mode = "compile" hint = """ To find the answer, you can consult the book section "References and Borrowing": https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html -The first problem is that `get_char` is taking ownership of the string. -So `data` is moved and can't be used for `string_uppercase` -`data` is moved to `get_char` first, meaning that `string_uppercase` cannot manipulate the data. -Once you've fixed that, `string_uppercase`'s function signature will also need to be adjusted. + +The first problem is that `get_char` is taking ownership of the string. So +`data` is moved and can't be used for `string_uppercase`. `data` is moved to +`get_char` first, meaning that `string_uppercase` cannot manipulate the data. + +Once you've fixed that, `string_uppercase`'s function signature will also need +to be adjusted. + Can you figure out how? Another hint: it has to do with the `&` character.""" @@ -368,33 +413,46 @@ name = "structs1" path = "exercises/structs/structs1.rs" mode = "test" hint = """ -Rust has more than one type of struct. Three actually, all variants are used to package related data together. -There are normal (or classic) structs. These are named collections of related data stored in fields. +Rust has more than one type of struct. Three actually, all variants are used to +package related data together. + +There are normal (or classic) structs. These are named collections of related +data stored in fields. + Tuple structs are basically just named tuples. -Finally, Unit-like structs. These don't have any fields and are useful for generics. + +Finally, Unit-like structs. These don't have any fields and are useful for +generics. In this exercise you need to complete and implement one of each kind. -Read more about structs in The Book: https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" +Read more about structs in The Book: +https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" path = "exercises/structs/structs2.rs" mode = "test" hint = """ -Creating instances of structs is easy, all you need to do is assign some values to its fields. +Creating instances of structs is easy, all you need to do is assign some values +to its fields. + There are however some shortcuts that can be taken when instantiating structs. -Have a look in The Book, to find out more: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" +Have a look in The Book, to find out more: +https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" [[exercises]] name = "structs3" path = "exercises/structs/structs3.rs" mode = "test" hint = """ -For is_international: What makes a package international? Seems related to the places it goes through right? +For `is_international`: What makes a package international? Seems related to +the places it goes through right? -For get_fees: This method takes an additional argument, is there a field in the Package struct that this relates to? +For `get_fees`: This method takes an additional argument, is there a field in +the `Package` struct that this relates to? -Have a look in The Book, to find out more about method implementations: https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" +Have a look in The Book, to find out more about method implementations: +https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" # ENUMS @@ -418,9 +476,11 @@ path = "exercises/enums/enums3.rs" mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. -and then create a match expression in `process()`. -Note that you need to deconstruct some message variants -in the match expression to get value in the variant.""" + +And then create a match expression in `process()`. + +Note that you need to deconstruct some message variants in the match expression +to get value in the variant.""" # STRINGS @@ -429,34 +489,40 @@ name = "strings1" path = "exercises/strings/strings1.rs" mode = "compile" hint = """ -The `current_favorite_color` function is currently returning a string slice with the `'static` -lifetime. We know this because the data of the string lives in our code itself -- it doesn't -come from a file or user input or another program -- so it will live as long as our program -lives. But it is still a string slice. There's one way to create a `String` by converting a -string slice covered in the Strings chapter of the book, and another way that uses the `From` -trait.""" +The `current_favorite_color` function is currently returning a string slice +with the `'static` lifetime. We know this because the data of the string lives +in our code itself -- it doesn't come from a file or user input or another +program -- so it will live as long as our program lives. + +But it is still a string slice. There's one way to create a `String` by +converting a string slice covered in the Strings chapter of the book, and +another way that uses the `From` trait.""" [[exercises]] name = "strings2" path = "exercises/strings/strings2.rs" mode = "compile" hint = """ -Yes, it would be really easy to fix this by just changing the value bound to `word` to be a -string slice instead of a `String`, wouldn't it?? There is a way to add one character to the -if statement, though, that will coerce the `String` into a string slice. +Yes, it would be really easy to fix this by just changing the value bound to +`word` to be a string slice instead of a `String`, wouldn't it?? There is a way +to add one character to the `if` statement, though, that will coerce the +`String` into a string slice. -Side note: If you're interested in learning about how this kind of reference conversion works, you can jump ahead in the book and read this part in the smart pointers chapter: https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" +Side note: If you're interested in learning about how this kind of reference +conversion works, you can jump ahead in the book and read this part in the +smart pointers chapter: +https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" [[exercises]] name = "strings3" path = "exercises/strings/strings3.rs" mode = "test" hint = """ -There's tons of useful standard library functions for strings. Let's try and use some of -them: ! +There's tons of useful standard library functions for strings. Let's try and use some of them: +https://doc.rust-lang.org/std/string/struct.String.html#method.trim -For the compose_me method: You can either use the `format!` macro, or convert the string -slice into an owned string, which you can then freely extend.""" +For the `compose_me` method: You can either use the `format!` macro, or convert +the string slice into an owned string, which you can then freely extend.""" [[exercises]] name = "strings4" @@ -484,6 +550,7 @@ The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and associated constants). Complete the `use` statements to fit the uses in main and find the one keyword missing for both constants. + Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" [[exercises]] @@ -491,9 +558,9 @@ name = "modules3" path = "exercises/modules/modules3.rs" mode = "compile" hint = """ -UNIX_EPOCH and SystemTime are declared in the std::time module. Add a use statement -for these two to bring them into scope. You can use nested paths or the glob -operator to bring these two in using only one line.""" +`UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a +`use` statement for these two to bring them into scope. You can use nested +paths or the glob operator to bring these two in using only one line.""" # HASHMAPS @@ -503,9 +570,10 @@ path = "exercises/hashmaps/hashmaps1.rs" mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out - the type for the `basket`. + the type for the `basket`. + Hint 2: Number of fruits should be at least 5. And you have to put - at least three different types of fruits. + at least three different types of fruits. """ [[exercises]] @@ -522,9 +590,14 @@ name = "hashmaps3" path = "exercises/hashmaps/hashmaps3.rs" mode = "test" hint = """ -Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert entries corresponding to each team in the scores table. +Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert + entries corresponding to each team in the scores table. + Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value -Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value. + +Hint 2: If there is already an entry for a given key, the value returned by + `entry()` can be updated based on the existing value. + Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value """ @@ -543,22 +616,28 @@ name = "options1" path = "exercises/options/options1.rs" mode = "test" hint = """ -Options can have a Some value, with an inner value, or a None value, without an inner value. -There's multiple ways to get at the inner value, you can use unwrap, or pattern match. Unwrapping -is the easiest, but how do you do it safely so that it doesn't panic in your face later?""" +Options can have a `Some` value, with an inner value, or a `None` value, +without an inner value. + +There's multiple ways to get at the inner value, you can use `unwrap`, or +pattern match. Unwrapping is the easiest, but how do you do it safely so that +it doesn't panic in your face later?""" [[exercises]] name = "options2" path = "exercises/options/options2.rs" mode = "test" hint = """ -check out: -https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html -https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html +Check out: -Remember that Options can be stacked in if let and while let. -For example: Some(Some(variable)) = variable2 -Also see Option::flatten +- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html +- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html + +Remember that `Option`s can be stacked in `if let` and `while let`. + +For example: `Some(Some(variable)) = variable2` + +Also see `Option::flatten` """ [[exercises]] @@ -566,10 +645,11 @@ name = "options3" path = "exercises/options/options3.rs" mode = "compile" hint = """ -The compiler says a partial move happened in the `match` -statement. How can this be avoided? The compiler shows the correction -needed. After making the correction as suggested by the compiler, do -read: https://doc.rust-lang.org/std/keyword.ref.html""" +The compiler says a partial move happened in the `match` statement. How can +this be avoided? The compiler shows the correction needed. + +After making the correction as suggested by the compiler, do read: +https://doc.rust-lang.org/std/keyword.ref.html""" # ERROR HANDLING @@ -579,16 +659,15 @@ path = "exercises/error_handling/errors1.rs" mode = "test" hint = """ `Ok` and `Err` are one of the variants of `Result`, so what the tests are saying -is that `generate_nametag_text` should return a `Result` instead of an -`Option`. +is that `generate_nametag_text` should return a `Result` instead of an `Option`. To make this change, you'll need to: - - update the return type in the function signature to be a Result that - could be the variants `Ok(String)` and `Err(String)` - - change the body of the function to return `Ok(stuff)` where it currently - returns `Some(stuff)` - - change the body of the function to return `Err(error message)` where it - currently returns `None`""" + - update the return type in the function signature to be a `Result` that could be the variants `Ok(String)` and `Err(String)` + - change the body of the function to return `Ok(stuff)` where it currently + returns `Some(stuff)` + - change the body of the function to return `Err(error message)` where it + currently returns `None`""" [[exercises]] name = "errors2" @@ -597,9 +676,12 @@ mode = "test" hint = """ One way to handle this is using a `match` statement on `item_quantity.parse::()` where the cases are `Ok(something)` and -`Err(something)`. This pattern is very common in Rust, though, so there's -a `?` operator that does pretty much what you would make that match statement -do for you! Take a look at this section of the Error Handling chapter: +`Err(something)`. + +This pattern is very common in Rust, though, so there's a `?` operator that +does pretty much what you would make that match statement do for you! + +Take a look at this section of the 'Error Handling' chapter: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator and give it a try!""" @@ -608,32 +690,38 @@ name = "errors3" path = "exercises/error_handling/errors3.rs" mode = "compile" hint = """ -If other functions can return a `Result`, why shouldn't `main`? It's a fairly common -convention to return something like Result<(), ErrorType> from your main function. -The unit (`()`) type is there because nothing is really needed in terms of positive -results.""" +If other functions can return a `Result`, why shouldn't `main`? It's a fairly +common convention to return something like `Result<(), ErrorType>` from your +main function. + +The unit (`()`) type is there because nothing is really needed in terms of +positive results.""" [[exercises]] name = "errors4" path = "exercises/error_handling/errors4.rs" mode = "test" hint = """ -`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. -It should be doing some checking, returning an `Err` result if those checks fail, and only -returning an `Ok` result if those checks determine that everything is... okay :)""" +`PositiveNonzeroInteger::new` is always creating a new instance and returning +an `Ok` result. + +It should be doing some checking, returning an `Err` result if those checks +fail, and only returning an `Ok` result if those checks determine that +everything is... okay :)""" [[exercises]] name = "errors5" path = "exercises/error_handling/errors5.rs" mode = "compile" hint = """ -There are two different possible `Result` types produced within `main()`, which are -propagated using `?` operators. How do we declare a return type from `main()` that allows both? +There are two different possible `Result` types produced within `main()`, which +are propagated using `?` operators. How do we declare a return type from +`main()` that allows both? -Under the hood, the `?` operator calls `From::from` on the error value to convert it to a boxed -trait object, a `Box`. This boxed trait object is polymorphic, and since all -errors implement the `error::Error` trait, we can capture lots of different errors in one "Box" -object. +Under the hood, the `?` operator calls `From::from` on the error value to +convert it to a boxed trait object, a `Box`. This boxed trait +object is polymorphic, and since all errors implement the `error::Error` trait, +we can capture lots of different errors in one "Box" object. Check out this section of the book: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator @@ -653,7 +741,7 @@ hint = """ This exercise uses a completed version of `PositiveNonzeroInteger` from errors4. -Below the line that TODO asks you to change, there is an example of using +Below the line that `TODO` asks you to change, there is an example of using the `map_err()` method on a `Result` to transform one type of error into another. Try using something similar on the `Result` from `parse()`. You might use the `?` operator to return early from the function, or you might @@ -672,7 +760,9 @@ name = "generics1" path = "exercises/generics/generics1.rs" mode = "compile" hint = """ -Vectors in Rust make use of generics to create dynamically sized arrays of any type. +Vectors in Rust make use of generics to create dynamically sized arrays of any +type. + You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] @@ -680,7 +770,8 @@ name = "generics2" path = "exercises/generics/generics2.rs" mode = "test" hint = """ -Currently we are wrapping only values of type 'u32'. +Currently we are wrapping only values of type `u32`. + Maybe we could update the explicit references to this data type somehow? If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions @@ -702,7 +793,8 @@ name = "traits2" path = "exercises/traits/traits2.rs" mode = "test" hint = """ -Notice how the trait takes ownership of 'self',and returns `Self`. +Notice how the trait takes ownership of `self`, and returns `Self`. + Try mutating the incoming string vector. Have a look at the tests to see what the result should look like! @@ -726,8 +818,8 @@ name = "traits4" path = "exercises/traits/traits4.rs" mode = "test" hint = """ -Instead of using concrete types as parameters you can use traits. Try replacing the -'??' with 'impl ' +Instead of using concrete types as parameters you can use traits. Try replacing +the '??' with 'impl ' See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters """ @@ -737,8 +829,8 @@ name = "traits5" path = "exercises/traits/traits5.rs" mode = "compile" hint = """ -To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the -'??' with 'impl <> + <>'. +To ensure a parameter implements multiple traits use the '+ syntax'. Try +replacing the '??' with 'impl <> + <>'. See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax """ @@ -750,8 +842,10 @@ name = "quiz3" path = "exercises/quiz3.rs" mode = "test" hint = """ -To find the best solution to this challenge you're going to need to think back to your -knowledge of traits, specifically Trait Bound Syntax - you may also need this: `use std::fmt::Display;`.""" +To find the best solution to this challenge you're going to need to think back +to your knowledge of traits, specifically 'Trait Bound Syntax' + +You may also need this: `use std::fmt::Display;`.""" # LIFETIMES @@ -768,17 +862,22 @@ name = "lifetimes2" path = "exercises/lifetimes/lifetimes2.rs" mode = "compile" hint = """ -Remember that the generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y. -You can take at least two paths to achieve the desired result while keeping the inner block: -1. Move the string2 declaration to make it live as long as string1 (how is result declared?) -2. Move println! into the inner block""" +Remember that the generic lifetime `'a` will get the concrete lifetime that is +equal to the smaller of the lifetimes of `x` and `y`. + +You can take at least two paths to achieve the desired result while keeping the +inner block: +1. Move the `string2` declaration to make it live as long as `string1` (how is + `result` declared?) +2. Move `println!` into the inner block""" [[exercises]] name = "lifetimes3" path = "exercises/lifetimes/lifetimes3.rs" mode = "compile" hint = """ -If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" +If you use a lifetime annotation in a struct's fields, where else does it need +to be added?""" # TESTS @@ -787,30 +886,39 @@ name = "tests1" path = "exercises/tests/tests1.rs" mode = "test" hint = """ -You don't even need to write any code to test -- you can just test values and run that, even -though you wouldn't do that in real life :) `assert!` is a macro that needs an argument. -Depending on the value of the argument, `assert!` will do nothing (in which case the test will -pass) or `assert!` will panic (in which case the test will fail). So try giving different values -to `assert!` and see which ones compile, which ones pass, and which ones fail :)""" +You don't even need to write any code to test -- you can just test values and +run that, even though you wouldn't do that in real life. :) + +`assert!` is a macro that needs an argument. Depending on the value of the +argument, `assert!` will do nothing (in which case the test will pass) or +`assert!` will panic (in which case the test will fail). + +So try giving different values to `assert!` and see which ones compile, which +ones pass, and which ones fail :)""" [[exercises]] name = "tests2" path = "exercises/tests/tests2.rs" mode = "test" hint = """ -Like the previous exercise, you don't need to write any code to get this test to compile and -run. `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two -values that are equal! Try giving it two arguments that are different! Try giving it two values -that are of different types! Try switching which argument comes first and which comes second!""" +Like the previous exercise, you don't need to write any code to get this test +to compile and run. + +`assert_eq!` is a macro that takes two arguments and compares them. Try giving +it two values that are equal! Try giving it two arguments that are different! +Try giving it two values that are of different types! Try switching which +argument comes first and which comes second!""" [[exercises]] name = "tests3" path = "exercises/tests/tests3.rs" mode = "test" hint = """ -You can call a function right where you're passing arguments to `assert!` -- so you could do -something like `assert!(having_fun())`. If you want to check that you indeed get false, you -can negate the result of what you're doing using `!`, like `assert!(!having_fun())`.""" +You can call a function right where you're passing arguments to `assert!`. So +you could do something like `assert!(having_fun())`. + +If you want to check that you indeed get `false`, you can negate the result of +what you're doing using `!`, like `assert!(!having_fun())`.""" [[exercises]] name = "tests4" @@ -818,7 +926,9 @@ path = "exercises/tests/tests4.rs" mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. + To handle that you need to add a special attribute to the test function. + You can refer to the docs: https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" @@ -830,14 +940,20 @@ path = "exercises/iterators/iterators1.rs" mode = "test" hint = """ Step 1: -We need to apply something to the collection `my_fav_fruits` before we start to go through -it. What could that be? Take a look at the struct definition for a vector for inspiration: + +We need to apply something to the collection `my_fav_fruits` before we start to +go through it. What could that be? Take a look at the struct definition for a +vector for inspiration: https://doc.rust-lang.org/std/vec/struct.Vec.html + Step 2 & step 3: + Very similar to the lines above and below. You've got this! + Step 4: -An iterator goes through all elements in a collection, but what if we've run out of -elements? What should we expect here? If you're stuck, take a look at + +An iterator goes through all elements in a collection, but what if we've run +out of elements? What should we expect here? If you're stuck, take a look at https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. """ @@ -846,50 +962,55 @@ name = "iterators2" path = "exercises/iterators/iterators2.rs" mode = "test" hint = """ -Step 1 +Step 1: + The variable `first` is a `char`. It needs to be capitalized and added to the remaining characters in `c` in order to return the correct `String`. + The remaining characters in `c` can be viewed as a string slice using the `as_str` method. + The documentation for `char` contains many useful methods. https://doc.rust-lang.org/std/primitive.char.html -Step 2 +Step 2: + Create an iterator from the slice. Transform the iterated values by applying -the `capitalize_first` function. Remember to collect the iterator. +the `capitalize_first` function. Remember to `collect` the iterator. + +Step 3: -Step 3. -This is surprisingly similar to the previous solution. Collect is very powerful -and very general. Rust just needs to know the desired type.""" +This is surprisingly similar to the previous solution. `collect` is very +powerful and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" path = "exercises/iterators/iterators3.rs" mode = "test" hint = """ -The divide function needs to return the correct error when even division is not -possible. +The `divide` function needs to return the correct error when even division is +not possible. -The division_results variable needs to be collected into a collection type. +The `division_results` variable needs to be collected into a collection type. -The result_with_list function needs to return a single Result where the success -case is a vector of integers and the failure case is a DivisionError. +The `result_with_list` function needs to return a single `Result` where the +success case is a vector of integers and the failure case is a `DivisionError`. -The list_of_results function needs to return a vector of results. +The `list_of_results` function needs to return a vector of results. -See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how -the `FromIterator` trait is used in `collect()`. This trait is REALLY powerful! It -can make the solution to this exercise infinitely easier.""" +See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for +how the `FromIterator` trait is used in `collect()`. This trait is REALLY +powerful! It can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" path = "exercises/iterators/iterators4.rs" mode = "test" hint = """ -In an imperative language, you might write a for loop that updates -a mutable variable. Or, you might write code utilizing recursion -and a match clause. In Rust you can take another functional -approach, computing the factorial elegantly with ranges and iterators. +In an imperative language, you might write a `for` loop that updates a mutable +variable. Or, you might write code utilizing recursion and a match clause. In +Rust you can take another functional approach, computing the factorial +elegantly with ranges and iterators. Hint 2: Check out the `fold` and `rfold` methods!""" @@ -898,16 +1019,17 @@ name = "iterators5" path = "exercises/iterators/iterators5.rs" mode = "test" hint = """ -The documentation for the std::iter::Iterator trait contains numerous methods +The documentation for the `std::iter::Iterator` trait contains numerous methods that would be helpful here. -The collection variable in count_collection_iterator is a slice of HashMaps. It -needs to be converted into an iterator in order to use the iterator methods. +The `collection` variable in `count_collection_iterator` is a slice of +`HashMap`s. It needs to be converted into an iterator in order to use the +iterator methods. -The fold method can be useful in the count_collection_iterator function. +The `fold` method can be useful in the `count_collection_iterator` function. -For a further challenge, consult the documentation for Iterator to find -a different method that could make your code more compact than using fold.""" +For a further challenge, consult the documentation for `Iterator` to find +a different method that could make your code more compact than using `fold`.""" # SMART POINTERS @@ -916,17 +1038,23 @@ name = "box1" path = "exercises/smart_pointers/box1.rs" mode = "test" hint = """ -Step 1 -The compiler's message should help: since we cannot store the value of the actual type -when working with recursive types, we need to store a reference (pointer) to its value. -We should, therefore, place our `List` inside a `Box`. More details in the book here: -https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2 -Creating an empty list should be fairly straightforward (hint: peek at the assertions). -For a non-empty list keep in mind that we want to use our Cons "list builder". -Although the current list is one of integers (i32), feel free to change the definition -and try other types! +Step 1: + +The compiler's message should help: since we cannot store the value of the +actual type when working with recursive types, we need to store a reference +(pointer) to its value. + +We should, therefore, place our `List` inside a `Box`. More details in the book +here: https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes + +Step 2: + +Creating an empty list should be fairly straightforward (hint: peek at the +assertions). + +For a non-empty list keep in mind that we want to use our `Cons` "list builder". +Although the current list is one of integers (`i32`), feel free to change the +definition and try other types! """ [[exercises]] @@ -934,11 +1062,16 @@ name = "rc1" path = "exercises/smart_pointers/rc1.rs" mode = "test" hint = """ -This is a straightforward exercise to use the Rc type. Each Planet has -ownership of the Sun, and uses Rc::clone() to increment the reference count of the Sun. -After using drop() to move the Planets out of scope individually, the reference count goes down. -In the end the sun only has one reference again, to itself. See more at: -https://doc.rust-lang.org/book/ch15-04-rc.html +This is a straightforward exercise to use the `Rc` type. Each `Planet` has +ownership of the `Sun`, and uses `Rc::clone()` to increment the reference count +of the `Sun`. + +After using `drop()` to move the `Planet`s out of scope individually, the +reference count goes down. + +In the end the `Sun` only has one reference again, to itself. + +See more at: https://doc.rust-lang.org/book/ch15-04-rc.html * Unfortunately Pluto is no longer considered a planet :( """ @@ -952,11 +1085,12 @@ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` inside the loop but still in the main thread. -`child_numbers` should be a clone of the Arc of the numbers instead of a +`child_numbers` should be a clone of the `Arc` of the numbers instead of a thread-local copy of the numbers. This is a simple exercise if you understand the underlying concepts, but if this -is too much of a struggle, consider reading through all of Chapter 16 in the book: +is too much of a struggle, consider reading through all of Chapter 16 in the +book: https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html """ @@ -965,7 +1099,8 @@ name = "cow1" path = "exercises/smart_pointers/cow1.rs" mode = "test" hint = """ -If Cow already owns the data it doesn't need to clone it when to_mut() is called. +If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is +called. Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation on the `Cow` type. @@ -985,7 +1120,9 @@ A challenge with multi-threaded applications is that the main thread can finish before the spawned threads are completed. https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles -Use the JoinHandles to wait for each thread to finish and collect their results. +Use the `JoinHandle`s to wait for each thread to finish and collect their +results. + https://doc.rust-lang.org/std/thread/struct.JoinHandle.html """ @@ -1001,13 +1138,14 @@ mutate the data at a time. Take a look at this section of the book: https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct and keep reading if you'd like more hints :) - Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: -`let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` -Similar to the code in the example in the book that happens after the text -that says "Sharing a Mutex Between Multiple Threads". If not, give that a try! If you -do and would like more hints, keep reading!! +``` +let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); +``` +Similar to the code in the example in the book that happens after the text +that says 'Sharing a Mutex Between Multiple Threads'. If not, give that a +try! If you do and would like more hints, keep reading!! Make sure neither of your threads are holding onto the lock of the mutex while they are sleeping, since this will prevent the other thread from @@ -1023,12 +1161,15 @@ name = "threads3" path = "exercises/threads/threads3.rs" mode = "test" hint = """ -An alternate way to handle concurrency between threads is to use -a mpsc (multiple producer, single consumer) channel to communicate. -With both a sending end and a receiving end, it's possible to -send values in one thread and receive them in another. -Multiple producers are possible by using clone() to create a duplicate -of the original sending end. +An alternate way to handle concurrency between threads is to use an `mpsc` +(multiple producer, single consumer) channel to communicate. + +With both a sending end and a receiving end, it's possible to send values in +one thread and receive them in another. + +Multiple producers are possible by using clone() to create a duplicate of the +original sending end. + See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. """ @@ -1071,13 +1212,14 @@ path = "exercises/macros/macros4.rs" mode = "compile" hint = """ You only need to add a single character to make this compile. -The way macros are written, it wants to see something between each -"macro arm", so it can separate them. -That's all the macro exercises we have in here, but it's barely even -scratching the surface of what you can do with Rust's macros. For a more -thorough introduction, you can have a read through the little book of Rust -macros: https://veykril.github.io/tlborm/""" +The way macros are written, it wants to see something between each "macro arm", +so it can separate them. + +That's all the macro exercises we have in here, but it's barely even scratching +the surface of what you can do with Rust's macros. For a more thorough +introduction, you can have a read through 'The Little Book of Rust Macros': +https://veykril.github.io/tlborm/""" # CLIPPY @@ -1087,21 +1229,22 @@ path = "exercises/clippy/clippy1.rs" mode = "clippy" hint = """ Rust stores the highest precision version of any long or infinite precision -mathematical constants in the Rust standard library. +mathematical constants in the Rust standard library: https://doc.rust-lang.org/stable/std/f32/consts/index.html -We may be tempted to use our own approximations for certain mathematical constants, -but clippy recognizes those imprecise mathematical constants as a source of -potential error. +We may be tempted to use our own approximations for certain mathematical +constants, but clippy recognizes those imprecise mathematical constants as a +source of potential error. + See the suggestions of the clippy warning in compile output and use the -appropriate replacement constant from std::f32::consts...""" +appropriate replacement constant from `std::f32::consts`...""" [[exercises]] name = "clippy2" path = "exercises/clippy/clippy2.rs" mode = "clippy" hint = """ -`for` loops over Option values are more clearly expressed as an `if let`""" +`for` loops over `Option` values are more clearly expressed as an `if let`""" [[exercises]] name = "clippy3" @@ -1131,8 +1274,8 @@ name = "from_str" path = "exercises/conversions/from_str.rs" mode = "test" hint = """ -The implementation of FromStr should return an Ok with a Person object, -or an Err with an error if the string is not valid. +The implementation of `FromStr` should return an `Ok` with a `Person` object, +or an `Err` with an error if the string is not valid. This is almost like the `from_into` exercise, but returning errors instead of falling back to a default value. @@ -1153,7 +1296,8 @@ path = "exercises/conversions/try_from_into.rs" mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. -You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html +You can also use the example at +https://doc.rust-lang.org/std/convert/trait.TryFrom.html Is there an implementation of `TryFrom` in the standard library that can both do the required integer conversion and check the range of the input? @@ -1174,4 +1318,4 @@ name = "as_ref_mut" path = "exercises/conversions/as_ref_mut.rs" mode = "test" hint = """ -Add AsRef or AsMut as a trait bound to the functions.""" +Add `AsRef` or `AsMut` as a trait bound to the functions.""" From b97c88f202b9b9c6f635748198203f2dd5b5cfbe Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 13 Oct 2023 13:28:45 +0200 Subject: [PATCH 0416/1432] docs: use new fancy install aliases --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8fac7a28bb..cf001e2862 100644 --- a/README.md +++ b/README.md @@ -25,13 +25,13 @@ You will need to have Rust installed. You can get it by visiting Date: Fri, 13 Oct 2023 15:28:14 -0400 Subject: [PATCH 0417/1432] chore(errors1): fix grammar typo in hint for exercise errors1 This commit corrects a grammar typo in the hint of the errors1 exercise, changing from: "`Ok` and `Err` are one of the variants of `Result`," to: "`Ok` and `Err` are the two variants of `Result`," --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index e3f66e28c5..02599fc570 100644 --- a/info.toml +++ b/info.toml @@ -658,7 +658,7 @@ name = "errors1" path = "exercises/error_handling/errors1.rs" mode = "test" hint = """ -`Ok` and `Err` are one of the variants of `Result`, so what the tests are saying +`Ok` and `Err` are the two variants of `Result`, so what the tests are saying is that `generate_nametag_text` should return a `Result` instead of an `Option`. To make this change, you'll need to: From 642aac6f43f49407308f18306399a910cf8fe6f6 Mon Sep 17 00:00:00 2001 From: Matt Nield <64328730+matthewjnield@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:47:38 -0400 Subject: [PATCH 0418/1432] chore(errors2): minor description wording change This commit makes a minor change in the wording of the description of the errors2 exercise to avoid potential confusion, changing: "A player of the game will type in how many items they want to buy, and the `total_cost` function will calculate the total cost of the tokens." to "A player of the game will type in how many items they want to buy, and the `total_cost` function will calculate the total cost of the items." --- exercises/error_handling/errors2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs index d4a5477b23..631fe67f90 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -3,7 +3,7 @@ // 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 -// the `total_cost` function will calculate the total cost of the tokens. Since +// the `total_cost` function will calculate the total cost of the items. Since // the player typed in the quantity, though, we get it as a string-- and they // might have typed anything, not just numbers! // From c7fccf74c9341fba294dbd895d3e05646d65a558 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:27:25 +0000 Subject: [PATCH 0419/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 3dcc3e6f97..f3807bba00 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -347,6 +347,7 @@ authors. Chris Rose
Chris Rose

πŸš‡ d1t2
d1t2

πŸš‡ docwilco
docwilco

πŸ’» + Matt Nield
Matt Nield

πŸ–‹ From 9a7d88f139e95fb94d42c09ba790afb012f9aad5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:27:26 +0000 Subject: [PATCH 0420/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d31607fdef..42f04e496f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2460,6 +2460,15 @@ "contributions": [ "code" ] + }, + { + "login": "matthewjnield", + "name": "Matt Nield", + "avatar_url": "https://avatars.githubusercontent.com/u/64328730?v=4", + "profile": "https://www.linkedin.com/in/matthew-nield1/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 64d95837e9813541cf5b357de13865ce687ae98d Mon Sep 17 00:00:00 2001 From: Adam Brewer Date: Mon, 16 Oct 2023 07:37:12 -0400 Subject: [PATCH 0421/1432] Update Exercises Directory Names to Reflect Order --- exercises/{intro => 00_intro}/README.md | 0 exercises/{intro => 00_intro}/intro1.rs | 2 +- exercises/{intro => 00_intro}/intro2.rs | 0 .../{variables => 01_variables}/README.md | 0 .../{variables => 01_variables}/variables1.rs | 0 .../{variables => 01_variables}/variables2.rs | 0 .../{variables => 01_variables}/variables3.rs | 0 .../{variables => 01_variables}/variables4.rs | 0 .../{variables => 01_variables}/variables5.rs | 0 .../{variables => 01_variables}/variables6.rs | 0 .../{functions => 02_functions}/README.md | 0 .../{functions => 02_functions}/functions1.rs | 0 .../{functions => 02_functions}/functions2.rs | 0 .../{functions => 02_functions}/functions3.rs | 0 .../{functions => 02_functions}/functions4.rs | 0 .../{functions => 02_functions}/functions5.rs | 0 exercises/{if => 03_if}/README.md | 0 exercises/{if => 03_if}/if1.rs | 0 exercises/{if => 03_if}/if2.rs | 0 exercises/{if => 03_if}/if3.rs | 0 .../README.md | 0 .../primitive_types1.rs | 0 .../primitive_types2.rs | 0 .../primitive_types3.rs | 0 .../primitive_types4.rs | 0 .../primitive_types5.rs | 0 .../primitive_types6.rs | 0 exercises/{vecs => 05_vecs}/README.md | 0 exercises/{vecs => 05_vecs}/vecs1.rs | 0 exercises/{vecs => 05_vecs}/vecs2.rs | 0 .../README.md | 0 .../move_semantics1.rs | 0 .../move_semantics2.rs | 0 .../move_semantics3.rs | 0 .../move_semantics4.rs | 0 .../move_semantics5.rs | 0 .../move_semantics6.rs | 0 exercises/{structs => 07_structs}/README.md | 0 exercises/{structs => 07_structs}/structs1.rs | 0 exercises/{structs => 07_structs}/structs2.rs | 0 exercises/{structs => 07_structs}/structs3.rs | 0 exercises/{enums => 08_enums}/README.md | 0 exercises/{enums => 08_enums}/enums1.rs | 0 exercises/{enums => 08_enums}/enums2.rs | 0 exercises/{enums => 08_enums}/enums3.rs | 0 exercises/{strings => 09_strings}/README.md | 0 exercises/{strings => 09_strings}/strings1.rs | 0 exercises/{strings => 09_strings}/strings2.rs | 0 exercises/{strings => 09_strings}/strings3.rs | 0 exercises/{strings => 09_strings}/strings4.rs | 0 exercises/{modules => 10_modules}/README.md | 0 exercises/{modules => 10_modules}/modules1.rs | 0 exercises/{modules => 10_modules}/modules2.rs | 0 exercises/{modules => 10_modules}/modules3.rs | 0 exercises/{hashmaps => 11_hashmaps}/README.md | 0 .../{hashmaps => 11_hashmaps}/hashmaps1.rs | 0 .../{hashmaps => 11_hashmaps}/hashmaps2.rs | 0 .../{hashmaps => 11_hashmaps}/hashmaps3.rs | 0 exercises/{options => 12_options}/README.md | 0 exercises/{options => 12_options}/options1.rs | 0 exercises/{options => 12_options}/options2.rs | 0 exercises/{options => 12_options}/options3.rs | 0 .../README.md | 0 .../errors1.rs | 0 .../errors2.rs | 0 .../errors3.rs | 0 .../errors4.rs | 0 .../errors5.rs | 0 .../errors6.rs | 0 exercises/{generics => 14_generics}/README.md | 0 .../{generics => 14_generics}/generics1.rs | 0 .../{generics => 14_generics}/generics2.rs | 0 exercises/{traits => 15_traits}/README.md | 0 exercises/{traits => 15_traits}/traits1.rs | 0 exercises/{traits => 15_traits}/traits2.rs | 0 exercises/{traits => 15_traits}/traits3.rs | 0 exercises/{traits => 15_traits}/traits4.rs | 0 exercises/{traits => 15_traits}/traits5.rs | 0 .../{lifetimes => 16_lifetimes}/README.md | 0 .../{lifetimes => 16_lifetimes}/lifetimes1.rs | 0 .../{lifetimes => 16_lifetimes}/lifetimes2.rs | 0 .../{lifetimes => 16_lifetimes}/lifetimes3.rs | 0 exercises/{tests => 17_tests}/README.md | 0 exercises/{tests => 17_tests}/tests1.rs | 0 exercises/{tests => 17_tests}/tests2.rs | 0 exercises/{tests => 17_tests}/tests3.rs | 0 exercises/{tests => 17_tests}/tests4.rs | 0 .../{iterators => 18_iterators}/README.md | 0 .../{iterators => 18_iterators}/iterators1.rs | 0 .../{iterators => 18_iterators}/iterators2.rs | 0 .../{iterators => 18_iterators}/iterators3.rs | 0 .../{iterators => 18_iterators}/iterators4.rs | 0 .../{iterators => 18_iterators}/iterators5.rs | 0 .../README.md | 0 .../arc1.rs | 0 .../box1.rs | 0 .../cow1.rs | 0 .../rc1.rs | 0 exercises/{threads => 20_threads}/README.md | 0 exercises/{threads => 20_threads}/threads1.rs | 0 exercises/{threads => 20_threads}/threads2.rs | 0 exercises/{threads => 20_threads}/threads3.rs | 0 exercises/{macros => 21_macros}/README.md | 0 exercises/{macros => 21_macros}/macros1.rs | 0 exercises/{macros => 21_macros}/macros2.rs | 0 exercises/{macros => 21_macros}/macros3.rs | 0 exercises/{macros => 21_macros}/macros4.rs | 0 exercises/{clippy => 22_clippy}/README.md | 0 exercises/{clippy => 22_clippy}/clippy1.rs | 0 exercises/{clippy => 22_clippy}/clippy2.rs | 0 exercises/{clippy => 22_clippy}/clippy3.rs | 0 .../{conversions => 23_conversions}/README.md | 0 .../as_ref_mut.rs | 0 .../from_into.rs | 0 .../from_str.rs | 0 .../try_from_into.rs | 0 .../using_as.rs | 0 info.toml | 188 +++++++++--------- 118 files changed, 95 insertions(+), 95 deletions(-) rename exercises/{intro => 00_intro}/README.md (100%) rename exercises/{intro => 00_intro}/intro1.rs (98%) rename exercises/{intro => 00_intro}/intro2.rs (100%) rename exercises/{variables => 01_variables}/README.md (100%) rename exercises/{variables => 01_variables}/variables1.rs (100%) rename exercises/{variables => 01_variables}/variables2.rs (100%) rename exercises/{variables => 01_variables}/variables3.rs (100%) rename exercises/{variables => 01_variables}/variables4.rs (100%) rename exercises/{variables => 01_variables}/variables5.rs (100%) rename exercises/{variables => 01_variables}/variables6.rs (100%) rename exercises/{functions => 02_functions}/README.md (100%) rename exercises/{functions => 02_functions}/functions1.rs (100%) rename exercises/{functions => 02_functions}/functions2.rs (100%) rename exercises/{functions => 02_functions}/functions3.rs (100%) rename exercises/{functions => 02_functions}/functions4.rs (100%) rename exercises/{functions => 02_functions}/functions5.rs (100%) rename exercises/{if => 03_if}/README.md (100%) rename exercises/{if => 03_if}/if1.rs (100%) rename exercises/{if => 03_if}/if2.rs (100%) rename exercises/{if => 03_if}/if3.rs (100%) rename exercises/{primitive_types => 04_primitive_types}/README.md (100%) rename exercises/{primitive_types => 04_primitive_types}/primitive_types1.rs (100%) rename exercises/{primitive_types => 04_primitive_types}/primitive_types2.rs (100%) rename exercises/{primitive_types => 04_primitive_types}/primitive_types3.rs (100%) rename exercises/{primitive_types => 04_primitive_types}/primitive_types4.rs (100%) rename exercises/{primitive_types => 04_primitive_types}/primitive_types5.rs (100%) rename exercises/{primitive_types => 04_primitive_types}/primitive_types6.rs (100%) rename exercises/{vecs => 05_vecs}/README.md (100%) rename exercises/{vecs => 05_vecs}/vecs1.rs (100%) rename exercises/{vecs => 05_vecs}/vecs2.rs (100%) rename exercises/{move_semantics => 06_move_semantics}/README.md (100%) rename exercises/{move_semantics => 06_move_semantics}/move_semantics1.rs (100%) rename exercises/{move_semantics => 06_move_semantics}/move_semantics2.rs (100%) rename exercises/{move_semantics => 06_move_semantics}/move_semantics3.rs (100%) rename exercises/{move_semantics => 06_move_semantics}/move_semantics4.rs (100%) rename exercises/{move_semantics => 06_move_semantics}/move_semantics5.rs (100%) rename exercises/{move_semantics => 06_move_semantics}/move_semantics6.rs (100%) rename exercises/{structs => 07_structs}/README.md (100%) rename exercises/{structs => 07_structs}/structs1.rs (100%) rename exercises/{structs => 07_structs}/structs2.rs (100%) rename exercises/{structs => 07_structs}/structs3.rs (100%) rename exercises/{enums => 08_enums}/README.md (100%) rename exercises/{enums => 08_enums}/enums1.rs (100%) rename exercises/{enums => 08_enums}/enums2.rs (100%) rename exercises/{enums => 08_enums}/enums3.rs (100%) rename exercises/{strings => 09_strings}/README.md (100%) rename exercises/{strings => 09_strings}/strings1.rs (100%) rename exercises/{strings => 09_strings}/strings2.rs (100%) rename exercises/{strings => 09_strings}/strings3.rs (100%) rename exercises/{strings => 09_strings}/strings4.rs (100%) rename exercises/{modules => 10_modules}/README.md (100%) rename exercises/{modules => 10_modules}/modules1.rs (100%) rename exercises/{modules => 10_modules}/modules2.rs (100%) rename exercises/{modules => 10_modules}/modules3.rs (100%) rename exercises/{hashmaps => 11_hashmaps}/README.md (100%) rename exercises/{hashmaps => 11_hashmaps}/hashmaps1.rs (100%) rename exercises/{hashmaps => 11_hashmaps}/hashmaps2.rs (100%) rename exercises/{hashmaps => 11_hashmaps}/hashmaps3.rs (100%) rename exercises/{options => 12_options}/README.md (100%) rename exercises/{options => 12_options}/options1.rs (100%) rename exercises/{options => 12_options}/options2.rs (100%) rename exercises/{options => 12_options}/options3.rs (100%) rename exercises/{error_handling => 13_error_handling}/README.md (100%) rename exercises/{error_handling => 13_error_handling}/errors1.rs (100%) rename exercises/{error_handling => 13_error_handling}/errors2.rs (100%) rename exercises/{error_handling => 13_error_handling}/errors3.rs (100%) rename exercises/{error_handling => 13_error_handling}/errors4.rs (100%) rename exercises/{error_handling => 13_error_handling}/errors5.rs (100%) rename exercises/{error_handling => 13_error_handling}/errors6.rs (100%) rename exercises/{generics => 14_generics}/README.md (100%) rename exercises/{generics => 14_generics}/generics1.rs (100%) rename exercises/{generics => 14_generics}/generics2.rs (100%) rename exercises/{traits => 15_traits}/README.md (100%) rename exercises/{traits => 15_traits}/traits1.rs (100%) rename exercises/{traits => 15_traits}/traits2.rs (100%) rename exercises/{traits => 15_traits}/traits3.rs (100%) rename exercises/{traits => 15_traits}/traits4.rs (100%) rename exercises/{traits => 15_traits}/traits5.rs (100%) rename exercises/{lifetimes => 16_lifetimes}/README.md (100%) rename exercises/{lifetimes => 16_lifetimes}/lifetimes1.rs (100%) rename exercises/{lifetimes => 16_lifetimes}/lifetimes2.rs (100%) rename exercises/{lifetimes => 16_lifetimes}/lifetimes3.rs (100%) rename exercises/{tests => 17_tests}/README.md (100%) rename exercises/{tests => 17_tests}/tests1.rs (100%) rename exercises/{tests => 17_tests}/tests2.rs (100%) rename exercises/{tests => 17_tests}/tests3.rs (100%) rename exercises/{tests => 17_tests}/tests4.rs (100%) rename exercises/{iterators => 18_iterators}/README.md (100%) rename exercises/{iterators => 18_iterators}/iterators1.rs (100%) rename exercises/{iterators => 18_iterators}/iterators2.rs (100%) rename exercises/{iterators => 18_iterators}/iterators3.rs (100%) rename exercises/{iterators => 18_iterators}/iterators4.rs (100%) rename exercises/{iterators => 18_iterators}/iterators5.rs (100%) rename exercises/{smart_pointers => 19_smart_pointers}/README.md (100%) rename exercises/{smart_pointers => 19_smart_pointers}/arc1.rs (100%) rename exercises/{smart_pointers => 19_smart_pointers}/box1.rs (100%) rename exercises/{smart_pointers => 19_smart_pointers}/cow1.rs (100%) rename exercises/{smart_pointers => 19_smart_pointers}/rc1.rs (100%) rename exercises/{threads => 20_threads}/README.md (100%) rename exercises/{threads => 20_threads}/threads1.rs (100%) rename exercises/{threads => 20_threads}/threads2.rs (100%) rename exercises/{threads => 20_threads}/threads3.rs (100%) rename exercises/{macros => 21_macros}/README.md (100%) rename exercises/{macros => 21_macros}/macros1.rs (100%) rename exercises/{macros => 21_macros}/macros2.rs (100%) rename exercises/{macros => 21_macros}/macros3.rs (100%) rename exercises/{macros => 21_macros}/macros4.rs (100%) rename exercises/{clippy => 22_clippy}/README.md (100%) rename exercises/{clippy => 22_clippy}/clippy1.rs (100%) rename exercises/{clippy => 22_clippy}/clippy2.rs (100%) rename exercises/{clippy => 22_clippy}/clippy3.rs (100%) rename exercises/{conversions => 23_conversions}/README.md (100%) rename exercises/{conversions => 23_conversions}/as_ref_mut.rs (100%) rename exercises/{conversions => 23_conversions}/from_into.rs (100%) rename exercises/{conversions => 23_conversions}/from_str.rs (100%) rename exercises/{conversions => 23_conversions}/try_from_into.rs (100%) rename exercises/{conversions => 23_conversions}/using_as.rs (100%) diff --git a/exercises/intro/README.md b/exercises/00_intro/README.md similarity index 100% rename from exercises/intro/README.md rename to exercises/00_intro/README.md diff --git a/exercises/intro/intro1.rs b/exercises/00_intro/intro1.rs similarity index 98% rename from exercises/intro/intro1.rs rename to exercises/00_intro/intro1.rs index 37fa011226..c5196d62e8 100644 --- a/exercises/intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -29,7 +29,7 @@ fn main() { println!("or logic error. The central concept behind Rustlings is to fix these errors and"); println!("solve the exercises. Good luck!"); println!(); - println!("The source for this exercise is in `exercises/intro/intro1.rs`. Have a look!"); + println!("The source for this exercise is in `exercises/intro00/intro1.rs`. Have a look!"); println!( "Going forward, the source of the exercises will always be in the success/failure output." ); diff --git a/exercises/intro/intro2.rs b/exercises/00_intro/intro2.rs similarity index 100% rename from exercises/intro/intro2.rs rename to exercises/00_intro/intro2.rs diff --git a/exercises/variables/README.md b/exercises/01_variables/README.md similarity index 100% rename from exercises/variables/README.md rename to exercises/01_variables/README.md diff --git a/exercises/variables/variables1.rs b/exercises/01_variables/variables1.rs similarity index 100% rename from exercises/variables/variables1.rs rename to exercises/01_variables/variables1.rs diff --git a/exercises/variables/variables2.rs b/exercises/01_variables/variables2.rs similarity index 100% rename from exercises/variables/variables2.rs rename to exercises/01_variables/variables2.rs diff --git a/exercises/variables/variables3.rs b/exercises/01_variables/variables3.rs similarity index 100% rename from exercises/variables/variables3.rs rename to exercises/01_variables/variables3.rs diff --git a/exercises/variables/variables4.rs b/exercises/01_variables/variables4.rs similarity index 100% rename from exercises/variables/variables4.rs rename to exercises/01_variables/variables4.rs diff --git a/exercises/variables/variables5.rs b/exercises/01_variables/variables5.rs similarity index 100% rename from exercises/variables/variables5.rs rename to exercises/01_variables/variables5.rs diff --git a/exercises/variables/variables6.rs b/exercises/01_variables/variables6.rs similarity index 100% rename from exercises/variables/variables6.rs rename to exercises/01_variables/variables6.rs diff --git a/exercises/functions/README.md b/exercises/02_functions/README.md similarity index 100% rename from exercises/functions/README.md rename to exercises/02_functions/README.md diff --git a/exercises/functions/functions1.rs b/exercises/02_functions/functions1.rs similarity index 100% rename from exercises/functions/functions1.rs rename to exercises/02_functions/functions1.rs diff --git a/exercises/functions/functions2.rs b/exercises/02_functions/functions2.rs similarity index 100% rename from exercises/functions/functions2.rs rename to exercises/02_functions/functions2.rs diff --git a/exercises/functions/functions3.rs b/exercises/02_functions/functions3.rs similarity index 100% rename from exercises/functions/functions3.rs rename to exercises/02_functions/functions3.rs diff --git a/exercises/functions/functions4.rs b/exercises/02_functions/functions4.rs similarity index 100% rename from exercises/functions/functions4.rs rename to exercises/02_functions/functions4.rs diff --git a/exercises/functions/functions5.rs b/exercises/02_functions/functions5.rs similarity index 100% rename from exercises/functions/functions5.rs rename to exercises/02_functions/functions5.rs diff --git a/exercises/if/README.md b/exercises/03_if/README.md similarity index 100% rename from exercises/if/README.md rename to exercises/03_if/README.md diff --git a/exercises/if/if1.rs b/exercises/03_if/if1.rs similarity index 100% rename from exercises/if/if1.rs rename to exercises/03_if/if1.rs diff --git a/exercises/if/if2.rs b/exercises/03_if/if2.rs similarity index 100% rename from exercises/if/if2.rs rename to exercises/03_if/if2.rs diff --git a/exercises/if/if3.rs b/exercises/03_if/if3.rs similarity index 100% rename from exercises/if/if3.rs rename to exercises/03_if/if3.rs diff --git a/exercises/primitive_types/README.md b/exercises/04_primitive_types/README.md similarity index 100% rename from exercises/primitive_types/README.md rename to exercises/04_primitive_types/README.md diff --git a/exercises/primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs similarity index 100% rename from exercises/primitive_types/primitive_types1.rs rename to exercises/04_primitive_types/primitive_types1.rs diff --git a/exercises/primitive_types/primitive_types2.rs b/exercises/04_primitive_types/primitive_types2.rs similarity index 100% rename from exercises/primitive_types/primitive_types2.rs rename to exercises/04_primitive_types/primitive_types2.rs diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs similarity index 100% rename from exercises/primitive_types/primitive_types3.rs rename to exercises/04_primitive_types/primitive_types3.rs diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs similarity index 100% rename from exercises/primitive_types/primitive_types4.rs rename to exercises/04_primitive_types/primitive_types4.rs diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs similarity index 100% rename from exercises/primitive_types/primitive_types5.rs rename to exercises/04_primitive_types/primitive_types5.rs diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs similarity index 100% rename from exercises/primitive_types/primitive_types6.rs rename to exercises/04_primitive_types/primitive_types6.rs diff --git a/exercises/vecs/README.md b/exercises/05_vecs/README.md similarity index 100% rename from exercises/vecs/README.md rename to exercises/05_vecs/README.md diff --git a/exercises/vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs similarity index 100% rename from exercises/vecs/vecs1.rs rename to exercises/05_vecs/vecs1.rs diff --git a/exercises/vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs similarity index 100% rename from exercises/vecs/vecs2.rs rename to exercises/05_vecs/vecs2.rs diff --git a/exercises/move_semantics/README.md b/exercises/06_move_semantics/README.md similarity index 100% rename from exercises/move_semantics/README.md rename to exercises/06_move_semantics/README.md diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs similarity index 100% rename from exercises/move_semantics/move_semantics1.rs rename to exercises/06_move_semantics/move_semantics1.rs diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs similarity index 100% rename from exercises/move_semantics/move_semantics2.rs rename to exercises/06_move_semantics/move_semantics2.rs diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs similarity index 100% rename from exercises/move_semantics/move_semantics3.rs rename to exercises/06_move_semantics/move_semantics3.rs diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs similarity index 100% rename from exercises/move_semantics/move_semantics4.rs rename to exercises/06_move_semantics/move_semantics4.rs diff --git a/exercises/move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs similarity index 100% rename from exercises/move_semantics/move_semantics5.rs rename to exercises/06_move_semantics/move_semantics5.rs diff --git a/exercises/move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs similarity index 100% rename from exercises/move_semantics/move_semantics6.rs rename to exercises/06_move_semantics/move_semantics6.rs diff --git a/exercises/structs/README.md b/exercises/07_structs/README.md similarity index 100% rename from exercises/structs/README.md rename to exercises/07_structs/README.md diff --git a/exercises/structs/structs1.rs b/exercises/07_structs/structs1.rs similarity index 100% rename from exercises/structs/structs1.rs rename to exercises/07_structs/structs1.rs diff --git a/exercises/structs/structs2.rs b/exercises/07_structs/structs2.rs similarity index 100% rename from exercises/structs/structs2.rs rename to exercises/07_structs/structs2.rs diff --git a/exercises/structs/structs3.rs b/exercises/07_structs/structs3.rs similarity index 100% rename from exercises/structs/structs3.rs rename to exercises/07_structs/structs3.rs diff --git a/exercises/enums/README.md b/exercises/08_enums/README.md similarity index 100% rename from exercises/enums/README.md rename to exercises/08_enums/README.md diff --git a/exercises/enums/enums1.rs b/exercises/08_enums/enums1.rs similarity index 100% rename from exercises/enums/enums1.rs rename to exercises/08_enums/enums1.rs diff --git a/exercises/enums/enums2.rs b/exercises/08_enums/enums2.rs similarity index 100% rename from exercises/enums/enums2.rs rename to exercises/08_enums/enums2.rs diff --git a/exercises/enums/enums3.rs b/exercises/08_enums/enums3.rs similarity index 100% rename from exercises/enums/enums3.rs rename to exercises/08_enums/enums3.rs diff --git a/exercises/strings/README.md b/exercises/09_strings/README.md similarity index 100% rename from exercises/strings/README.md rename to exercises/09_strings/README.md diff --git a/exercises/strings/strings1.rs b/exercises/09_strings/strings1.rs similarity index 100% rename from exercises/strings/strings1.rs rename to exercises/09_strings/strings1.rs diff --git a/exercises/strings/strings2.rs b/exercises/09_strings/strings2.rs similarity index 100% rename from exercises/strings/strings2.rs rename to exercises/09_strings/strings2.rs diff --git a/exercises/strings/strings3.rs b/exercises/09_strings/strings3.rs similarity index 100% rename from exercises/strings/strings3.rs rename to exercises/09_strings/strings3.rs diff --git a/exercises/strings/strings4.rs b/exercises/09_strings/strings4.rs similarity index 100% rename from exercises/strings/strings4.rs rename to exercises/09_strings/strings4.rs diff --git a/exercises/modules/README.md b/exercises/10_modules/README.md similarity index 100% rename from exercises/modules/README.md rename to exercises/10_modules/README.md diff --git a/exercises/modules/modules1.rs b/exercises/10_modules/modules1.rs similarity index 100% rename from exercises/modules/modules1.rs rename to exercises/10_modules/modules1.rs diff --git a/exercises/modules/modules2.rs b/exercises/10_modules/modules2.rs similarity index 100% rename from exercises/modules/modules2.rs rename to exercises/10_modules/modules2.rs diff --git a/exercises/modules/modules3.rs b/exercises/10_modules/modules3.rs similarity index 100% rename from exercises/modules/modules3.rs rename to exercises/10_modules/modules3.rs diff --git a/exercises/hashmaps/README.md b/exercises/11_hashmaps/README.md similarity index 100% rename from exercises/hashmaps/README.md rename to exercises/11_hashmaps/README.md diff --git a/exercises/hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs similarity index 100% rename from exercises/hashmaps/hashmaps1.rs rename to exercises/11_hashmaps/hashmaps1.rs diff --git a/exercises/hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs similarity index 100% rename from exercises/hashmaps/hashmaps2.rs rename to exercises/11_hashmaps/hashmaps2.rs diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs similarity index 100% rename from exercises/hashmaps/hashmaps3.rs rename to exercises/11_hashmaps/hashmaps3.rs diff --git a/exercises/options/README.md b/exercises/12_options/README.md similarity index 100% rename from exercises/options/README.md rename to exercises/12_options/README.md diff --git a/exercises/options/options1.rs b/exercises/12_options/options1.rs similarity index 100% rename from exercises/options/options1.rs rename to exercises/12_options/options1.rs diff --git a/exercises/options/options2.rs b/exercises/12_options/options2.rs similarity index 100% rename from exercises/options/options2.rs rename to exercises/12_options/options2.rs diff --git a/exercises/options/options3.rs b/exercises/12_options/options3.rs similarity index 100% rename from exercises/options/options3.rs rename to exercises/12_options/options3.rs diff --git a/exercises/error_handling/README.md b/exercises/13_error_handling/README.md similarity index 100% rename from exercises/error_handling/README.md rename to exercises/13_error_handling/README.md diff --git a/exercises/error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs similarity index 100% rename from exercises/error_handling/errors1.rs rename to exercises/13_error_handling/errors1.rs diff --git a/exercises/error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs similarity index 100% rename from exercises/error_handling/errors2.rs rename to exercises/13_error_handling/errors2.rs diff --git a/exercises/error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs similarity index 100% rename from exercises/error_handling/errors3.rs rename to exercises/13_error_handling/errors3.rs diff --git a/exercises/error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs similarity index 100% rename from exercises/error_handling/errors4.rs rename to exercises/13_error_handling/errors4.rs diff --git a/exercises/error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs similarity index 100% rename from exercises/error_handling/errors5.rs rename to exercises/13_error_handling/errors5.rs diff --git a/exercises/error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs similarity index 100% rename from exercises/error_handling/errors6.rs rename to exercises/13_error_handling/errors6.rs diff --git a/exercises/generics/README.md b/exercises/14_generics/README.md similarity index 100% rename from exercises/generics/README.md rename to exercises/14_generics/README.md diff --git a/exercises/generics/generics1.rs b/exercises/14_generics/generics1.rs similarity index 100% rename from exercises/generics/generics1.rs rename to exercises/14_generics/generics1.rs diff --git a/exercises/generics/generics2.rs b/exercises/14_generics/generics2.rs similarity index 100% rename from exercises/generics/generics2.rs rename to exercises/14_generics/generics2.rs diff --git a/exercises/traits/README.md b/exercises/15_traits/README.md similarity index 100% rename from exercises/traits/README.md rename to exercises/15_traits/README.md diff --git a/exercises/traits/traits1.rs b/exercises/15_traits/traits1.rs similarity index 100% rename from exercises/traits/traits1.rs rename to exercises/15_traits/traits1.rs diff --git a/exercises/traits/traits2.rs b/exercises/15_traits/traits2.rs similarity index 100% rename from exercises/traits/traits2.rs rename to exercises/15_traits/traits2.rs diff --git a/exercises/traits/traits3.rs b/exercises/15_traits/traits3.rs similarity index 100% rename from exercises/traits/traits3.rs rename to exercises/15_traits/traits3.rs diff --git a/exercises/traits/traits4.rs b/exercises/15_traits/traits4.rs similarity index 100% rename from exercises/traits/traits4.rs rename to exercises/15_traits/traits4.rs diff --git a/exercises/traits/traits5.rs b/exercises/15_traits/traits5.rs similarity index 100% rename from exercises/traits/traits5.rs rename to exercises/15_traits/traits5.rs diff --git a/exercises/lifetimes/README.md b/exercises/16_lifetimes/README.md similarity index 100% rename from exercises/lifetimes/README.md rename to exercises/16_lifetimes/README.md diff --git a/exercises/lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs similarity index 100% rename from exercises/lifetimes/lifetimes1.rs rename to exercises/16_lifetimes/lifetimes1.rs diff --git a/exercises/lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs similarity index 100% rename from exercises/lifetimes/lifetimes2.rs rename to exercises/16_lifetimes/lifetimes2.rs diff --git a/exercises/lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs similarity index 100% rename from exercises/lifetimes/lifetimes3.rs rename to exercises/16_lifetimes/lifetimes3.rs diff --git a/exercises/tests/README.md b/exercises/17_tests/README.md similarity index 100% rename from exercises/tests/README.md rename to exercises/17_tests/README.md diff --git a/exercises/tests/tests1.rs b/exercises/17_tests/tests1.rs similarity index 100% rename from exercises/tests/tests1.rs rename to exercises/17_tests/tests1.rs diff --git a/exercises/tests/tests2.rs b/exercises/17_tests/tests2.rs similarity index 100% rename from exercises/tests/tests2.rs rename to exercises/17_tests/tests2.rs diff --git a/exercises/tests/tests3.rs b/exercises/17_tests/tests3.rs similarity index 100% rename from exercises/tests/tests3.rs rename to exercises/17_tests/tests3.rs diff --git a/exercises/tests/tests4.rs b/exercises/17_tests/tests4.rs similarity index 100% rename from exercises/tests/tests4.rs rename to exercises/17_tests/tests4.rs diff --git a/exercises/iterators/README.md b/exercises/18_iterators/README.md similarity index 100% rename from exercises/iterators/README.md rename to exercises/18_iterators/README.md diff --git a/exercises/iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs similarity index 100% rename from exercises/iterators/iterators1.rs rename to exercises/18_iterators/iterators1.rs diff --git a/exercises/iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs similarity index 100% rename from exercises/iterators/iterators2.rs rename to exercises/18_iterators/iterators2.rs diff --git a/exercises/iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs similarity index 100% rename from exercises/iterators/iterators3.rs rename to exercises/18_iterators/iterators3.rs diff --git a/exercises/iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs similarity index 100% rename from exercises/iterators/iterators4.rs rename to exercises/18_iterators/iterators4.rs diff --git a/exercises/iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs similarity index 100% rename from exercises/iterators/iterators5.rs rename to exercises/18_iterators/iterators5.rs diff --git a/exercises/smart_pointers/README.md b/exercises/19_smart_pointers/README.md similarity index 100% rename from exercises/smart_pointers/README.md rename to exercises/19_smart_pointers/README.md diff --git a/exercises/smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs similarity index 100% rename from exercises/smart_pointers/arc1.rs rename to exercises/19_smart_pointers/arc1.rs diff --git a/exercises/smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs similarity index 100% rename from exercises/smart_pointers/box1.rs rename to exercises/19_smart_pointers/box1.rs diff --git a/exercises/smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs similarity index 100% rename from exercises/smart_pointers/cow1.rs rename to exercises/19_smart_pointers/cow1.rs diff --git a/exercises/smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs similarity index 100% rename from exercises/smart_pointers/rc1.rs rename to exercises/19_smart_pointers/rc1.rs diff --git a/exercises/threads/README.md b/exercises/20_threads/README.md similarity index 100% rename from exercises/threads/README.md rename to exercises/20_threads/README.md diff --git a/exercises/threads/threads1.rs b/exercises/20_threads/threads1.rs similarity index 100% rename from exercises/threads/threads1.rs rename to exercises/20_threads/threads1.rs diff --git a/exercises/threads/threads2.rs b/exercises/20_threads/threads2.rs similarity index 100% rename from exercises/threads/threads2.rs rename to exercises/20_threads/threads2.rs diff --git a/exercises/threads/threads3.rs b/exercises/20_threads/threads3.rs similarity index 100% rename from exercises/threads/threads3.rs rename to exercises/20_threads/threads3.rs diff --git a/exercises/macros/README.md b/exercises/21_macros/README.md similarity index 100% rename from exercises/macros/README.md rename to exercises/21_macros/README.md diff --git a/exercises/macros/macros1.rs b/exercises/21_macros/macros1.rs similarity index 100% rename from exercises/macros/macros1.rs rename to exercises/21_macros/macros1.rs diff --git a/exercises/macros/macros2.rs b/exercises/21_macros/macros2.rs similarity index 100% rename from exercises/macros/macros2.rs rename to exercises/21_macros/macros2.rs diff --git a/exercises/macros/macros3.rs b/exercises/21_macros/macros3.rs similarity index 100% rename from exercises/macros/macros3.rs rename to exercises/21_macros/macros3.rs diff --git a/exercises/macros/macros4.rs b/exercises/21_macros/macros4.rs similarity index 100% rename from exercises/macros/macros4.rs rename to exercises/21_macros/macros4.rs diff --git a/exercises/clippy/README.md b/exercises/22_clippy/README.md similarity index 100% rename from exercises/clippy/README.md rename to exercises/22_clippy/README.md diff --git a/exercises/clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs similarity index 100% rename from exercises/clippy/clippy1.rs rename to exercises/22_clippy/clippy1.rs diff --git a/exercises/clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs similarity index 100% rename from exercises/clippy/clippy2.rs rename to exercises/22_clippy/clippy2.rs diff --git a/exercises/clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs similarity index 100% rename from exercises/clippy/clippy3.rs rename to exercises/22_clippy/clippy3.rs diff --git a/exercises/conversions/README.md b/exercises/23_conversions/README.md similarity index 100% rename from exercises/conversions/README.md rename to exercises/23_conversions/README.md diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs similarity index 100% rename from exercises/conversions/as_ref_mut.rs rename to exercises/23_conversions/as_ref_mut.rs diff --git a/exercises/conversions/from_into.rs b/exercises/23_conversions/from_into.rs similarity index 100% rename from exercises/conversions/from_into.rs rename to exercises/23_conversions/from_into.rs diff --git a/exercises/conversions/from_str.rs b/exercises/23_conversions/from_str.rs similarity index 100% rename from exercises/conversions/from_str.rs rename to exercises/23_conversions/from_str.rs diff --git a/exercises/conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs similarity index 100% rename from exercises/conversions/try_from_into.rs rename to exercises/23_conversions/try_from_into.rs diff --git a/exercises/conversions/using_as.rs b/exercises/23_conversions/using_as.rs similarity index 100% rename from exercises/conversions/using_as.rs rename to exercises/23_conversions/using_as.rs diff --git a/info.toml b/info.toml index 02599fc570..bbfee14247 100644 --- a/info.toml +++ b/info.toml @@ -2,15 +2,15 @@ [[exercises]] name = "intro1" -path = "exercises/intro/intro1.rs" +path = "exercises/00_intro/intro1.rs" mode = "compile" hint = """ -Remove the `I AM NOT DONE` comment in the `exercises/intro/intro1.rs` file +Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file to move on to the next exercise.""" [[exercises]] name = "intro2" -path = "exercises/intro/intro2.rs" +path = "exercises/00_intro/intro2.rs" mode = "compile" hint = """ Add an argument after the format string.""" @@ -19,7 +19,7 @@ Add an argument after the format string.""" [[exercises]] name = "variables1" -path = "exercises/variables/variables1.rs" +path = "exercises/01_variables/variables1.rs" mode = "compile" hint = """ The declaration in the first line in the main function is missing a keyword @@ -27,7 +27,7 @@ that is needed in Rust to create a new variable binding.""" [[exercises]] name = "variables2" -path = "exercises/variables/variables2.rs" +path = "exercises/01_variables/variables2.rs" mode = "compile" hint = """ The compiler message is saying that Rust cannot infer the type that the @@ -46,7 +46,7 @@ What if `x` is the same type as `10`? What if it's a different type?""" [[exercises]] name = "variables3" -path = "exercises/variables/variables3.rs" +path = "exercises/01_variables/variables3.rs" mode = "compile" hint = """ Oops! In this exercise, we have a variable binding that we've created on in the @@ -60,7 +60,7 @@ programming language -- thankfully the Rust compiler has caught this for us!""" [[exercises]] name = "variables4" -path = "exercises/variables/variables4.rs" +path = "exercises/01_variables/variables4.rs" mode = "compile" hint = """ In Rust, variable bindings are immutable by default. But here we're trying @@ -69,7 +69,7 @@ a variable binding mutable instead.""" [[exercises]] name = "variables5" -path = "exercises/variables/variables5.rs" +path = "exercises/01_variables/variables5.rs" mode = "compile" hint = """ In `variables4` we already learned how to make an immutable variable mutable @@ -87,7 +87,7 @@ Try to solve this exercise afterwards using this technique.""" [[exercises]] name = "variables6" -path = "exercises/variables/variables6.rs" +path = "exercises/01_variables/variables6.rs" mode = "compile" hint = """ We know about variables and mutability, but there is another important type of @@ -107,7 +107,7 @@ https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants [[exercises]] name = "functions1" -path = "exercises/functions/functions1.rs" +path = "exercises/02_functions/functions1.rs" mode = "compile" hint = """ This main function is calling a function that it expects to exist, but the @@ -117,7 +117,7 @@ Sounds a lot like `main`, doesn't it?""" [[exercises]] name = "functions2" -path = "exercises/functions/functions2.rs" +path = "exercises/02_functions/functions2.rs" mode = "compile" hint = """ Rust requires that all parts of a function's signature have type annotations, @@ -125,7 +125,7 @@ but `call_me` is missing the type annotation of `num`.""" [[exercises]] name = "functions3" -path = "exercises/functions/functions3.rs" +path = "exercises/02_functions/functions3.rs" mode = "compile" hint = """ This time, the function *declaration* is okay, but there's something wrong @@ -137,7 +137,7 @@ DONE` comment.""" [[exercises]] name = "functions4" -path = "exercises/functions/functions4.rs" +path = "exercises/02_functions/functions4.rs" mode = "compile" hint = """ The error message points to the function `sale_price` and says it expects a type @@ -149,7 +149,7 @@ for the prices here, since they can't be negative? If so, kudos!""" [[exercises]] name = "functions5" -path = "exercises/functions/functions5.rs" +path = "exercises/02_functions/functions5.rs" mode = "compile" hint = """ This is a really common error that can be fixed by removing one character. @@ -168,7 +168,7 @@ They are not the same. There are two solutions: [[exercises]] name = "if1" -path = "exercises/if/if1.rs" +path = "exercises/03_if/if1.rs" mode = "test" hint = """ It's possible to do this in one line if you would like! @@ -184,7 +184,7 @@ Remember in Rust that: [[exercises]] name = "if2" -path = "exercises/if/if2.rs" +path = "exercises/03_if/if2.rs" mode = "test" hint = """ For that first compiler error, it's important in Rust that each conditional @@ -193,7 +193,7 @@ conditions checking different input values.""" [[exercises]] name = "if3" -path = "exercises/if/if3.rs" +path = "exercises/03_if/if3.rs" mode = "test" hint = """ In Rust, every arm of an `if` expression has to return the same type of value. @@ -211,19 +211,19 @@ hint = "No hints this time ;)" [[exercises]] name = "primitive_types1" -path = "exercises/primitive_types/primitive_types1.rs" +path = "exercises/04_primitive_types/primitive_types1.rs" mode = "compile" hint = "No hints this time ;)" [[exercises]] name = "primitive_types2" -path = "exercises/primitive_types/primitive_types2.rs" +path = "exercises/04_primitive_types/primitive_types2.rs" mode = "compile" hint = "No hints this time ;)" [[exercises]] name = "primitive_types3" -path = "exercises/primitive_types/primitive_types3.rs" +path = "exercises/04_primitive_types/primitive_types3.rs" mode = "compile" hint = """ There's a shorthand to initialize Arrays with a certain size that does not @@ -239,7 +239,7 @@ for `a.len() >= 100`?""" [[exercises]] name = "primitive_types4" -path = "exercises/primitive_types/primitive_types4.rs" +path = "exercises/04_primitive_types/primitive_types4.rs" mode = "test" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section @@ -254,7 +254,7 @@ https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] name = "primitive_types5" -path = "exercises/primitive_types/primitive_types5.rs" +path = "exercises/04_primitive_types/primitive_types5.rs" mode = "compile" hint = """ Take a look at the 'Data Types -> The Tuple Type' section of the book: @@ -267,7 +267,7 @@ of the tuple. You can do it!!""" [[exercises]] name = "primitive_types6" -path = "exercises/primitive_types/primitive_types6.rs" +path = "exercises/04_primitive_types/primitive_types6.rs" mode = "test" hint = """ While you could use a destructuring `let` for the tuple here, try @@ -280,7 +280,7 @@ Now you have another tool in your toolbox!""" [[exercises]] name = "vecs1" -path = "exercises/vecs/vecs1.rs" +path = "exercises/05_vecs/vecs1.rs" mode = "test" hint = """ In Rust, there are two ways to define a Vector. @@ -295,7 +295,7 @@ of the Rust book to learn more. [[exercises]] name = "vecs2" -path = "exercises/vecs/vecs2.rs" +path = "exercises/05_vecs/vecs2.rs" mode = "test" hint = """ In the first function we are looping over the Vector and getting a reference to @@ -318,7 +318,7 @@ What do you think is the more commonly used pattern under Rust developers? [[exercises]] name = "move_semantics1" -path = "exercises/move_semantics/move_semantics1.rs" +path = "exercises/06_move_semantics/move_semantics1.rs" mode = "test" hint = """ So you've got the "cannot borrow immutable local variable `vec` as mutable" @@ -332,7 +332,7 @@ happens!""" [[exercises]] name = "move_semantics2" -path = "exercises/move_semantics/move_semantics2.rs" +path = "exercises/06_move_semantics/move_semantics2.rs" mode = "test" hint = """ When running this exercise for the first time, you'll notice an error about @@ -353,7 +353,7 @@ try them all: [[exercises]] name = "move_semantics3" -path = "exercises/move_semantics/move_semantics3.rs" +path = "exercises/06_move_semantics/move_semantics3.rs" mode = "test" hint = """ The difference between this one and the previous ones is that the first line @@ -363,7 +363,7 @@ an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" -path = "exercises/move_semantics/move_semantics4.rs" +path = "exercises/06_move_semantics/move_semantics4.rs" mode = "test" hint = """ Stop reading whenever you feel like you have enough direction :) Or try @@ -377,7 +377,7 @@ So the end goal is to: [[exercises]] name = "move_semantics5" -path = "exercises/move_semantics/move_semantics5.rs" +path = "exercises/06_move_semantics/move_semantics5.rs" mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in @@ -389,7 +389,7 @@ https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-ref [[exercises]] name = "move_semantics6" -path = "exercises/move_semantics/move_semantics6.rs" +path = "exercises/06_move_semantics/move_semantics6.rs" mode = "compile" hint = """ To find the answer, you can consult the book section "References and Borrowing": @@ -410,7 +410,7 @@ Another hint: it has to do with the `&` character.""" [[exercises]] name = "structs1" -path = "exercises/structs/structs1.rs" +path = "exercises/07_structs/structs1.rs" mode = "test" hint = """ Rust has more than one type of struct. Three actually, all variants are used to @@ -430,7 +430,7 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" -path = "exercises/structs/structs2.rs" +path = "exercises/07_structs/structs2.rs" mode = "test" hint = """ Creating instances of structs is easy, all you need to do is assign some values @@ -442,7 +442,7 @@ https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-ins [[exercises]] name = "structs3" -path = "exercises/structs/structs3.rs" +path = "exercises/07_structs/structs3.rs" mode = "test" hint = """ For `is_international`: What makes a package international? Seems related to @@ -458,13 +458,13 @@ https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" [[exercises]] name = "enums1" -path = "exercises/enums/enums1.rs" +path = "exercises/08_enums/enums1.rs" mode = "compile" hint = "No hints this time ;)" [[exercises]] name = "enums2" -path = "exercises/enums/enums2.rs" +path = "exercises/08_enums/enums2.rs" mode = "compile" hint = """ You can create enumerations that have different variants with different types @@ -472,7 +472,7 @@ such as no data, anonymous structs, a single string, tuples, ...etc""" [[exercises]] name = "enums3" -path = "exercises/enums/enums3.rs" +path = "exercises/08_enums/enums3.rs" mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. @@ -486,7 +486,7 @@ to get value in the variant.""" [[exercises]] name = "strings1" -path = "exercises/strings/strings1.rs" +path = "exercises/09_strings/strings1.rs" mode = "compile" hint = """ The `current_favorite_color` function is currently returning a string slice @@ -500,7 +500,7 @@ another way that uses the `From` trait.""" [[exercises]] name = "strings2" -path = "exercises/strings/strings2.rs" +path = "exercises/09_strings/strings2.rs" mode = "compile" hint = """ Yes, it would be really easy to fix this by just changing the value bound to @@ -515,7 +515,7 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion [[exercises]] name = "strings3" -path = "exercises/strings/strings3.rs" +path = "exercises/09_strings/strings3.rs" mode = "test" hint = """ There's tons of useful standard library functions for strings. Let's try and use some of them: @@ -526,7 +526,7 @@ the string slice into an owned string, which you can then freely extend.""" [[exercises]] name = "strings4" -path = "exercises/strings/strings4.rs" +path = "exercises/09_strings/strings4.rs" mode = "compile" hint = "No hints this time ;)" @@ -534,7 +534,7 @@ hint = "No hints this time ;)" [[exercises]] name = "modules1" -path = "exercises/modules/modules1.rs" +path = "exercises/10_modules/modules1.rs" mode = "compile" hint = """ Everything is private in Rust by default-- but there's a keyword we can use @@ -543,7 +543,7 @@ needs to be public.""" [[exercises]] name = "modules2" -path = "exercises/modules/modules2.rs" +path = "exercises/10_modules/modules2.rs" mode = "compile" hint = """ The delicious_snacks module is trying to present an external interface that is @@ -555,7 +555,7 @@ Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-w [[exercises]] name = "modules3" -path = "exercises/modules/modules3.rs" +path = "exercises/10_modules/modules3.rs" mode = "compile" hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a @@ -566,7 +566,7 @@ paths or the glob operator to bring these two in using only one line.""" [[exercises]] name = "hashmaps1" -path = "exercises/hashmaps/hashmaps1.rs" +path = "exercises/11_hashmaps/hashmaps1.rs" mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out @@ -578,7 +578,7 @@ Hint 2: Number of fruits should be at least 5. And you have to put [[exercises]] name = "hashmaps2" -path = "exercises/hashmaps/hashmaps2.rs" +path = "exercises/11_hashmaps/hashmaps2.rs" mode = "test" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. @@ -587,7 +587,7 @@ Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only- [[exercises]] name = "hashmaps3" -path = "exercises/hashmaps/hashmaps3.rs" +path = "exercises/11_hashmaps/hashmaps3.rs" mode = "test" hint = """ Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert @@ -613,7 +613,7 @@ hint = "No hints this time ;)" [[exercises]] name = "options1" -path = "exercises/options/options1.rs" +path = "exercises/12_options/options1.rs" mode = "test" hint = """ Options can have a `Some` value, with an inner value, or a `None` value, @@ -625,7 +625,7 @@ it doesn't panic in your face later?""" [[exercises]] name = "options2" -path = "exercises/options/options2.rs" +path = "exercises/12_options/options2.rs" mode = "test" hint = """ Check out: @@ -642,7 +642,7 @@ Also see `Option::flatten` [[exercises]] name = "options3" -path = "exercises/options/options3.rs" +path = "exercises/12_options/options3.rs" mode = "compile" hint = """ The compiler says a partial move happened in the `match` statement. How can @@ -655,7 +655,7 @@ https://doc.rust-lang.org/std/keyword.ref.html""" [[exercises]] name = "errors1" -path = "exercises/error_handling/errors1.rs" +path = "exercises/13_error_handling/errors1.rs" mode = "test" hint = """ `Ok` and `Err` are the two variants of `Result`, so what the tests are saying @@ -671,7 +671,7 @@ To make this change, you'll need to: [[exercises]] name = "errors2" -path = "exercises/error_handling/errors2.rs" +path = "exercises/13_error_handling/errors2.rs" mode = "test" hint = """ One way to handle this is using a `match` statement on @@ -687,7 +687,7 @@ and give it a try!""" [[exercises]] name = "errors3" -path = "exercises/error_handling/errors3.rs" +path = "exercises/13_error_handling/errors3.rs" mode = "compile" hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly @@ -699,7 +699,7 @@ positive results.""" [[exercises]] name = "errors4" -path = "exercises/error_handling/errors4.rs" +path = "exercises/13_error_handling/errors4.rs" mode = "test" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning @@ -711,7 +711,7 @@ everything is... okay :)""" [[exercises]] name = "errors5" -path = "exercises/error_handling/errors5.rs" +path = "exercises/13_error_handling/errors5.rs" mode = "compile" hint = """ There are two different possible `Result` types produced within `main()`, which @@ -735,7 +735,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "errors6" -path = "exercises/error_handling/errors6.rs" +path = "exercises/13_error_handling/errors6.rs" mode = "test" hint = """ This exercise uses a completed version of `PositiveNonzeroInteger` from @@ -757,7 +757,7 @@ https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" [[exercises]] name = "generics1" -path = "exercises/generics/generics1.rs" +path = "exercises/14_generics/generics1.rs" mode = "compile" hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any @@ -767,7 +767,7 @@ You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] name = "generics2" -path = "exercises/generics/generics2.rs" +path = "exercises/14_generics/generics2.rs" mode = "test" hint = """ Currently we are wrapping only values of type `u32`. @@ -781,7 +781,7 @@ If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html [[exercises]] name = "traits1" -path = "exercises/traits/traits1.rs" +path = "exercises/15_traits/traits1.rs" mode = "test" hint = """ A discussion about Traits in Rust can be found at: @@ -790,7 +790,7 @@ https://doc.rust-lang.org/book/ch10-02-traits.html [[exercises]] name = "traits2" -path = "exercises/traits/traits2.rs" +path = "exercises/15_traits/traits2.rs" mode = "test" hint = """ Notice how the trait takes ownership of `self`, and returns `Self`. @@ -803,7 +803,7 @@ the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" [[exercises]] name = "traits3" -path = "exercises/traits/traits3.rs" +path = "exercises/15_traits/traits3.rs" mode = "test" hint = """ Traits can have a default implementation for functions. Structs that implement @@ -815,7 +815,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#def [[exercises]] name = "traits4" -path = "exercises/traits/traits4.rs" +path = "exercises/15_traits/traits4.rs" mode = "test" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing @@ -826,7 +826,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#tra [[exercises]] name = "traits5" -path = "exercises/traits/traits5.rs" +path = "exercises/15_traits/traits5.rs" mode = "compile" hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try @@ -851,7 +851,7 @@ You may also need this: `use std::fmt::Display;`.""" [[exercises]] name = "lifetimes1" -path = "exercises/lifetimes/lifetimes1.rs" +path = "exercises/16_lifetimes/lifetimes1.rs" mode = "compile" hint = """ Let the compiler guide you. Also take a look at the book if you need help: @@ -859,7 +859,7 @@ https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] name = "lifetimes2" -path = "exercises/lifetimes/lifetimes2.rs" +path = "exercises/16_lifetimes/lifetimes2.rs" mode = "compile" hint = """ Remember that the generic lifetime `'a` will get the concrete lifetime that is @@ -873,7 +873,7 @@ inner block: [[exercises]] name = "lifetimes3" -path = "exercises/lifetimes/lifetimes3.rs" +path = "exercises/16_lifetimes/lifetimes3.rs" mode = "compile" hint = """ If you use a lifetime annotation in a struct's fields, where else does it need @@ -883,7 +883,7 @@ to be added?""" [[exercises]] name = "tests1" -path = "exercises/tests/tests1.rs" +path = "exercises/17_tests/tests1.rs" mode = "test" hint = """ You don't even need to write any code to test -- you can just test values and @@ -898,7 +898,7 @@ ones pass, and which ones fail :)""" [[exercises]] name = "tests2" -path = "exercises/tests/tests2.rs" +path = "exercises/17_tests/tests2.rs" mode = "test" hint = """ Like the previous exercise, you don't need to write any code to get this test @@ -911,7 +911,7 @@ argument comes first and which comes second!""" [[exercises]] name = "tests3" -path = "exercises/tests/tests3.rs" +path = "exercises/17_tests/tests3.rs" mode = "test" hint = """ You can call a function right where you're passing arguments to `assert!`. So @@ -922,7 +922,7 @@ what you're doing using `!`, like `assert!(!having_fun())`.""" [[exercises]] name = "tests4" -path = "exercises/tests/tests4.rs" +path = "exercises/17_tests/tests4.rs" mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. @@ -936,7 +936,7 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa [[exercises]] name = "iterators1" -path = "exercises/iterators/iterators1.rs" +path = "exercises/18_iterators/iterators1.rs" mode = "test" hint = """ Step 1: @@ -959,7 +959,7 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. [[exercises]] name = "iterators2" -path = "exercises/iterators/iterators2.rs" +path = "exercises/18_iterators/iterators2.rs" mode = "test" hint = """ Step 1: @@ -985,7 +985,7 @@ powerful and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" -path = "exercises/iterators/iterators3.rs" +path = "exercises/18_iterators/iterators3.rs" mode = "test" hint = """ The `divide` function needs to return the correct error when even division is @@ -1004,7 +1004,7 @@ powerful! It can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" -path = "exercises/iterators/iterators4.rs" +path = "exercises/18_iterators/iterators4.rs" mode = "test" hint = """ In an imperative language, you might write a `for` loop that updates a mutable @@ -1016,7 +1016,7 @@ Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" -path = "exercises/iterators/iterators5.rs" +path = "exercises/18_iterators/iterators5.rs" mode = "test" hint = """ The documentation for the `std::iter::Iterator` trait contains numerous methods @@ -1035,7 +1035,7 @@ a different method that could make your code more compact than using `fold`.""" [[exercises]] name = "box1" -path = "exercises/smart_pointers/box1.rs" +path = "exercises/19_smart_pointers/box1.rs" mode = "test" hint = """ Step 1: @@ -1059,7 +1059,7 @@ definition and try other types! [[exercises]] name = "rc1" -path = "exercises/smart_pointers/rc1.rs" +path = "exercises/19_smart_pointers/rc1.rs" mode = "test" hint = """ This is a straightforward exercise to use the `Rc` type. Each `Planet` has @@ -1078,7 +1078,7 @@ See more at: https://doc.rust-lang.org/book/ch15-04-rc.html [[exercises]] name = "arc1" -path = "exercises/smart_pointers/arc1.rs" +path = "exercises/19_smart_pointers/arc1.rs" mode = "compile" hint = """ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order @@ -1096,7 +1096,7 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" -path = "exercises/smart_pointers/cow1.rs" +path = "exercises/19_smart_pointers/cow1.rs" mode = "test" hint = """ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is @@ -1110,7 +1110,7 @@ on the `Cow` type. [[exercises]] name = "threads1" -path = "exercises/threads/threads1.rs" +path = "exercises/20_threads/threads1.rs" mode = "compile" hint = """ `JoinHandle` is a struct that is returned from a spawned thread: @@ -1128,7 +1128,7 @@ https://doc.rust-lang.org/std/thread/struct.JoinHandle.html [[exercises]] name = "threads2" -path = "exercises/threads/threads2.rs" +path = "exercises/20_threads/threads2.rs" mode = "compile" hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access @@ -1158,7 +1158,7 @@ what you've learned :)""" [[exercises]] name = "threads3" -path = "exercises/threads/threads3.rs" +path = "exercises/20_threads/threads3.rs" mode = "test" hint = """ An alternate way to handle concurrency between threads is to use an `mpsc` @@ -1177,7 +1177,7 @@ See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. [[exercises]] name = "macros1" -path = "exercises/macros/macros1.rs" +path = "exercises/21_macros/macros1.rs" mode = "compile" hint = """ When you call a macro, you need to add something special compared to a @@ -1186,7 +1186,7 @@ regular function call. If you're stuck, take a look at what's inside [[exercises]] name = "macros2" -path = "exercises/macros/macros2.rs" +path = "exercises/21_macros/macros2.rs" mode = "compile" hint = """ Macros don't quite play by the same rules as the rest of Rust, in terms of @@ -1197,7 +1197,7 @@ Unlike other things in Rust, the order of "where you define a macro" versus [[exercises]] name = "macros3" -path = "exercises/macros/macros3.rs" +path = "exercises/21_macros/macros3.rs" mode = "compile" hint = """ In order to use a macro outside of its module, you need to do something @@ -1208,7 +1208,7 @@ exported macros, if you've seen any of those around.""" [[exercises]] name = "macros4" -path = "exercises/macros/macros4.rs" +path = "exercises/21_macros/macros4.rs" mode = "compile" hint = """ You only need to add a single character to make this compile. @@ -1225,7 +1225,7 @@ https://veykril.github.io/tlborm/""" [[exercises]] name = "clippy1" -path = "exercises/clippy/clippy1.rs" +path = "exercises/22_clippy/clippy1.rs" mode = "clippy" hint = """ Rust stores the highest precision version of any long or infinite precision @@ -1241,14 +1241,14 @@ appropriate replacement constant from `std::f32::consts`...""" [[exercises]] name = "clippy2" -path = "exercises/clippy/clippy2.rs" +path = "exercises/22_clippy/clippy2.rs" mode = "clippy" hint = """ `for` loops over `Option` values are more clearly expressed as an `if let`""" [[exercises]] name = "clippy3" -path = "exercises/clippy/clippy3.rs" +path = "exercises/22_clippy/clippy3.rs" mode = "clippy" hint = "No hints this time!" @@ -1256,7 +1256,7 @@ hint = "No hints this time!" [[exercises]] name = "using_as" -path = "exercises/conversions/using_as.rs" +path = "exercises/23_conversions/using_as.rs" mode = "test" hint = """ Use the `as` operator to cast one of the operands in the last line of the @@ -1264,14 +1264,14 @@ Use the `as` operator to cast one of the operands in the last line of the [[exercises]] name = "from_into" -path = "exercises/conversions/from_into.rs" +path = "exercises/23_conversions/from_into.rs" mode = "test" hint = """ Follow the steps provided right before the `From` implementation""" [[exercises]] name = "from_str" -path = "exercises/conversions/from_str.rs" +path = "exercises/23_conversions/from_str.rs" mode = "test" hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, @@ -1292,7 +1292,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "try_from_into" -path = "exercises/conversions/try_from_into.rs" +path = "exercises/23_conversions/try_from_into.rs" mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. @@ -1315,7 +1315,7 @@ Challenge: Can you make the `TryFrom` implementations generic over many integer [[exercises]] name = "as_ref_mut" -path = "exercises/conversions/as_ref_mut.rs" +path = "exercises/23_conversions/as_ref_mut.rs" mode = "test" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" From 3c4fde46106efab749ab9af9fe76f5e75779ef5d Mon Sep 17 00:00:00 2001 From: markgreene74 Date: Mon, 16 Oct 2023 22:41:34 +0100 Subject: [PATCH 0422/1432] fix(watch): update the CLIPPY_CARGO_TOML_PATH ... to reflect the changes to the exercise directory names. The path exercises/clippy replaced with exercises/22_clippy. closes #1726 --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index c7b5672d35..664b362bc3 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -12,7 +12,7 @@ const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE"; const CONTEXT: usize = 2; -const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/clippy/Cargo.toml"; +const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml"; // Get a temporary file name that is hopefully unique #[inline] From 3545c5a7a403bda177084564c7ceedad50aec8e0 Mon Sep 17 00:00:00 2001 From: markgreene74 Date: Mon, 16 Oct 2023 22:53:21 +0100 Subject: [PATCH 0423/1432] fix(intro1.rs): typo in the exercise body --- exercises/00_intro/intro1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index c5196d62e8..5dd18b4599 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -29,7 +29,7 @@ fn main() { println!("or logic error. The central concept behind Rustlings is to fix these errors and"); println!("solve the exercises. Good luck!"); println!(); - println!("The source for this exercise is in `exercises/intro00/intro1.rs`. Have a look!"); + println!("The source for this exercise is in `exercises/00_intro/intro1.rs`. Have a look!"); println!( "Going forward, the source of the exercises will always be in the success/failure output." ); From d757726aca44bbb4f96de21990c29714a0a497ca Mon Sep 17 00:00:00 2001 From: Versha Dhankar <45564258+VeeDeltaVee@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:54:59 -0700 Subject: [PATCH 0424/1432] docs: fix windows installation instructions Currently, the windows installation instructions download a script from the URL ps1.rustlings.cool. This URL isn't detected as a URL in some cases, which means that PowerShell tries to load the data from a local file called ps1.rustlings.cool. This was breaking my install, and adding the https:// fixed it. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf001e2862..42e282e061 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser Then, you can run: ```ps1 -Start-BitsTransfer -Source ps1.rustlings.cool -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 +Start-BitsTransfer -Source https://ps1.rustlings.cool -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 ``` To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors. From be6630bec6fa0d08352ee70f31cb8e4e091fc64a Mon Sep 17 00:00:00 2001 From: The Bearodactyl <114454115+TheBearodactyl@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:59:58 +0000 Subject: [PATCH 0425/1432] Update install.sh --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 9aca5b6843..5915a33dd3 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -echo "Let's get you set up with Rustlings!" +echo -e "\nLet's get you set up with Rustlings!" echo "Checking requirements..." if [ -x "$(command -v git)" ] From 9394825018475e77915932e93300a30c8d392db1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:32:32 +0000 Subject: [PATCH 0426/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index f3807bba00..f7174a36c5 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -348,6 +348,7 @@ authors. d1t2
d1t2

πŸš‡ docwilco
docwilco

πŸ’» Matt Nield
Matt Nield

πŸ–‹ + The Bearodactyl
The Bearodactyl

πŸ’» From faa261e3e20b17f65b609916ec351712b91595c1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:32:33 +0000 Subject: [PATCH 0427/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 42f04e496f..51e14a0a53 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2469,6 +2469,15 @@ "contributions": [ "content" ] + }, + { + "login": "TheBearodactyl", + "name": "The Bearodactyl", + "avatar_url": "https://avatars.githubusercontent.com/u/114454115?v=4", + "profile": "https://github.com/TheBearodactyl", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From a4698d8e1043c01b61ce42ad62535c13c1934453 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:33:37 +0000 Subject: [PATCH 0428/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index f7174a36c5..4fa91c066a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -350,6 +350,9 @@ authors. Matt Nield
Matt Nield

πŸ–‹ The Bearodactyl
The Bearodactyl

πŸ’» + + markgreene74
markgreene74

πŸ’» + From 5b25de927e89f138779ad82963cc50c6d8def4ff Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:33:38 +0000 Subject: [PATCH 0429/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 51e14a0a53..232aac3d7a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2478,6 +2478,15 @@ "contributions": [ "code" ] + }, + { + "login": "markgreene74", + "name": "markgreene74", + "avatar_url": "https://avatars.githubusercontent.com/u/18945890?v=4", + "profile": "https://github.com/markgreene74", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From c5c33578d9d1cad9f1df4b220d32a046dd4668a0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:34:40 +0000 Subject: [PATCH 0430/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4fa91c066a..a120b0a823 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -352,6 +352,7 @@ authors. markgreene74
markgreene74

πŸ’» + Versha Dhankar
Versha Dhankar

πŸ“– From e74a7001ef29d482b6b28b4dbf3c7db952adeac9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:34:41 +0000 Subject: [PATCH 0431/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 232aac3d7a..574ac66263 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2487,6 +2487,15 @@ "contributions": [ "code" ] + }, + { + "login": "VeeDeltaVee", + "name": "Versha Dhankar", + "avatar_url": "https://avatars.githubusercontent.com/u/45564258?v=4", + "profile": "https://github.com/VeeDeltaVee", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 8, From 2ac6606c6c747f32f8ead1ec3505e4cae6edfa5d Mon Sep 17 00:00:00 2001 From: Tristram Oaten Date: Fri, 20 Oct 2023 17:31:56 +0100 Subject: [PATCH 0432/1432] fix(intro2): changed intro2 to be a name error, not a format string error. --- exercises/00_intro/intro2.rs | 2 +- info.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index 990b20f09c..a28ad3dc45 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -8,5 +8,5 @@ // I AM NOT DONE fn main() { - println!("Hello {}!"); + printline!("Hello there!") } diff --git a/info.toml b/info.toml index bbfee14247..44f344a391 100644 --- a/info.toml +++ b/info.toml @@ -13,7 +13,7 @@ name = "intro2" path = "exercises/00_intro/intro2.rs" mode = "compile" hint = """ -Add an argument after the format string.""" +The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" # VARIABLES From 24f819d8236ba963a52a4aed7d24fb6e3cf3c3ef Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 09:03:37 +0000 Subject: [PATCH 0433/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index a120b0a823..26bf369313 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -353,6 +353,7 @@ authors. markgreene74
markgreene74

πŸ’» Versha Dhankar
Versha Dhankar

πŸ“– + Tristram Oaten
Tristram Oaten

πŸ–‹ From 9de432e6bf628dfb4ad2ff4b59ee731961cfcfda Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 09:03:38 +0000 Subject: [PATCH 0434/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 574ac66263..2e965fdcce 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2496,6 +2496,15 @@ "contributions": [ "doc" ] + }, + { + "login": "0atman", + "name": "Tristram Oaten", + "avatar_url": "https://avatars.githubusercontent.com/u/114097?v=4", + "profile": "http://0atman.com", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 3181d9f3f88a2113c67ff784f414c7e89a21d425 Mon Sep 17 00:00:00 2001 From: danieltinazzi Date: Sat, 4 Nov 2023 17:29:10 +0100 Subject: [PATCH 0435/1432] fix progress bar count --- src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 89bd444f3f..bbff712d64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -362,7 +362,10 @@ fn watch( .iter() .filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)), ); - let num_done = exercises.iter().filter(|e| e.looks_done()).count(); + let num_done = exercises + .iter() + .filter(|e| e.looks_done() && !filepath.ends_with(&e.path)) + .count(); clear_screen(); match verify( pending_exercises, From 7e16e7721ae22c39dc349cfa72d0ede83f8af2f5 Mon Sep 17 00:00:00 2001 From: Raymon Roos Date: Sun, 5 Nov 2023 15:30:47 +0100 Subject: [PATCH 0436/1432] fix(traits3): grammar mistake in the hint for traits3 --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 44f344a391..887662ab80 100644 --- a/info.toml +++ b/info.toml @@ -808,7 +808,7 @@ mode = "test" hint = """ Traits can have a default implementation for functions. Structs that implement the trait can then use the default version of these functions if they choose not -implement the function themselves. +to implement the function themselves. See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations """ From cb4776097882c31330da2b7aba8faac743bc4e57 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:38:19 +0000 Subject: [PATCH 0437/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 26bf369313..73c99ed802 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -354,6 +354,7 @@ authors. markgreene74
markgreene74

πŸ’» Versha Dhankar
Versha Dhankar

πŸ“– Tristram Oaten
Tristram Oaten

πŸ–‹ + Daniel Tinazzi
Daniel Tinazzi

πŸ–‹ From 3b3416792474f9528a83550408b1d7bccecdce36 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:38:20 +0000 Subject: [PATCH 0438/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2e965fdcce..417bb395ea 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2505,6 +2505,15 @@ "contributions": [ "content" ] + }, + { + "login": "danieltinazzi", + "name": "Daniel Tinazzi", + "avatar_url": "https://avatars.githubusercontent.com/u/11833533?v=4", + "profile": "https://github.com/danieltinazzi", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From e0904de6ea38154ad1454f16569c0aa7f94ba718 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:39:54 +0000 Subject: [PATCH 0439/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 73c99ed802..05c48b6e74 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -355,6 +355,7 @@ authors. Versha Dhankar
Versha Dhankar

πŸ“– Tristram Oaten
Tristram Oaten

πŸ–‹ Daniel Tinazzi
Daniel Tinazzi

πŸ–‹ + Raymon Roos
Raymon Roos

πŸ–‹ From 6e7f1f5f07237f4a9e4bdd06c1faf203df735372 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:39:55 +0000 Subject: [PATCH 0440/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 417bb395ea..8256c302a5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2514,6 +2514,15 @@ "contributions": [ "content" ] + }, + { + "login": "raymon-roos", + "name": "Raymon Roos", + "avatar_url": "https://avatars.githubusercontent.com/u/38888470?v=4", + "profile": "https://github.com/raymon-roos", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From dad22169849a4d16d6a74c69a45017dab07677ff Mon Sep 17 00:00:00 2001 From: Sarup Banskota Date: Tue, 7 Nov 2023 13:02:59 +0800 Subject: [PATCH 0441/1432] Add CodeCrafters --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 42e282e061..fdaee2833b 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,8 @@ Run the command `rustlings lsp` which will generate a `rust-project.json` at the Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. +On [CodeCrafters](https://codecrafters.io/rust) you can get some quality practice through recreating different technologies from scratch in Rust (e.g Build your own BitTorrent, HTTP Server, SQLite, etc). + ## Uninstalling Rustlings If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created From 706e075908a6ae0ad2fa3da7ae7279c8b9514f6d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 09:13:11 +0000 Subject: [PATCH 0442/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 05c48b6e74..cd64e3604d 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -356,6 +356,7 @@ authors. Tristram Oaten
Tristram Oaten

πŸ–‹ Daniel Tinazzi
Daniel Tinazzi

πŸ–‹ Raymon Roos
Raymon Roos

πŸ–‹ + Sarup Banskota
Sarup Banskota

πŸ“– From 761fced7868f3dd38da95676cb79723a5be91ae2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 09:13:12 +0000 Subject: [PATCH 0443/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8256c302a5..3033ee9604 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2523,6 +2523,15 @@ "contributions": [ "content" ] + }, + { + "login": "sarupbanskota", + "name": "Sarup Banskota", + "avatar_url": "https://avatars.githubusercontent.com/u/3149580?v=4", + "profile": "https://codecrafters.io", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 8, From 3080141c5ffc6c113506a44e00b7b949a8cc7648 Mon Sep 17 00:00:00 2001 From: junderw Date: Fri, 10 Nov 2023 23:19:23 -0700 Subject: [PATCH 0444/1432] Fix gitignore for clippy exercise --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f319d39dfa..32f3c77bab 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,8 @@ target/ **/*.rs.bk .DS_Store *.pdb -exercises/clippy/Cargo.toml -exercises/clippy/Cargo.lock +exercises/22_clippy/Cargo.toml +exercises/22_clippy/Cargo.lock rust-project.json .idea .vscode/* From 4d6cd0ebb4791be37bc50ac6eddb57981eab0b23 Mon Sep 17 00:00:00 2001 From: Adwait Kumar Singh Date: Sat, 11 Nov 2023 16:39:44 -0800 Subject: [PATCH 0445/1432] Revert "Add CodeCrafters" This reverts commit dad22169849a4d16d6a74c69a45017dab07677ff. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index fdaee2833b..42e282e061 100644 --- a/README.md +++ b/README.md @@ -154,8 +154,6 @@ Run the command `rustlings lsp` which will generate a `rust-project.json` at the Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. -On [CodeCrafters](https://codecrafters.io/rust) you can get some quality practice through recreating different technologies from scratch in Rust (e.g Build your own BitTorrent, HTTP Server, SQLite, etc). - ## Uninstalling Rustlings If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created From 3461c4f73dcedab0b7687e04a806a930ce5f3c17 Mon Sep 17 00:00:00 2001 From: Bastian Pedersen Date: Sun, 12 Nov 2023 11:39:14 +0100 Subject: [PATCH 0446/1432] Reword clippy1 exercise to be more readable --- exercises/22_clippy/clippy1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index 95c0141f47..e0c6ce7c4e 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -3,8 +3,8 @@ // 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. +// 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. From f01f2d13c75c72282e323c3057cb54fbc29c4600 Mon Sep 17 00:00:00 2001 From: Dilshad <98999149+a-rustacean@users.noreply.github.com> Date: Sun, 12 Nov 2023 22:19:40 +0530 Subject: [PATCH 0447/1432] Revert "docs: add sarupbanskota as a contributor for doc" --- .all-contributorsrc | 9 --------- AUTHORS.md | 1 - 2 files changed, 10 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 3033ee9604..8256c302a5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2523,15 +2523,6 @@ "contributions": [ "content" ] - }, - { - "login": "sarupbanskota", - "name": "Sarup Banskota", - "avatar_url": "https://avatars.githubusercontent.com/u/3149580?v=4", - "profile": "https://codecrafters.io", - "contributions": [ - "doc" - ] } ], "contributorsPerLine": 8, diff --git a/AUTHORS.md b/AUTHORS.md index cd64e3604d..05c48b6e74 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -356,7 +356,6 @@ authors. Tristram Oaten
Tristram Oaten

πŸ–‹ Daniel Tinazzi
Daniel Tinazzi

πŸ–‹ Raymon Roos
Raymon Roos

πŸ–‹ - Sarup Banskota
Sarup Banskota

πŸ“– From 0f629a47a564427fc7348eeb370f3a97040fdea5 Mon Sep 17 00:00:00 2001 From: jbouganim-parallel <150748285+jbouganim-parallel@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:30:38 -0800 Subject: [PATCH 0448/1432] fix broken link in windows installation instructions https://github.com/rust-lang/rustlings/issues/1759 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdaee2833b..1011c8060f 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser Then, you can run: ```ps1 -Start-BitsTransfer -Source https://ps1.rustlings.cool -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 +Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 ``` To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors. From 77b687d501771c24bd83294d97b8e6f9ffa92d6b Mon Sep 17 00:00:00 2001 From: Matthias Richter Date: Tue, 14 Nov 2023 08:19:15 +0100 Subject: [PATCH 0449/1432] fix(enums) make enum variants more consistent closes #1545 --- exercises/08_enums/enums1.rs | 1 + exercises/08_enums/enums2.rs | 9 ++++++++- exercises/08_enums/enums3.rs | 12 ++++++++++++ info.toml | 2 +- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index 25525b252d..ab87270662 100644 --- a/exercises/08_enums/enums1.rs +++ b/exercises/08_enums/enums1.rs @@ -12,6 +12,7 @@ enum Message { fn main() { println!("{:?}", Message::Quit); println!("{:?}", Message::Echo); + println!("{:?}", Message::Resize); println!("{:?}", Message::Move); println!("{:?}", Message::ChangeColor); } diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index df93fe0f15..133c77957f 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -10,6 +10,12 @@ enum Message { // TODO: define the different variants used below } +#[derive(Debug)] +struct Point { + x: u8, + y: u8, +} + impl Message { fn call(&self) { println!("{:?}", self); @@ -18,7 +24,8 @@ impl Message { fn main() { let messages = [ - Message::Move { x: 10, y: 30 }, + Message::Resize { w: 10, h: 30 }, + Message::Move(Point { x: 10, y: 15 }), Message::Echo(String::from("hello world")), Message::ChangeColor(200, 255, 255), Message::Quit, diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index 92d18c46a4..fffb9d01b6 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -18,6 +18,8 @@ struct Point { struct State { color: (u8, u8, u8), + width: u8, + height: u8, position: Point, quit: bool, message: String, @@ -36,6 +38,11 @@ impl State { self.message = s } + fn resize(&mut self, w: u8, h: u8) { + self.width = w; + self.height = h; + } + fn move_position(&mut self, p: Point) { self.position = p; } @@ -55,16 +62,21 @@ mod tests { fn test_match_message_call() { let mut state = State { quit: false, + width: 0, + height: 0, position: Point { x: 0, y: 0 }, color: (0, 0, 0), message: "hello world".to_string(), }; state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Echo(String::from("Hello world!"))); + state.process(Message::Resize { w: 10, h: 30 }); state.process(Message::Move(Point { x: 10, y: 15 })); state.process(Message::Quit); assert_eq!(state.color, (255, 0, 255)); + assert_eq!(state.width, 10); + assert_eq!(state.height, 30); assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); assert_eq!(state.quit, true); diff --git a/info.toml b/info.toml index 887662ab80..578d01c26a 100644 --- a/info.toml +++ b/info.toml @@ -468,7 +468,7 @@ path = "exercises/08_enums/enums2.rs" mode = "compile" hint = """ You can create enumerations that have different variants with different types -such as no data, anonymous structs, a single string, tuples, ...etc""" +such as anonymous structs, structs, a single string, tuples, no data, ...etc""" [[exercises]] name = "enums3" From 70f472484f0e43566d146e3184c39dc650f27a74 Mon Sep 17 00:00:00 2001 From: liv Date: Tue, 14 Nov 2023 15:49:52 +0100 Subject: [PATCH 0450/1432] docs: revert fancy install aliases --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fdaee2833b..20a65cfc92 100644 --- a/README.md +++ b/README.md @@ -25,13 +25,13 @@ You will need to have Rust installed. You can get it by visiting Date: Wed, 15 Nov 2023 23:17:40 +0100 Subject: [PATCH 0451/1432] chore(watch): update notify dependency to v6 closes #1640 --- Cargo.lock | 261 +++++++++++++++++----------------------------------- Cargo.toml | 2 +- src/main.rs | 68 ++++++++------ 3 files changed, 124 insertions(+), 207 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 418032a71d..93489eb047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "bstr" version = "1.6.2" @@ -97,12 +103,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -168,6 +168,25 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + [[package]] name = "difflib" version = "0.4.0" @@ -200,14 +219,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -219,41 +238,15 @@ dependencies = [ "num-traits", ] -[[package]] -name = "fsevent" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" -dependencies = [ - "bitflags", - "fsevent-sys", -] - [[package]] name = "fsevent-sys" -version = "2.0.1" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" dependencies = [ "libc", ] -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - [[package]] name = "glob" version = "0.3.1" @@ -278,7 +271,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -306,11 +299,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.7.1" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" dependencies = [ - "bitflags", + "bitflags 1.3.2", "inotify-sys", "libc", ] @@ -330,16 +323,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", + "cfg-if", ] [[package]] @@ -358,13 +342,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] -name = "kernel32-sys" -version = "0.2.2" +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "bitflags 1.3.2", + "libc", ] [[package]] @@ -373,17 +367,11 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" -version = "0.2.140" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" @@ -391,7 +379,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -402,56 +390,14 @@ checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "mio" -version = "0.6.23" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", "libc", "log", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - -[[package]] -name = "mio-extras" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" -dependencies = [ - "lazycell", - "log", - "mio", - "slab", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - -[[package]] -name = "net2" -version = "0.2.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", + "wasi", + "windows-sys 0.48.0", ] [[package]] @@ -462,20 +408,32 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "notify" -version = "4.0.17" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags", + "bitflags 2.4.1", + "crossbeam-channel", "filetime", - "fsevent", "fsevent-sys", "inotify", + "kqueue", "libc", + "log", "mio", - "mio-extras", "walkdir", - "winapi 0.3.9", + "windows-sys 0.48.0", +] + +[[package]] +name = "notify-debouncer-mini" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43" +dependencies = [ + "crossbeam-channel", + "log", + "notify", ] [[package]] @@ -550,11 +508,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -590,7 +548,7 @@ dependencies = [ "glob", "home", "indicatif", - "notify", + "notify-debouncer-mini", "predicates", "regex", "serde", @@ -653,15 +611,6 @@ dependencies = [ "serde", ] -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - [[package]] name = "strsim" version = "0.10.0" @@ -757,10 +706,10 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.2.8" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" @@ -772,12 +721,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -790,7 +733,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -814,37 +757,13 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -954,13 +873,3 @@ checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] diff --git a/Cargo.toml b/Cargo.toml index a055d4f348..24bee7cf7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] indicatif = "0.17.6" console = "0.15" -notify = "4.0" +notify-debouncer-mini = "0.4.1" toml = "0.7.6" regex = "1.5" serde = { version = "1.0", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index bbff712d64..f17d9d95fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,8 @@ use crate::run::{reset, run}; use crate::verify::verify; use clap::{Parser, Subcommand}; use console::Emoji; -use notify::DebouncedEvent; -use notify::{RecommendedWatcher, RecursiveMode, Watcher}; +use notify_debouncer_mini::notify::{self, RecursiveMode}; +use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use std::ffi::OsStr; use std::fs; use std::io::{self, prelude::*}; @@ -331,8 +331,10 @@ fn watch( let (tx, rx) = channel(); let should_quit = Arc::new(AtomicBool::new(false)); - let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(1))?; - watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?; + let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; + debouncer + .watcher() + .watch(Path::new("./exercises"), RecursiveMode::Recursive)?; clear_screen(); @@ -350,38 +352,44 @@ fn watch( loop { match rx.recv_timeout(Duration::from_secs(1)) { Ok(event) => match event { - DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => { - if b.extension() == Some(OsStr::new("rs")) && b.exists() { - let filepath = b.as_path().canonicalize().unwrap(); - let pending_exercises = exercises - .iter() - .find(|e| filepath.ends_with(&e.path)) - .into_iter() - .chain( + Ok(events) => { + for event in events { + let event_path = event.path; + if event.kind == DebouncedEventKind::Any + && event_path.extension() == Some(OsStr::new("rs")) + && event_path.exists() + { + let filepath = event_path.as_path().canonicalize().unwrap(); + let pending_exercises = exercises .iter() - .filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)), - ); - let num_done = exercises - .iter() - .filter(|e| e.looks_done() && !filepath.ends_with(&e.path)) - .count(); - clear_screen(); - match verify( - pending_exercises, - (num_done, exercises.len()), - verbose, - success_hints, - ) { - Ok(_) => return Ok(WatchStatus::Finished), - Err(exercise) => { - let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap(); - *failed_exercise_hint = Some(to_owned_hint(exercise)); + .find(|e| filepath.ends_with(&e.path)) + .into_iter() + .chain(exercises.iter().filter(|e| { + !e.looks_done() && !filepath.ends_with(&e.path) + })); + let num_done = exercises + .iter() + .filter(|e| e.looks_done() && !filepath.ends_with(&e.path)) + .count(); + clear_screen(); + match verify( + pending_exercises, + (num_done, exercises.len()), + verbose, + success_hints, + ) { + Ok(_) => return Ok(WatchStatus::Finished), + Err(exercise) => { + let mut failed_exercise_hint = + failed_exercise_hint.lock().unwrap(); + *failed_exercise_hint = Some(to_owned_hint(exercise)); + } } } } } - _ => {} + Err(e) => println!("watch error: {e:?}"), }, Err(RecvTimeoutError::Timeout) => { // the timeout expired, just check the `should_quit` variable below then loop again From 8bfe2ec71e4800b798d7c0d7b3224ff2530a1c79 Mon Sep 17 00:00:00 2001 From: Daniel Somerfield Date: Tue, 21 Nov 2023 14:02:26 -0800 Subject: [PATCH 0452/1432] Fix all_fruits_types_in_basket to fail if all fruit kinds are not included --- exercises/11_hashmaps/hashmaps2.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index a592569093..ab918cd8a7 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -18,7 +18,7 @@ use std::collections::HashMap; -#[derive(Hash, PartialEq, Eq)] +#[derive(Hash, PartialEq, Eq, Debug)] enum Fruit { Apple, Banana, @@ -27,6 +27,14 @@ enum Fruit { Pineapple, } +const FRUIT_KINDS: [Fruit; 5] = [ + Fruit::Apple, + Fruit::Banana, + Fruit::Mango, + Fruit::Lychee, + Fruit::Pineapple, +]; + fn fruit_basket(basket: &mut HashMap) { let fruit_kinds = vec![ Fruit::Apple, @@ -81,12 +89,15 @@ mod tests { let count = basket.values().sum::(); assert!(count > 11); } - + #[test] fn all_fruit_types_in_basket() { let mut basket = get_fruit_basket(); fruit_basket(&mut basket); - for amount in basket.values() { + for fruit_kind in FRUIT_KINDS { + let amount = basket + .get(&fruit_kind) + .expect(format!("Fruit kind {:?} was not found in basket", fruit_kind).as_str()); assert_ne!(amount, &0); } } From 5c4821ac6ffb8f62db05895ebeed21ae397ed649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Wed, 22 Nov 2023 01:45:19 +0100 Subject: [PATCH 0453/1432] fix(watch): Fix rendering of the finishing ferris MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 571bab2 ("Run clippy --fix"), the "" string was changed to r"", even though it contains an intentional escape sequence, which now looks wrong. My commit undoes this change: Before: +----------------------------------------------------+ | You made it to the Fe-nish line! | +-------------------------- ------------------------+ \\/ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ After: +----------------------------------------------------+ | You made it to the Fe-nish line! | +-------------------------- ------------------------+ \/ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ Running `cargo clippy` (version 0.1.70) after this commit does not reveal any new warnings. Fixes: 571bab2 ("Run clippy --fix") --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index bbff712d64..2aa6f6bd6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -435,10 +435,10 @@ started, here's a couple of notes about how Rustlings operates: Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. Make sure to have your editor open!"#; -const FENISH_LINE: &str = r"+----------------------------------------------------+ +const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | +-------------------------- ------------------------+ - \\/ + \\/ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ From 12d1bf407ad85da0b1f0c062564af68117ae6111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Wed, 22 Nov 2023 01:40:01 +0100 Subject: [PATCH 0454/1432] feat(watch): Add red color to the finishing ferris This adds some eye-candy for users who finish Rustlings. It is based on ANSI terminal escape sequences and should work in most environments. --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2aa6f6bd6c..97015af55f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -438,7 +438,7 @@ exercise. Make sure to have your editor open!"#; const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | +-------------------------- ------------------------+ - \\/ + \\/\x1b[31m β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ @@ -453,7 +453,7 @@ const FENISH_LINE: &str = "+---------------------------------------------------- β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’ + β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m We hope you enjoyed learning about the various aspects of Rust! If you noticed any issues, please don't hesitate to report them to our repo. From 21b1e6ecf875e1fe1453c54cdba8224e98acb740 Mon Sep 17 00:00:00 2001 From: parnavh Date: Wed, 22 Nov 2023 22:06:17 +0530 Subject: [PATCH 0455/1432] fix(move_semantics): removed unused mut --- exercises/06_move_semantics/move_semantics2.rs | 2 +- exercises/06_move_semantics/move_semantics3.rs | 2 +- exercises/06_move_semantics/move_semantics4.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index baf6bcc953..dc58be5086 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -11,7 +11,7 @@ fn main() { let vec0 = vec![22, 44, 66]; - let mut vec1 = fill_vec(vec0); + 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 7af9e694d6..7152c71681 100644 --- a/exercises/06_move_semantics/move_semantics3.rs +++ b/exercises/06_move_semantics/move_semantics3.rs @@ -12,7 +12,7 @@ fn main() { let vec0 = vec![22, 44, 66]; - let mut vec1 = fill_vec(vec0); + 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 80b49dba26..bfc917fa87 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -13,7 +13,7 @@ fn main() { let vec0 = vec![22, 44, 66]; - let mut vec1 = fill_vec(vec0); + let vec1 = fill_vec(vec0); assert_eq!(vec1, vec![22, 44, 66, 88]); } From 194e0b951d5e2c3c869592c39a20e5acb311e1fa Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:10:37 +0000 Subject: [PATCH 0456/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 05c48b6e74..a768b3fa95 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -356,6 +356,7 @@ authors. Tristram Oaten
Tristram Oaten

πŸ–‹ Daniel Tinazzi
Daniel Tinazzi

πŸ–‹ Raymon Roos
Raymon Roos

πŸ–‹ + Matthias
Matthias

πŸ’» From 5d78a2f103c8dad2f1d8084c04acb8a9c691ac5a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:10:38 +0000 Subject: [PATCH 0457/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8256c302a5..777e59469b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2523,6 +2523,15 @@ "contributions": [ "content" ] + }, + { + "login": "matthri", + "name": "Matthias", + "avatar_url": "https://avatars.githubusercontent.com/u/67913999?v=4", + "profile": "https://github.com/matthri", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 4b800978179c9a07636a17ce4060ab78ae78958b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:33:58 +0000 Subject: [PATCH 0458/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index a768b3fa95..b07ffdc9c1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -357,6 +357,7 @@ authors. Daniel Tinazzi
Daniel Tinazzi

πŸ–‹ Raymon Roos
Raymon Roos

πŸ–‹ Matthias
Matthias

πŸ’» + J. NeuschΓ€fer
J. NeuschΓ€fer

πŸ’» From 3e7f1e3ff24b8618e7738091a5c78d316f673450 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:33:59 +0000 Subject: [PATCH 0459/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 777e59469b..681392bd7e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2532,6 +2532,15 @@ "contributions": [ "code" ] + }, + { + "login": "neuschaefer", + "name": "J. NeuschΓ€fer", + "avatar_url": "https://avatars.githubusercontent.com/u/1021512?v=4", + "profile": "https://github.com/neuschaefer", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 5bbdb3d5ba758ea3fc143c0930ed84a92a4a814d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:39:43 +0000 Subject: [PATCH 0460/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index b07ffdc9c1..21597acb98 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -358,6 +358,7 @@ authors. Raymon Roos
Raymon Roos

πŸ–‹ Matthias
Matthias

πŸ’» J. NeuschΓ€fer
J. NeuschΓ€fer

πŸ’» + Bastian Pedersen
Bastian Pedersen

πŸ–‹ From 8863855627648c140ef1bd64711936e8a6597e1d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:39:44 +0000 Subject: [PATCH 0461/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 681392bd7e..2008f03b18 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2541,6 +2541,15 @@ "contributions": [ "code" ] + }, + { + "login": "bastianpedersen", + "name": "Bastian Pedersen", + "avatar_url": "https://avatars.githubusercontent.com/u/58905488?v=4", + "profile": "https://scooterhacking.org", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From f35f63fa5763bde734ca086aa9e3398e8319539d Mon Sep 17 00:00:00 2001 From: NicolasRoelandt Date: Fri, 8 Dec 2023 17:52:21 +0000 Subject: [PATCH 0462/1432] Remove confusing aside in 23_conversions/from_str.rs The advice tell us how to return as String error message. Unless I missed something, we can't even return a String error message here, so this advice is more confusing than anything and should better be removed. --- exercises/23_conversions/from_str.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index 34472c32cc..e209347475 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -44,10 +44,6 @@ enum ParsePersonError { // 6. If while extracting the name and the age something goes wrong, an error // should be returned // If everything goes well, then return a Result of a Person object -// -// As an aside: `Box` implements `From<&'_ str>`. This means that if -// you want to return a string error message, you can do so via just using -// return `Err("my error message".into())`. impl FromStr for Person { type Err = ParsePersonError; From 5453fad99146905b690f99d76992b840ad8772a2 Mon Sep 17 00:00:00 2001 From: Paul Leydier Date: Mon, 18 Dec 2023 21:16:18 -0500 Subject: [PATCH 0463/1432] docs: sort exercise to book chapter mapping by exercise --- exercises/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/README.md b/exercises/README.md index c7effa95b6..fd0926f18a 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -17,11 +17,11 @@ | error_handling | Β§9 | | generics | Β§10 | | traits | Β§10.2 | -| tests | Β§11.1 | | lifetimes | Β§10.3 | +| tests | Β§11.1 | | iterators | Β§13.2-4 | -| threads | Β§16.1-3 | | smart_pointers | Β§15, Β§16.3 | +| threads | Β§16.1-3 | | macros | Β§19.6 | | clippy | Β§21.4 | | conversions | n/a | From 93aef73eb54759ad7491306e69946de1975012bd Mon Sep 17 00:00:00 2001 From: Sergei Gerasenko Date: Tue, 9 Jan 2024 10:16:51 -0600 Subject: [PATCH 0464/1432] Correct for more standard English --- exercises/11_hashmaps/hashmaps3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 08e977c331..36544ee3e9 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -36,7 +36,7 @@ fn build_scores_table(results: String) -> HashMap { let team_2_score: u8 = v[3].parse().unwrap(); // TODO: Populate the scores table with details extracted from the // current line. Keep in mind that goals scored by team_1 - // will be the number of goals conceded from team_2, and similarly + // will be the number of goals conceded by team_2, and similarly // goals scored by team_2 will be the number of goals conceded by // team_1. } From 3200581d4dfff638db6328990ad061be52b97643 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:51:47 +0000 Subject: [PATCH 0465/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 21597acb98..5466c9cfbc 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -360,6 +360,9 @@ authors. J. NeuschΓ€fer
J. NeuschΓ€fer

πŸ’» Bastian Pedersen
Bastian Pedersen

πŸ–‹ + + gerases
gerases

πŸ–‹ + From e2674498c6504e7d7a7d55f1e19907c77f7b5d91 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:51:48 +0000 Subject: [PATCH 0466/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2008f03b18..cc26876534 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2550,6 +2550,15 @@ "contributions": [ "content" ] + }, + { + "login": "gerases", + "name": "gerases", + "avatar_url": "https://avatars.githubusercontent.com/u/8953623?v=4", + "profile": "https://github.com/gerases", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From b70ed105db29fb908f97b117b56ed3732837df08 Mon Sep 17 00:00:00 2001 From: reifenrath-dev Date: Fri, 19 Jan 2024 11:18:54 +0100 Subject: [PATCH 0467/1432] chore: update from_into.rs task description to fit the code --- exercises/23_conversions/from_into.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 60911f3e21..d78b7b5bf4 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -24,7 +24,7 @@ impl Default for Person { } } -// Your task is to complete this implementation in order for the line `let p = +// Your task is to complete this implementation in order for the line `let p1 = // Person::from("Mark,20")` to compile Please note that you'll need to parse the // age component into a `usize` with something like `"4".parse::()`. The // outcome of this needs to be handled appropriately. From 6072ec16a01461af9914ad89899de498d0309de7 Mon Sep 17 00:00:00 2001 From: Peter Neave Date: Tue, 23 Jan 2024 12:33:21 +1100 Subject: [PATCH 0468/1432] fix: Ensure scripts have LF line endings Use gitattributes file to ensure script files have LF line endings. This solves a problem for users who wish to use DevContainers on Windows machines and the file has been cloned from the repository with CRLF line endings. --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..efdba87644 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +*.sh text eol=lf From bcb192c707009ab9d9bb06812cb09b9d446ccc11 Mon Sep 17 00:00:00 2001 From: LeverImmy <506503360@qq.com> Date: Sun, 4 Feb 2024 10:51:06 +0800 Subject: [PATCH 0469/1432] chore: fixed minor typo --- exercises/23_conversions/from_into.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 60911f3e21..00f8c2ffb7 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -25,7 +25,7 @@ impl Default for Person { } // Your task is to complete this implementation in order for the line `let p = -// Person::from("Mark,20")` to compile Please note that you'll need to parse the +// Person::from("Mark,20")` to compile. Please note that you'll need to parse the // age component into a `usize` with something like `"4".parse::()`. The // outcome of this needs to be handled appropriately. // From 53c40024d894beb45f4319d08ff054d7d7ac319f Mon Sep 17 00:00:00 2001 From: luvchurchill <46406654+luvchurchill@users.noreply.github.com> Date: Tue, 6 Feb 2024 01:54:04 +0200 Subject: [PATCH 0470/1432] Added .git to end of Repo's https URL The install.sh script didn't work for me, after I changed this locally it worked URL needs to end in .git --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 5915a33dd3..fdbe8d4386 100755 --- a/install.sh +++ b/install.sh @@ -137,7 +137,7 @@ fi Path=${1:-rustlings/} echo "Cloning Rustlings at $Path..." -git clone -q https://github.com/rust-lang/rustlings "$Path" +git clone -q https://github.com/rust-lang/rustlings.git "$Path" cd "$Path" From 75ee0e42450713cc8c8288c71a0f148cdefea526 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 12 Feb 2024 18:13:20 +0100 Subject: [PATCH 0471/1432] Clarified hint text --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 887662ab80..aa4ed05222 100644 --- a/info.toml +++ b/info.toml @@ -145,7 +145,7 @@ after the `->`. This is where the function's return type should be -- take a look at the `is_even` function for an example! Also: Did you figure out that, technically, `u32` would be the more fitting type -for the prices here, since they can't be negative? If so, kudos!""" +for the inputs of the functions here, since the original prices shouldn't be negative? If so, kudos!""" [[exercises]] name = "functions5" From 1da82a0eabdae611519d0c58aff34eb9c792dc90 Mon Sep 17 00:00:00 2001 From: Guizoul Date: Wed, 28 Feb 2024 14:19:05 +0100 Subject: [PATCH 0472/1432] docs: Added comment for handling equal numbers in if/if1.rs `bigger` function --- exercises/03_if/if1.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs index 4734d78fdd..38d283c21a 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -6,6 +6,7 @@ pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! + // If both numbers are equal, any of them is returned. // Do not use: // - another function call // - additional variables From 19b5e24d5caf551aef5fd749ed7437d3044bf6ce Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 4 Mar 2024 10:38:09 -0500 Subject: [PATCH 0473/1432] Update hashmaps3.rs description for clarity I struggled with this exercise and didn't understand that it was looking for a summary of goals scored/conceded per team, instead of per match. My goal here is just to clarify the language, essentially saying "the total number of goals the team scored" to indicate that we are looking for a sum. Updated the exercise description to clarify this point. Relates loosely to closed issue https://github.com/rust-lang/rustlings/issues/1361 --- exercises/11_hashmaps/hashmaps3.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 36544ee3e9..8d9236df96 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -4,10 +4,11 @@ // the form : ",,," // Example: England,France,4,2 (England scored 4 goals, France 2). // -// You have to build a scores table containing the name of the team, goals the -// team scored, and 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. +// 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, +// complete it to pass the test. // // Make me pass the tests! // From 373676a57601374a7c7d8b2aa447ecf249a1caa1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 22:02:08 +0000 Subject: [PATCH 0474/1432] chore(deps): bump mio from 0.8.9 to 0.8.11 Bumps [mio](https://github.com/tokio-rs/mio) from 0.8.9 to 0.8.11. - [Release notes](https://github.com/tokio-rs/mio/releases) - [Changelog](https://github.com/tokio-rs/mio/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/mio/compare/v0.8.9...v0.8.11) --- updated-dependencies: - dependency-name: mio dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93489eb047..5858d3abe5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,9 +390,9 @@ checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", From 547f3ac835bb6cf0db654c528d31bce361d01381 Mon Sep 17 00:00:00 2001 From: luna <26529488+hyphena@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:31:33 -0500 Subject: [PATCH 0475/1432] chore: minor typo fix --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 887662ab80..39eb33edce 100644 --- a/info.toml +++ b/info.toml @@ -248,7 +248,7 @@ starting and ending (plus one) indices of the items in the `Array` that you want to end up in the slice. If you're curious why the first argument of `assert_eq!` does not have an -ampersand for a reference since the second argument is areference, take a look +ampersand for a reference since the second argument is a reference, take a look at the coercion chapter of the nomicon: https://doc.rust-lang.org/nomicon/coercions.html""" From 11f0fd7fd9673996d8d2381ade1a371f945f1875 Mon Sep 17 00:00:00 2001 From: Kyle VanderBeek Date: Sat, 9 Mar 2024 19:45:50 +0000 Subject: [PATCH 0476/1432] Convert to lightweight dev container; simplify. Instead of running `rustup` on a multi-gigabyte general-purpose Linux base, use the premade devcontainers/rust:1 which closely tracks the rust toolchain releases. Rip out excess setup steps since devcontainers come with the repo checked out; just compile/update the binary. --- .devcontainer/devcontainer.json | 8 +------- .devcontainer/setup.sh | 7 ------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100755 .devcontainer/setup.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e1b2cec10b..59f9571aad 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,12 +1,6 @@ { - "image": "mcr.microsoft.com/devcontainers/universal:2-linux", - "waitFor": "onCreateCommand", - "onCreateCommand": ".devcontainer/setup.sh", + "image": "mcr.microsoft.com/devcontainers/rust:1", "updateContentCommand": "cargo build", - "postCreateCommand": "", - "postAttachCommand": { - "server": "rustlings watch" - }, "customizations": { "vscode": { "extensions": [ diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh deleted file mode 100755 index 0e090a8678..0000000000 --- a/.devcontainer/setup.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -curl https://sh.rustup.rs -sSf | sh -s -- -y - -# Update current shell environment variables after install to find rustup -. "$HOME/.cargo/env" -rustup install stable -bash install.sh From e424e9f6c78ec54c5a91b942398f7012f46f6c90 Mon Sep 17 00:00:00 2001 From: Kyle VanderBeek Date: Sat, 9 Mar 2024 20:07:51 +0000 Subject: [PATCH 0477/1432] Add target directory to $PATH Makes the pre-built command work in the shell right away. --- .devcontainer/devcontainer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 59f9571aad..643021aaa8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,9 @@ { "image": "mcr.microsoft.com/devcontainers/rust:1", "updateContentCommand": "cargo build", + "remoteEnv": { + "PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/target/debug" + }, "customizations": { "vscode": { "extensions": [ From 77903200a0c19ef4ad2de7b2abec06e301d0dccc Mon Sep 17 00:00:00 2001 From: Kyle VanderBeek Date: Sat, 9 Mar 2024 20:20:14 +0000 Subject: [PATCH 0478/1432] Remove duplicate vscode extension list. It's already in the vendor-specific .vscode files. --- .devcontainer/devcontainer.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 643021aaa8..a7f4f78f9c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,12 +3,5 @@ "updateContentCommand": "cargo build", "remoteEnv": { "PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/target/debug" - }, - "customizations": { - "vscode": { - "extensions": [ - "rust-lang.rust-analyzer" - ] - } } } From 2fb135026cd3b072a54fdd211405090afca6f75e Mon Sep 17 00:00:00 2001 From: Kyle VanderBeek Date: Sat, 9 Mar 2024 23:18:31 +0000 Subject: [PATCH 0479/1432] Add back the post-attach watch. --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a7f4f78f9c..f25e8bd8d6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,7 @@ { "image": "mcr.microsoft.com/devcontainers/rust:1", - "updateContentCommand": "cargo build", + "updateContentCommand": ["cargo", "build"], + "postAttachCommand": ["rustlings", "watch"], "remoteEnv": { "PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/target/debug" } From 36db08340d4503ac363e9f6499fc534a479b7491 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 10 Mar 2024 23:57:35 +0100 Subject: [PATCH 0480/1432] Update dependencies --- Cargo.lock | 316 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 22 ++-- 2 files changed, 165 insertions(+), 173 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93489eb047..3950c476c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -27,43 +27,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "assert_cmd" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" dependencies = [ "anstyle", "bstr", @@ -88,15 +88,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bstr" -version = "1.6.2" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "regex-automata", @@ -111,9 +111,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.3" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" +checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" dependencies = [ "clap_builder", "clap_derive", @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.2" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -145,9 +145,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -157,35 +157,31 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "console" -version = "0.15.5" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", + "windows-sys 0.52.0", ] [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "difflib" @@ -199,12 +195,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - [[package]] name = "encode_unicode" version = "0.3.6" @@ -219,14 +209,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -255,9 +245,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -267,18 +257,18 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "home" -version = "0.5.4" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown", @@ -286,9 +276,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.6" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -326,20 +316,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "kqueue" @@ -369,30 +350,27 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.6.3" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -412,7 +390,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "crossbeam-channel", "filetime", "fsevent-sys", @@ -438,9 +416,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -453,20 +431,19 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "portable-atomic" -version = "1.4.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "predicates" -version = "3.0.3" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", "float-cmp", - "itertools", "normalize-line-endings", "predicates-core", "regex", @@ -490,53 +467,59 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.7.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustlings" @@ -558,9 +541,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -573,18 +556,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.158" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -593,9 +576,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -604,24 +587,24 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "2.0.8" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -636,9 +619,9 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "toml" -version = "0.7.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", @@ -648,18 +631,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ "indexmap", "serde", @@ -670,15 +653,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "utf8parse" @@ -697,9 +680,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -729,9 +712,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -744,26 +727,20 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.48.5", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.4", ] [[package]] @@ -782,10 +759,19 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" +name = "windows-targets" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] [[package]] name = "windows_aarch64_gnullvm" @@ -794,10 +780,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +name = "windows_aarch64_gnullvm" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -806,10 +792,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] -name = "windows_i686_gnu" -version = "0.42.2" +name = "windows_aarch64_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -818,10 +804,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] -name = "windows_i686_msvc" -version = "0.42.2" +name = "windows_i686_gnu" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -830,10 +816,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +name = "windows_i686_msvc" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -842,10 +828,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +name = "windows_x86_64_gnu" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -854,10 +840,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" +name = "windows_x86_64_gnullvm" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -865,11 +851,17 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + [[package]] name = "winnow" -version = "0.5.15" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 24bee7cf7f..218b79901b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,22 +9,22 @@ authors = [ edition = "2021" [dependencies] -indicatif = "0.17.6" -console = "0.15" -notify-debouncer-mini = "0.4.1" -toml = "0.7.6" -regex = "1.5" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.81" -home = "0.5.3" +clap = { version = "4.5.2", features = ["derive"] } +console = "0.15.8" glob = "0.3.0" -clap = { version = "4.4.0", features = ["derive"] } +home = "0.5.9" +indicatif = "0.17.8" +notify-debouncer-mini = "0.4.1" +regex = "1.10.3" +serde_json = "1.0.114" +serde = { version = "1.0.197", features = ["derive"] } +toml = "0.8.10" [[bin]] name = "rustlings" path = "src/main.rs" [dev-dependencies] -assert_cmd = "2.0.12" -predicates = "3.0.3" +assert_cmd = "2.0.14" glob = "0.3.0" +predicates = "3.1.0" From f5e9db90ccb5b84fbcb0ccff8aaade50588d534c Mon Sep 17 00:00:00 2001 From: pavedroad Date: Tue, 12 Mar 2024 14:35:48 +0800 Subject: [PATCH 0481/1432] chore: fix typo Signed-off-by: pavedroad --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7226a47af..a199e4decd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -167,7 +167,7 @@ - **structs3**: Clarifed the hint - **quiz2, as_ref_mut, options1, traits1, traits2**: Clarified hints - **traits1, traits2, cli**: Tidied up unmatching backticks -- **enums2**: Removed unneccessary indirection of self +- **enums2**: Removed unnecessary indirection of self - **enums3**: Added an extra tuple comment #### Housekeeping From 098ff228d73d9f359e948712acb346240e85af05 Mon Sep 17 00:00:00 2001 From: Ahmed <111569638+0Ahmed-0@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:35:53 +0200 Subject: [PATCH 0482/1432] chore: fix a minor typo --- exercises/09_strings/strings3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index b29f9325bc..384e7ce3ea 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -11,7 +11,7 @@ fn trim_me(input: &str) -> String { } fn compose_me(input: &str) -> String { - // TODO: Add " world!" to the string! There's multiple ways to do this! + // TODO: Add " world!" to the string! There are multiple ways to do this! ??? } From c46a7115265406330dc95fc7ab8e500cd8d24859 Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 15 Mar 2024 13:48:57 +0100 Subject: [PATCH 0483/1432] fix: revert from_into test change --- exercises/23_conversions/from_into.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 60911f3e21..da1a9a5f8a 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -43,8 +43,7 @@ impl Default for Person { // I AM NOT DONE impl From<&str> for Person { - fn from(s: &str) -> Person { - } + fn from(s: &str) -> Person {} } fn main() { @@ -127,14 +126,14 @@ mod tests { #[test] fn test_trailing_comma() { let p: Person = Person::from("Mike,32,"); - assert_eq!(p.name, "Mike"); - assert_eq!(p.age, 32); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); } #[test] fn test_trailing_comma_and_some_string() { - let p: Person = Person::from("Mike,32,man"); - assert_eq!(p.name, "Mike"); - assert_eq!(p.age, 32); + let p: Person = Person::from("Mike,32,dog"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); } } From 17ee0e3c7a47054baf5e66c5525541e4153c03b7 Mon Sep 17 00:00:00 2001 From: Luca Plian <98339220+AnonimAnonim2245@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:51:24 +0200 Subject: [PATCH 0484/1432] optimized the UI code (#1830) --- src/ui.rs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index 1ee463168d..74835e136c 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,33 +1,28 @@ -macro_rules! warn { - ($fmt:literal, $ex:expr) => {{ +macro_rules! print_emoji { + ($emoji:expr, $sign:expr, $color: ident ,$fmt:literal, $ex:expr) => {{ use console::{style, Emoji}; use std::env; let formatstr = format!($fmt, $ex); if env::var("NO_EMOJI").is_ok() { - println!("{} {}", style("!").red(), style(formatstr).red()); + println!("{} {}", style($sign).$color(), style(formatstr).$color()); } else { println!( "{} {}", - style(Emoji("⚠️ ", "!")).red(), - style(formatstr).red() + style(Emoji($emoji, $sign)).$color(), + style(formatstr).$color() ); } }}; } +macro_rules! warn { + ($fmt:literal, $ex:expr) => {{ + print_emoji!("⚠️ ", "!", red, $fmt, $ex); + }}; +} + macro_rules! success { ($fmt:literal, $ex:expr) => {{ - use console::{style, Emoji}; - use std::env; - let formatstr = format!($fmt, $ex); - if env::var("NO_EMOJI").is_ok() { - println!("{} {}", style("βœ“").green(), style(formatstr).green()); - } else { - println!( - "{} {}", - style(Emoji("βœ…", "βœ“")).green(), - style(formatstr).green() - ); - } + print_emoji!("βœ… ", "βœ“", green, $fmt, $ex); }}; } From 2b97faa1d391ab473b542dd02e70cca27356a04a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:51:43 +0000 Subject: [PATCH 0485/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 5466c9cfbc..ca834ad696 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -362,6 +362,7 @@ authors. gerases
gerases

πŸ–‹ + Luca Plian
Luca Plian

πŸ’» From 3c6c29e19e2f46434fb8c9c0df3d1950053213c1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:51:44 +0000 Subject: [PATCH 0486/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index cc26876534..c16d2c5ee2 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2559,6 +2559,15 @@ "contributions": [ "content" ] + }, + { + "login": "AnonimAnonim2245", + "name": "Luca Plian", + "avatar_url": "https://avatars.githubusercontent.com/u/98339220?v=4", + "profile": "https://github.com/AnonimAnonim2245", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 557bbe2b85235da28774fb136ed505f1fd4ddf6d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:52:53 +0000 Subject: [PATCH 0487/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index ca834ad696..4685d25618 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -363,6 +363,7 @@ authors. gerases
gerases

πŸ–‹ Luca Plian
Luca Plian

πŸ’» + RenΓ© Reifenrath
RenΓ© Reifenrath

πŸ–‹ From 6f88dd437e44e775d0fbb1b976eb032548d1ecfc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:52:54 +0000 Subject: [PATCH 0488/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index c16d2c5ee2..8a248ecbc0 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2568,6 +2568,15 @@ "contributions": [ "code" ] + }, + { + "login": "reifenrath-dev", + "name": "RenΓ© Reifenrath", + "avatar_url": "https://avatars.githubusercontent.com/u/18126097?v=4", + "profile": "https://reifenrath.dev/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From fe698d90967a57729560322e39bf3e95c2e97ac5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:56:52 +0000 Subject: [PATCH 0489/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4685d25618..ba08a15b4f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -364,6 +364,7 @@ authors. gerases
gerases

πŸ–‹ Luca Plian
Luca Plian

πŸ’» RenΓ© Reifenrath
RenΓ© Reifenrath

πŸ–‹ + Peter Neave
Peter Neave

πŸš‡ From 0d5c105c15fe85900ee39f4aa8cfe1ffc3d5d63c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:56:53 +0000 Subject: [PATCH 0490/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 8a248ecbc0..9c35721d42 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2577,6 +2577,15 @@ "contributions": [ "content" ] + }, + { + "login": "peterneave", + "name": "Peter Neave", + "avatar_url": "https://avatars.githubusercontent.com/u/7982708?v=4", + "profile": "https://github.com/peterneave", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From 032e3c9f3017f392e627dd99daae83b5f0fa09ff Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:02:09 +0000 Subject: [PATCH 0491/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index ba08a15b4f..ae29b64da3 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -365,6 +365,7 @@ authors. Luca Plian
Luca Plian

πŸ’» RenΓ© Reifenrath
RenΓ© Reifenrath

πŸ–‹ Peter Neave
Peter Neave

πŸš‡ + Jan
Jan

πŸ–‹ From 4c51cb09092717855486286ddc5462fbd0a45b29 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:02:10 +0000 Subject: [PATCH 0492/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9c35721d42..29eea97061 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2586,6 +2586,15 @@ "contributions": [ "infra" ] + }, + { + "login": "JanB1", + "name": "Jan", + "avatar_url": "https://avatars.githubusercontent.com/u/5552248?v=4", + "profile": "http://www.janb1.com", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From fe28feba1e164267a16210fd5dd80b701fa38c1d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:17:17 +0000 Subject: [PATCH 0493/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index ae29b64da3..cf66d7f629 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -366,6 +366,7 @@ authors. RenΓ© Reifenrath
RenΓ© Reifenrath

πŸ–‹ Peter Neave
Peter Neave

πŸš‡ Jan
Jan

πŸ–‹ + Kyle VanderBeek
Kyle VanderBeek

πŸš‡ From 62b435a309c154a610c428cf57825b287a9c26e9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:17:18 +0000 Subject: [PATCH 0494/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 29eea97061..80ca245ac1 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2595,6 +2595,15 @@ "contributions": [ "content" ] + }, + { + "login": "kylev", + "name": "Kyle VanderBeek", + "avatar_url": "https://avatars.githubusercontent.com/u/46888?v=4", + "profile": "http://www.kylev.com/", + "contributions": [ + "infra" + ] } ], "contributorsPerLine": 8, From 3f0dc81a9c065dd44f41ce1a462e2998173f3eb0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:19:33 +0000 Subject: [PATCH 0495/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index cf66d7f629..e150ffe06c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -367,6 +367,7 @@ authors. Peter Neave
Peter Neave

πŸš‡ Jan
Jan

πŸ–‹ Kyle VanderBeek
Kyle VanderBeek

πŸš‡ + pavedroad
pavedroad

πŸ–‹ From 1ac392a551201c073bb22a76075e987218b583a6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:19:34 +0000 Subject: [PATCH 0496/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 80ca245ac1..f2d04b2b39 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2604,6 +2604,15 @@ "contributions": [ "infra" ] + }, + { + "login": "pavedroad", + "name": "pavedroad", + "avatar_url": "https://avatars.githubusercontent.com/u/138004431?v=4", + "profile": "https://github.com/pavedroad", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 3d6b5e81020729e3c684f767d2ca6e7789c66cf3 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:22:13 +0000 Subject: [PATCH 0497/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index e150ffe06c..854b6b6aa0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -368,6 +368,7 @@ authors. Jan
Jan

πŸ–‹ Kyle VanderBeek
Kyle VanderBeek

πŸš‡ pavedroad
pavedroad

πŸ–‹ + luna
luna

πŸ–‹ From 9cf5a2f83f44a0f43b66810d2dfcc71d4d53e712 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:22:14 +0000 Subject: [PATCH 0498/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f2d04b2b39..c5424b3c82 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2613,6 +2613,15 @@ "contributions": [ "content" ] + }, + { + "login": "hyphena", + "name": "luna", + "avatar_url": "https://avatars.githubusercontent.com/u/26529488?v=4", + "profile": "https://github.com/hyphena", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 4304b3981e353b1fe55eea1f07f3b2ec172fdcb8 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:32:30 +0000 Subject: [PATCH 0499/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 854b6b6aa0..aabfb9dc14 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -370,6 +370,9 @@ authors. pavedroad
pavedroad

πŸ–‹ luna
luna

πŸ–‹ + + Evan Miller
Evan Miller

πŸ–‹ + From 8d1258f26abed7bc8c222974b5430e51fc8628e5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:32:31 +0000 Subject: [PATCH 0500/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index c5424b3c82..431c3cd58a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2622,6 +2622,15 @@ "contributions": [ "content" ] + }, + { + "login": "evanmiller2112", + "name": "Evan Miller", + "avatar_url": "https://avatars.githubusercontent.com/u/28488957?v=4", + "profile": "https://github.com/evanmiller2112", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From f8f627b6f76bfae3fb23cb0c06aecfa644a27d87 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:35:16 +0000 Subject: [PATCH 0501/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index aabfb9dc14..39a3b6bf53 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -372,6 +372,7 @@ authors. Evan Miller
Evan Miller

πŸ–‹ + luvchurchill
luvchurchill

πŸ’» From c01ddbc7477dc062dd7fac06ba5cb9e5eeaa15e9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:35:17 +0000 Subject: [PATCH 0502/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 431c3cd58a..7d26d4cba5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2631,6 +2631,15 @@ "contributions": [ "content" ] + }, + { + "login": "luvchurchill", + "name": "luvchurchill", + "avatar_url": "https://avatars.githubusercontent.com/u/46406654?v=4", + "profile": "https://github.com/luvchurchill", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From b7add6a1a81c77c09971cc2330fb9d016c7c8cac Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:37:01 +0000 Subject: [PATCH 0503/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 39a3b6bf53..bf97ce7fc3 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -373,6 +373,7 @@ authors. Evan Miller
Evan Miller

πŸ–‹ luvchurchill
luvchurchill

πŸ’» + Ze-en Xiong
Ze-en Xiong

πŸ–‹ From b7b74910d8fe8aed81df5fe120bc2f4c272877de Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:37:02 +0000 Subject: [PATCH 0504/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7d26d4cba5..b093b480c0 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2640,6 +2640,15 @@ "contributions": [ "code" ] + }, + { + "login": "LeverImmy", + "name": "Ze-en Xiong", + "avatar_url": "https://avatars.githubusercontent.com/u/47663913?v=4", + "profile": "https://leverimmy.top/", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 1c3b129a532d7d99bf81a232ae76c49892c6ff82 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:39:18 +0000 Subject: [PATCH 0505/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index bf97ce7fc3..a037a647b0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -374,6 +374,7 @@ authors. Evan Miller
Evan Miller

πŸ–‹ luvchurchill
luvchurchill

πŸ’» Ze-en Xiong
Ze-en Xiong

πŸ–‹ + Parnav Harinathan
Parnav Harinathan

πŸ–‹ From 1e69e6799778ce54d64227a05dd60e4e89753bbd Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:39:19 +0000 Subject: [PATCH 0506/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b093b480c0..60f034ef72 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2649,6 +2649,15 @@ "contributions": [ "content" ] + }, + { + "login": "parnavh", + "name": "Parnav Harinathan", + "avatar_url": "https://avatars.githubusercontent.com/u/45985534?v=4", + "profile": "https://github.com/parnavh", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 0d37f9011c818112c2c9ff1792c3b8a55a6c3160 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:40:36 +0000 Subject: [PATCH 0507/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index a037a647b0..a74e77f547 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -375,6 +375,7 @@ authors. luvchurchill
luvchurchill

πŸ’» Ze-en Xiong
Ze-en Xiong

πŸ–‹ Parnav Harinathan
Parnav Harinathan

πŸ–‹ + 0Ahmed-0
0Ahmed-0

πŸ–‹ From d53ae18918a0e2f65f981938326cc8c03a862595 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:40:37 +0000 Subject: [PATCH 0508/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 60f034ef72..0328a51cfb 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2658,6 +2658,15 @@ "contributions": [ "content" ] + }, + { + "login": "0Ahmed-0", + "name": "0Ahmed-0", + "avatar_url": "https://avatars.githubusercontent.com/u/111569638?v=4", + "profile": "https://github.com/0Ahmed-0", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From d8ecf4bc2d34a10149999a7974d5ba71625fab90 Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 15 Mar 2024 15:01:32 +0100 Subject: [PATCH 0509/1432] fix: clean up "return" wording in iterators4 --- exercises/18_iterators/iterators4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 79e1692bac..3c0724e9b8 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -8,7 +8,7 @@ pub fn factorial(num: u64) -> u64 { // Complete this function to return the factorial of num // Do not use: - // - return + // - early returns (using the `return` keyword explicitly) // Try not to use: // - imperative style loops (for, while) // - additional variables From ae69f423cd3a3cb795e0864316dfaf6198fd511c Mon Sep 17 00:00:00 2001 From: guizo792 <95940388+guizo792@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:36:28 +0000 Subject: [PATCH 0510/1432] Update exercises/03_if/if1.rs Co-authored-by: liv --- exercises/03_if/if1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs index 38d283c21a..d2afccf841 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -6,7 +6,7 @@ pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! - // If both numbers are equal, any of them is returned. + // If both numbers are equal, any of them can be returned. // Do not use: // - another function call // - additional variables From 60f44cc5097471d76e54761e36a7bddfdbcfb4fd Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:56:54 +0000 Subject: [PATCH 0511/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index a74e77f547..5515745fe0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -376,6 +376,7 @@ authors. Ze-en Xiong
Ze-en Xiong

πŸ–‹ Parnav Harinathan
Parnav Harinathan

πŸ–‹ 0Ahmed-0
0Ahmed-0

πŸ–‹ + guizo792
guizo792

πŸ–‹ From d4233f816674a98bc603bb551d6729eb92f5e75e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:56:55 +0000 Subject: [PATCH 0512/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0328a51cfb..2f6cd73235 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2667,6 +2667,15 @@ "contributions": [ "content" ] + }, + { + "login": "guizo792", + "name": "guizo792", + "avatar_url": "https://avatars.githubusercontent.com/u/95940388?v=4", + "profile": "https://github.com/guizo792", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From 80388c042bd1c0048fccf1cf739486642969b8a7 Mon Sep 17 00:00:00 2001 From: Kazuki Matsuo Date: Sat, 16 Mar 2024 13:56:34 +0900 Subject: [PATCH 0513/1432] fix(verify): show stdout of the last line --- src/verify.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/verify.rs b/src/verify.rs index 8a2ad49fba..aee2afa385 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -38,6 +38,15 @@ pub fn verify<'a>( percentage += 100.0 / total as f32; bar.inc(1); bar.set_message(format!("({:.1} %)", percentage)); + if bar.position() == total as u64 { + println!( + "Progress: You completed {} / {} exercises ({:.1} %).", + bar.position(), + total, + percentage + ); + bar.finish(); + } } Ok(()) } From a07172a069ea0a70e70256bf981d33c5b03acfb0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 17 Mar 2024 12:19:36 +0000 Subject: [PATCH 0514/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 5515745fe0..00122e07ce 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -377,6 +377,7 @@ authors. Parnav Harinathan
Parnav Harinathan

πŸ–‹ 0Ahmed-0
0Ahmed-0

πŸ–‹ guizo792
guizo792

πŸ–‹ + Kazuki Matsuo
Kazuki Matsuo

πŸ’» From e1fa6cf30bfa82ce3cf3882a146f9fd7c434692d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 17 Mar 2024 12:19:37 +0000 Subject: [PATCH 0515/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2f6cd73235..f2446f1f76 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2676,6 +2676,15 @@ "contributions": [ "content" ] + }, + { + "login": "kazu728", + "name": "Kazuki Matsuo", + "avatar_url": "https://avatars.githubusercontent.com/u/34614358?v=4", + "profile": "https://github.com/kazu728", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 71700c506c34af636fc9d4098b5bebc21dfe9a3c Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 18 Mar 2024 01:12:37 +0100 Subject: [PATCH 0516/1432] Remove unneeded Arc --- exercises/20_threads/threads3.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 91006bbcab..acb97b4bb1 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -27,22 +27,18 @@ impl Queue { } fn send_tx(q: Queue, tx: mpsc::Sender) -> () { - let qc = Arc::new(q); - let qc1 = Arc::clone(&qc); - let qc2 = Arc::clone(&qc); - thread::spawn(move || { - for val in &qc1.first_half { + for val in q.first_half { println!("sending {:?}", val); - tx.send(*val).unwrap(); + tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); thread::spawn(move || { - for val in &qc2.second_half { + for val in q.second_half { println!("sending {:?}", val); - tx.send(*val).unwrap(); + tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); From 1fe32a7ff2250ae893fdd54b394e6521d32dd024 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 18 Mar 2024 01:44:25 +0100 Subject: [PATCH 0517/1432] Fix the sysroot path when it contains whitespaces --- src/project.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/project.rs b/src/project.rs index bcbd7ada84..00fc304dc0 100644 --- a/src/project.rs +++ b/src/project.rs @@ -2,7 +2,7 @@ use glob::glob; use serde::{Deserialize, Serialize}; use std::env; use std::error::Error; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; /// Contains the structure of resulting rust-project.json file @@ -79,21 +79,24 @@ impl RustAnalyzerProject { .output()? .stdout; - let toolchain = String::from_utf8_lossy(&toolchain); - let mut whitespace_iter = toolchain.split_whitespace(); + let toolchain = String::from_utf8(toolchain)?; + let toolchain = toolchain.trim_end(); - let toolchain = whitespace_iter.next().unwrap_or(&toolchain); + println!("Determined toolchain: {toolchain}\n"); - println!("Determined toolchain: {}\n", &toolchain); - - self.sysroot_src = (std::path::Path::new(toolchain) + let Ok(path) = Path::new(toolchain) .join("lib") .join("rustlib") .join("src") .join("rust") .join("library") - .to_string_lossy()) - .to_string(); + .into_os_string() + .into_string() + else { + return Err("The sysroot path is invalid UTF8".into()); + }; + self.sysroot_src = path; + Ok(()) } } From f2833c5279a139ee5ab747b3dc573946a524349f Mon Sep 17 00:00:00 2001 From: Dan Bond Date: Mon, 18 Mar 2024 16:47:15 -0700 Subject: [PATCH 0518/1432] options1: Update wording & fix grammar Signed-off-by: Dan Bond --- exercises/12_options/options1.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index e131b48b99..3cbfecd66f 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -6,11 +6,11 @@ // I AM NOT DONE // This function returns how much icecream there is left in the fridge. -// If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them +// If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it // all, so there'll be no more left :( fn maybe_icecream(time_of_day: u16) -> Option { // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a - // value of 0 The Option output should gracefully handle cases where + // value of 0. The Option output should gracefully handle cases where // time_of_day > 23. // TODO: Complete the function body - remember to return an Option! ??? @@ -22,10 +22,11 @@ mod tests { #[test] fn check_icecream() { + assert_eq!(maybe_icecream(0), Some(5)); assert_eq!(maybe_icecream(9), Some(5)); - assert_eq!(maybe_icecream(10), Some(5)); - assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(18), Some(5)); assert_eq!(maybe_icecream(22), Some(0)); + assert_eq!(maybe_icecream(23), Some(0)); assert_eq!(maybe_icecream(25), None); } From eb952a480d2dabcafa8b55e1a89872c9b5e4194b Mon Sep 17 00:00:00 2001 From: Dan Bond Date: Mon, 18 Mar 2024 16:47:54 -0700 Subject: [PATCH 0519/1432] verify: fix success message spacing Signed-off-by: Dan Bond --- src/verify.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verify.rs b/src/verify.rs index aee2afa385..cafecab0dc 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -195,7 +195,7 @@ fn prompt_for_completion( if no_emoji { println!("~*~ {success_msg} ~*~") } else { - println!("πŸŽ‰ πŸŽ‰ {success_msg} πŸŽ‰ πŸŽ‰") + println!("πŸŽ‰ πŸŽ‰ {success_msg} πŸŽ‰ πŸŽ‰") } println!(); From e276c1219279e14b267a36e99fb3908427bc67ff Mon Sep 17 00:00:00 2001 From: honeywest Date: Thu, 21 Mar 2024 15:18:50 +0800 Subject: [PATCH 0520/1432] feat: ui format --- src/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui.rs b/src/ui.rs index 74835e136c..d8177b9f50 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,5 +1,5 @@ macro_rules! print_emoji { - ($emoji:expr, $sign:expr, $color: ident ,$fmt:literal, $ex:expr) => {{ + ($emoji:expr, $sign:expr, $color: ident, $fmt:literal, $ex:expr) => {{ use console::{style, Emoji}; use std::env; let formatstr = format!($fmt, $ex); From 3dce7e56961a40748f428d10c50540a075839f8d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 23 Mar 2024 18:51:25 +0100 Subject: [PATCH 0521/1432] Improvements to watch mode --- Cargo.lock | 7 +++++ Cargo.toml | 1 + src/main.rs | 85 ++++++++++++++++++++++++++++++----------------------- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3950c476c0..e86f9fa4f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,6 +536,7 @@ dependencies = [ "regex", "serde", "serde_json", + "shlex", "toml", ] @@ -594,6 +595,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "strsim" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 218b79901b..2cf8bc3999 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ notify-debouncer-mini = "0.4.1" regex = "1.10.3" serde_json = "1.0.114" serde = { version = "1.0.197", features = ["derive"] } +shlex = "1.3.0" toml = "0.8.10" [[bin]] diff --git a/src/main.rs b/src/main.rs index a06f0c563c..7c469d5c90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use clap::{Parser, Subcommand}; use console::Emoji; use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; +use shlex::Shlex; use std::ffi::OsStr; use std::fs; use std::io::{self, prelude::*}; @@ -25,6 +26,16 @@ mod project; mod run; mod verify; +const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: + hint - prints the current exercise's hint + clear - clears the screen + quit - quits watch mode + ! - executes a command, like `!rustc --explain E0381` + help - displays this help message + +Watch mode automatically re-evaluates the current exercise +when you edit a file's contents."; + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -246,47 +257,49 @@ fn main() { } fn spawn_watch_shell( - failed_exercise_hint: &Arc>>, + failed_exercise_hint: Arc>>, should_quit: Arc, ) { - let failed_exercise_hint = Arc::clone(failed_exercise_hint); println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); - thread::spawn(move || loop { + + thread::spawn(move || { let mut input = String::new(); - match io::stdin().read_line(&mut input) { - Ok(_) => { - let input = input.trim(); - if input == "hint" { - if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { - println!("{hint}"); - } - } else if input == "clear" { - println!("\x1B[2J\x1B[1;1H"); - } else if input.eq("quit") { - should_quit.store(true, Ordering::SeqCst); - println!("Bye!"); - } else if input.eq("help") { - println!("Commands available to you in watch mode:"); - println!(" hint - prints the current exercise's hint"); - println!(" clear - clears the screen"); - println!(" quit - quits watch mode"); - println!(" ! - executes a command, like `!rustc --explain E0381`"); - println!(" help - displays this help message"); - println!(); - println!("Watch mode automatically re-evaluates the current exercise"); - println!("when you edit a file's contents.") - } else if let Some(cmd) = input.strip_prefix('!') { - let parts: Vec<&str> = cmd.split_whitespace().collect(); - if parts.is_empty() { - println!("no command provided"); - } else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() { - println!("failed to execute command `{}`: {}", cmd, e); - } - } else { - println!("unknown command: {input}"); + let mut stdin = io::stdin().lock(); + + loop { + // Recycle input buffer. + input.clear(); + + if let Err(e) = stdin.read_line(&mut input) { + println!("error reading command: {e}"); + } + + let input = input.trim(); + if input == "hint" { + if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { + println!("{hint}"); } + } else if input == "clear" { + println!("\x1B[2J\x1B[1;1H"); + } else if input == "quit" { + should_quit.store(true, Ordering::SeqCst); + println!("Bye!"); + } else if input == "help" { + println!("{WATCH_MODE_HELP_MESSAGE}"); + } else if let Some(cmd) = input.strip_prefix('!') { + let mut parts = Shlex::new(cmd); + + let Some(program) = parts.next() else { + println!("no command provided"); + continue; + }; + + if let Err(e) = Command::new(program).args(parts).status() { + println!("failed to execute command `{cmd}`: {e}"); + } + } else { + println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}"); } - Err(error) => println!("error reading command: {error}"), } }); } @@ -348,7 +361,7 @@ fn watch( Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))), }; - spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit)); + spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit)); loop { match rx.recv_timeout(Duration::from_secs(1)) { Ok(event) => match event { From 0d93266462f56d28501f068a764405a0cd0bf41a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 23 Mar 2024 18:56:30 +0100 Subject: [PATCH 0522/1432] Initialize the input buffer with some capacity --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 7c469d5c90..2b6a48cfbe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -263,7 +263,7 @@ fn spawn_watch_shell( println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); thread::spawn(move || { - let mut input = String::new(); + let mut input = String::with_capacity(32); let mut stdin = io::stdin().lock(); loop { From 27fa7c3e4a5bb58b21359e9d6246f66b5f20a978 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 23 Mar 2024 19:00:15 +0100 Subject: [PATCH 0523/1432] Move the const string to the bottom like others --- src/main.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2b6a48cfbe..d2614df6b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,16 +26,6 @@ mod project; mod run; mod verify; -const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: - hint - prints the current exercise's hint - clear - clears the screen - quit - quits watch mode - ! - executes a command, like `!rustc --explain E0381` - help - displays this help message - -Watch mode automatically re-evaluates the current exercise -when you edit a file's contents."; - /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -490,3 +480,13 @@ const WELCOME: &str = r" welcome to... | | | |_| \__ \ |_| | | | | | (_| \__ \ |_| \__,_|___/\__|_|_|_| |_|\__, |___/ |___/"; + +const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: + hint - prints the current exercise's hint + clear - clears the screen + quit - quits watch mode + ! - executes a command, like `!rustc --explain E0381` + help - displays this help message + +Watch mode automatically re-evaluates the current exercise +when you edit a file's contents."; From a325df55d1077c8613905bb82709cd8c80341641 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 23 Mar 2024 21:56:40 +0100 Subject: [PATCH 0524/1432] Cache filters --- src/main.rs | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index a06f0c563c..9bf58668d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,31 +128,45 @@ fn main() { println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); } let mut exercises_done: u16 = 0; - let filters = filter.clone().unwrap_or_default().to_lowercase(); - exercises.iter().for_each(|e| { - let fname = format!("{}", e.path.display()); + let lowercase_filter = filter + .as_ref() + .map(|s| s.to_lowercase()) + .unwrap_or_default(); + let filters = lowercase_filter + .split(',') + .filter_map(|f| { + let f = f.trim(); + if f.is_empty() { + None + } else { + Some(f) + } + }) + .collect::>(); + + for exercise in &exercises { + let fname = format!("{}", exercise.path.display()); let filter_cond = filters - .split(',') - .filter(|f| !f.trim().is_empty()) - .any(|f| e.name.contains(f) || fname.contains(f)); - let status = if e.looks_done() { + .iter() + .any(|f| exercise.name.contains(f) || fname.contains(f)); + let status = if exercise.looks_done() { exercises_done += 1; "Done" } else { "Pending" }; let solve_cond = { - (e.looks_done() && solved) - || (!e.looks_done() && unsolved) + (exercise.looks_done() && solved) + || (!exercise.looks_done() && unsolved) || (!solved && !unsolved) }; if solve_cond && (filter_cond || filter.is_none()) { let line = if paths { format!("{fname}\n") } else if names { - format!("{}\n", e.name) + format!("{}\n", exercise.name) } else { - format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name) + format!("{:<17}\t{fname:<46}\t{status:<7}\n", exercise.name) }; // Somehow using println! leads to the binary panicking // when its output is piped. @@ -168,7 +182,8 @@ fn main() { }); } } - }); + } + let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0; println!( "Progress: You completed {} / {} exercises ({:.1} %).", From 01b7d6334c44d55f11d7f09c45e76b2db7fef948 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 23 Mar 2024 22:08:25 +0100 Subject: [PATCH 0525/1432] Remove unneeded to_string call --- src/verify.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verify.rs b/src/verify.rs index aee2afa385..e3a8e88730 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -224,7 +224,7 @@ fn prompt_for_completion( let formatted_line = if context_line.important { format!("{}", style(context_line.line).bold()) } else { - context_line.line.to_string() + context_line.line }; println!( From 0aeaccc3a50b5b60b6005161847641bade75effa Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 24 Mar 2024 18:34:46 +0100 Subject: [PATCH 0526/1432] Optimize state --- src/exercise.rs | 114 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 38 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 664b362bc3..b112fe8a48 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,16 +1,16 @@ use regex::Regex; use serde::Deserialize; -use std::env; use std::fmt::{self, Display, Formatter}; use std::fs::{self, remove_file, File}; -use std::io::Read; +use std::io::{self, BufRead, BufReader}; use std::path::PathBuf; use std::process::{self, Command}; +use std::{array, env, mem}; const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; -const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE"; +const I_AM_DONE_REGEX: &str = r"^\s*///?\s*I\s+AM\s+NOT\s+DONE"; const CONTEXT: usize = 2; const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml"; @@ -205,51 +205,89 @@ path = "{}.rs""#, } pub fn state(&self) -> State { - let mut source_file = File::open(&self.path).unwrap_or_else(|e| { + let source_file = File::open(&self.path).unwrap_or_else(|e| { panic!( "We were unable to open the exercise file {}! {e}", self.path.display() ) }); - - let source = { - let mut s = String::new(); - source_file.read_to_string(&mut s).unwrap_or_else(|e| { - panic!( - "We were unable to read the exercise file {}! {e}", - self.path.display() - ) - }); - s + let mut source_reader = BufReader::new(source_file); + let mut read_line = |buf: &mut String| -> io::Result<_> { + let n = source_reader.read_line(buf)?; + if buf.ends_with('\n') { + buf.pop(); + if buf.ends_with('\r') { + buf.pop(); + } + } + Ok(n) }; let re = Regex::new(I_AM_DONE_REGEX).unwrap(); - - if !re.is_match(&source) { - return State::Done; + let mut matched_line_ind: usize = 0; + let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); + let mut line = String::with_capacity(256); + + loop { + match read_line(&mut line) { + Ok(0) => break, + Ok(_) => { + if re.is_match(&line) { + let mut context = Vec::with_capacity(2 * CONTEXT + 1); + for (ind, prev_line) in prev_lines + .into_iter() + .rev() + .take(matched_line_ind) + .enumerate() + { + context.push(ContextLine { + line: prev_line, + // TODO + number: matched_line_ind - CONTEXT + ind + 1, + important: false, + }); + } + + context.push(ContextLine { + line, + number: matched_line_ind + 1, + important: true, + }); + + for ind in 0..CONTEXT { + let mut next_line = String::with_capacity(256); + let Ok(n) = read_line(&mut next_line) else { + break; + }; + + if n == 0 { + break; + } + + context.push(ContextLine { + line: next_line, + number: matched_line_ind + ind + 2, + important: false, + }); + } + + return State::Pending(context); + } + + matched_line_ind += 1; + for prev_line in &mut prev_lines { + mem::swap(&mut line, prev_line); + } + line.clear(); + } + Err(e) => panic!( + "We were unable to read the exercise file {}! {e}", + self.path.display() + ), + } } - let matched_line_index = source - .lines() - .enumerate() - .find_map(|(i, line)| if re.is_match(line) { Some(i) } else { None }) - .expect("This should not happen at all"); - - let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize; - let max_line = matched_line_index + CONTEXT; - - let context = source - .lines() - .enumerate() - .filter(|&(i, _)| i >= min_line && i <= max_line) - .map(|(i, line)| ContextLine { - line: line.to_string(), - number: i + 1, - important: i == matched_line_index, - }) - .collect(); - - State::Pending(context) + State::Done } // Check that the exercise looks to be solved using self.state() From e1375ef4319641749611124ae495346d32e04e2d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 24 Mar 2024 18:47:27 +0100 Subject: [PATCH 0527/1432] Use to_string_lossy --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 9bf58668d2..067c810c8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -145,7 +145,7 @@ fn main() { .collect::>(); for exercise in &exercises { - let fname = format!("{}", exercise.path.display()); + let fname = exercise.path.to_string_lossy(); let filter_cond = filters .iter() .any(|f| exercise.name.contains(f) || fname.contains(f)); From f205ee3d4c6f259c82e4f1226acc6a5ae5e70031 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 24 Mar 2024 18:50:46 +0100 Subject: [PATCH 0528/1432] Call looks_done only once --- src/main.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 067c810c8f..f646fdca03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -149,17 +149,15 @@ fn main() { let filter_cond = filters .iter() .any(|f| exercise.name.contains(f) || fname.contains(f)); - let status = if exercise.looks_done() { + let looks_done = exercise.looks_done(); + let status = if looks_done { exercises_done += 1; "Done" } else { "Pending" }; - let solve_cond = { - (exercise.looks_done() && solved) - || (!exercise.looks_done() && unsolved) - || (!solved && !unsolved) - }; + let solve_cond = + (looks_done && solved) || (!looks_done && unsolved) || (!solved && !unsolved); if solve_cond && (filter_cond || filter.is_none()) { let line = if paths { format!("{fname}\n") From c0c112985b531bbcf503a2b1a8c2764030a16c99 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 24 Mar 2024 19:18:19 +0100 Subject: [PATCH 0529/1432] Replace regex with winnow --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/exercise.rs | 44 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3950c476c0..e42b8f40cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -533,10 +533,10 @@ dependencies = [ "indicatif", "notify-debouncer-mini", "predicates", - "regex", "serde", "serde_json", "toml", + "winnow", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 218b79901b..dd4c0c3000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,10 @@ glob = "0.3.0" home = "0.5.9" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" -regex = "1.10.3" serde_json = "1.0.114" serde = { version = "1.0.197", features = ["derive"] } toml = "0.8.10" +winnow = "0.6.5" [[bin]] name = "rustlings" diff --git a/src/exercise.rs b/src/exercise.rs index b112fe8a48..8f580d300f 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,4 +1,3 @@ -use regex::Regex; use serde::Deserialize; use std::fmt::{self, Display, Formatter}; use std::fs::{self, remove_file, File}; @@ -6,14 +5,34 @@ use std::io::{self, BufRead, BufReader}; use std::path::PathBuf; use std::process::{self, Command}; use std::{array, env, mem}; +use winnow::ascii::{space0, space1}; +use winnow::combinator::opt; +use winnow::Parser; const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; -const I_AM_DONE_REGEX: &str = r"^\s*///?\s*I\s+AM\s+NOT\s+DONE"; const CONTEXT: usize = 2; const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml"; +fn not_done(input: &str) -> bool { + ( + space0::<_, ()>, + "//", + opt('/'), + space0, + 'I', + space1, + "AM", + space1, + "NOT", + space1, + "DONE", + ) + .parse_next(&mut &*input) + .is_ok() +} + // Get a temporary file name that is hopefully unique #[inline] fn temp_file() -> String { @@ -223,7 +242,6 @@ path = "{}.rs""#, Ok(n) }; - let re = Regex::new(I_AM_DONE_REGEX).unwrap(); let mut matched_line_ind: usize = 0; let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); let mut line = String::with_capacity(256); @@ -232,7 +250,7 @@ path = "{}.rs""#, match read_line(&mut line) { Ok(0) => break, Ok(_) => { - if re.is_match(&line) { + if not_done(&line) { let mut context = Vec::with_capacity(2 * CONTEXT + 1); for (ind, prev_line) in prev_lines .into_iter() @@ -413,4 +431,22 @@ mod test { let out = exercise.compile().unwrap().run().unwrap(); assert!(out.stdout.contains("THIS TEST TOO SHALL PASS")); } + + #[test] + fn test_not_done() { + assert!(not_done("// I AM NOT DONE")); + assert!(not_done("/// I AM NOT DONE")); + assert!(not_done("// I AM NOT DONE")); + assert!(not_done("/// I AM NOT DONE")); + assert!(not_done("// I AM NOT DONE")); + assert!(not_done("// I AM NOT DONE")); + assert!(not_done("// I AM NOT DONE")); + assert!(not_done("// I AM NOT DONE ")); + assert!(not_done("// I AM NOT DONE!")); + + assert!(!not_done("I AM NOT DONE")); + assert!(!not_done("// NOT DONE")); + assert!(!not_done("DONE")); + assert!(!not_done("// i am not done")); + } } From bdf826a026cfe7f89c31433cfd2b9a32cbe66d2c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 24 Mar 2024 22:22:55 +0100 Subject: [PATCH 0530/1432] Make "I AM NOT DONE" caseless --- src/exercise.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 8f580d300f..136e94399c 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -5,7 +5,7 @@ use std::io::{self, BufRead, BufReader}; use std::path::PathBuf; use std::process::{self, Command}; use std::{array, env, mem}; -use winnow::ascii::{space0, space1}; +use winnow::ascii::{space0, Caseless}; use winnow::combinator::opt; use winnow::Parser; @@ -21,13 +21,7 @@ fn not_done(input: &str) -> bool { "//", opt('/'), space0, - 'I', - space1, - "AM", - space1, - "NOT", - space1, - "DONE", + Caseless("I AM NOT DONE"), ) .parse_next(&mut &*input) .is_ok() @@ -438,15 +432,13 @@ mod test { assert!(not_done("/// I AM NOT DONE")); assert!(not_done("// I AM NOT DONE")); assert!(not_done("/// I AM NOT DONE")); - assert!(not_done("// I AM NOT DONE")); - assert!(not_done("// I AM NOT DONE")); - assert!(not_done("// I AM NOT DONE")); assert!(not_done("// I AM NOT DONE ")); assert!(not_done("// I AM NOT DONE!")); + assert!(not_done("// I am not done")); + assert!(not_done("// i am NOT done")); assert!(!not_done("I AM NOT DONE")); assert!(!not_done("// NOT DONE")); assert!(!not_done("DONE")); - assert!(!not_done("// i am not done")); } } From 51b4c240ed006a8279bd94e9b7ed5df67086c86e Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 00:30:01 +0100 Subject: [PATCH 0531/1432] Use `which` instead of running `rustc --version` --- Cargo.lock | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 16 ++-------------- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3950c476c0..1bfd301fb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,6 +195,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -207,6 +213,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "filetime" version = "0.2.23" @@ -354,6 +370,12 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "log" version = "0.4.21" @@ -521,6 +543,19 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustlings" version = "5.6.1" @@ -537,6 +572,7 @@ dependencies = [ "serde", "serde_json", "toml", + "which", ] [[package]] @@ -694,6 +730,18 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "which" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -865,3 +913,9 @@ checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" diff --git a/Cargo.toml b/Cargo.toml index 218b79901b..de65fc6ea8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ regex = "1.10.3" serde_json = "1.0.114" serde = { version = "1.0.197", features = ["derive"] } toml = "0.8.10" +which = "6.0.1" [[bin]] name = "rustlings" diff --git a/src/main.rs b/src/main.rs index a06f0c563c..f932631bd2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use std::ffi::OsStr; use std::fs; use std::io::{self, prelude::*}; use std::path::Path; -use std::process::{Command, Stdio}; +use std::process::Command; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::{Arc, Mutex}; @@ -100,7 +100,7 @@ fn main() { std::process::exit(1); } - if !rustc_exists() { + if which::which("rustc").is_err() { println!("We cannot find `rustc`."); println!("Try running `rustc --version` to diagnose your problem."); println!("For instructions on how to install Rust, check the README."); @@ -403,18 +403,6 @@ fn watch( } } -fn rustc_exists() -> bool { - Command::new("rustc") - .args(["--version"]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .stdin(Stdio::null()) - .spawn() - .and_then(|mut child| child.wait()) - .map(|status| status.success()) - .unwrap_or(false) -} - const DEFAULT_OUT: &str = r#"Thanks for installing Rustlings! Is this your first time? Don't worry, Rustlings was made for beginners! We are From 83cd91ccca22e36ed94e03cc622a88ef45e6da10 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 02:35:51 +0100 Subject: [PATCH 0532/1432] Replace toml with toml_edit --- Cargo.lock | 18 +++--------------- Cargo.toml | 2 +- src/main.rs | 6 ++++-- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3950c476c0..52b27259b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,7 +536,7 @@ dependencies = [ "regex", "serde", "serde_json", - "toml", + "toml_edit", ] [[package]] @@ -617,18 +617,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" -[[package]] -name = "toml" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - [[package]] name = "toml_datetime" version = "0.6.5" @@ -640,9 +628,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ "indexmap", "serde", diff --git a/Cargo.toml b/Cargo.toml index 218b79901b..28614595f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ notify-debouncer-mini = "0.4.1" regex = "1.10.3" serde_json = "1.0.114" serde = { version = "1.0.197", features = ["derive"] } -toml = "0.8.10" +toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } [[bin]] name = "rustlings" diff --git a/src/main.rs b/src/main.rs index a06f0c563c..8e0029dd08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,8 +107,10 @@ fn main() { std::process::exit(1); } - let toml_str = &fs::read_to_string("info.toml").unwrap(); - let exercises = toml::from_str::(toml_str).unwrap().exercises; + let info_file = fs::read_to_string("info.toml").unwrap(); + let exercises = toml_edit::de::from_str::(&info_file) + .unwrap() + .exercises; let verbose = args.nocapture; let command = args.command.unwrap_or_else(|| { From e4520602f52935ff310534afc65160bcc5796a97 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 02:41:45 +0100 Subject: [PATCH 0533/1432] Use the NotFound variant of the IO error --- src/main.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8e0029dd08..d6542aa2a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,15 +91,6 @@ fn main() { println!("\n{WELCOME}\n"); } - if !Path::new("info.toml").exists() { - println!( - "{} must be run from the rustlings directory", - std::env::current_exe().unwrap().to_str().unwrap() - ); - println!("Try `cd rustlings/`!"); - std::process::exit(1); - } - if !rustc_exists() { println!("We cannot find `rustc`."); println!("Try running `rustc --version` to diagnose your problem."); @@ -107,7 +98,15 @@ fn main() { std::process::exit(1); } - let info_file = fs::read_to_string("info.toml").unwrap(); + let info_file = fs::read_to_string("info.toml").unwrap_or_else(|e| { + match e.kind() { + io::ErrorKind::NotFound => println!( + "The program must be run from the rustlings directory\nTry `cd rustlings/`!", + ), + _ => println!("Failed to read the info.toml file: {e}"), + } + std::process::exit(1); + }); let exercises = toml_edit::de::from_str::(&info_file) .unwrap() .exercises; From b3aef377beacb09d8efff5a59376edc7fae7766c Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 03:33:14 +0100 Subject: [PATCH 0534/1432] Use a custom capacity for the JSON buffer --- src/project.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/project.rs b/src/project.rs index 00fc304dc0..93f941dd98 100644 --- a/src/project.rs +++ b/src/project.rs @@ -31,10 +31,12 @@ impl RustAnalyzerProject { /// Write rust-project.json to disk pub fn write_to_disk(&self) -> Result<(), std::io::Error> { - std::fs::write( - "./rust-project.json", - serde_json::to_vec(&self).expect("Failed to serialize to JSON"), - )?; + // Using the capacity 2^14 = 16384 since the file length in bytes is higher than 2^13. + // The final length is not known exactly because it depends on the user's sysroot path, + // the current number of exercises etc. + let mut buf = Vec::with_capacity(16384); + serde_json::to_writer(&mut buf, &self).expect("Failed to serialize to JSON"); + std::fs::write("rust-project.json", buf)?; Ok(()) } From efa9f5704853acda6874725004b480d720683faf Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 03:46:56 +0100 Subject: [PATCH 0535/1432] Add anyhow --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/main.rs | 5 ++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 3950c476c0..270051eee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + [[package]] name = "assert_cmd" version = "2.0.14" @@ -525,6 +531,7 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" name = "rustlings" version = "5.6.1" dependencies = [ + "anyhow", "assert_cmd", "clap", "console", diff --git a/Cargo.toml b/Cargo.toml index 218b79901b..d7b5a0965b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ authors = [ edition = "2021" [dependencies] +anyhow = "1.0.81" clap = { version = "4.5.2", features = ["derive"] } console = "0.15.8" glob = "0.3.0" diff --git a/src/main.rs b/src/main.rs index a06f0c563c..4a4f2198e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use crate::exercise::{Exercise, ExerciseList}; use crate::project::RustAnalyzerProject; use crate::run::{reset, run}; use crate::verify::verify; +use anyhow::Result; use clap::{Parser, Subcommand}; use console::Emoji; use notify_debouncer_mini::notify::{self, RecursiveMode}; @@ -84,7 +85,7 @@ enum Subcommands { Lsp, } -fn main() { +fn main() -> Result<()> { let args = Args::parse(); if args.command.is_none() { @@ -243,6 +244,8 @@ fn main() { } }, } + + Ok(()) } fn spawn_watch_shell( From 51712cc19f97972f470c4d8791974f8eaba095d1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 03:49:10 +0100 Subject: [PATCH 0536/1432] Merge get_sysroot_src into the constructor --- src/main.rs | 5 +--- src/project.rs | 77 +++++++++++++++++++++++++------------------------- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4a4f2198e2..4ce0b30dfd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -204,10 +204,7 @@ fn main() -> Result<()> { } Subcommands::Lsp => { - let mut project = RustAnalyzerProject::new(); - project - .get_sysroot_src() - .expect("Couldn't find toolchain path, do you have `rustc` installed?"); + let mut project = RustAnalyzerProject::build()?; project .exercises_to_json() .expect("Couldn't parse rustlings exercises files"); diff --git a/src/project.rs b/src/project.rs index 93f941dd98..a7414d1f16 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,3 +1,4 @@ +use anyhow::{bail, Context, Result}; use glob::glob; use serde::{Deserialize, Serialize}; use std::env; @@ -22,11 +23,44 @@ pub struct Crate { } impl RustAnalyzerProject { - pub fn new() -> RustAnalyzerProject { - RustAnalyzerProject { - sysroot_src: String::new(), - crates: Vec::new(), + pub fn build() -> Result { + // check if RUST_SRC_PATH is set + if let Ok(sysroot_src) = env::var("RUST_SRC_PATH") { + return Ok(Self { + sysroot_src, + crates: Vec::new(), + }); } + + let toolchain = Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output() + .context("Failed to get the sysroot from `rustc`. Do you have `rustc` installed?")? + .stdout; + + let toolchain = + String::from_utf8(toolchain).context("The toolchain path is invalid UTF8")?; + let toolchain = toolchain.trim_end(); + + println!("Determined toolchain: {toolchain}\n"); + + let Ok(sysroot_src) = Path::new(toolchain) + .join("lib") + .join("rustlib") + .join("src") + .join("rust") + .join("library") + .into_os_string() + .into_string() + else { + bail!("The sysroot path is invalid UTF8"); + }; + + Ok(Self { + sysroot_src, + crates: Vec::new(), + }) } /// Write rust-project.json to disk @@ -66,39 +100,4 @@ impl RustAnalyzerProject { } Ok(()) } - - /// Use `rustc` to determine the default toolchain - pub fn get_sysroot_src(&mut self) -> Result<(), Box> { - // check if RUST_SRC_PATH is set - if let Ok(path) = env::var("RUST_SRC_PATH") { - self.sysroot_src = path; - return Ok(()); - } - - let toolchain = Command::new("rustc") - .arg("--print") - .arg("sysroot") - .output()? - .stdout; - - let toolchain = String::from_utf8(toolchain)?; - let toolchain = toolchain.trim_end(); - - println!("Determined toolchain: {toolchain}\n"); - - let Ok(path) = Path::new(toolchain) - .join("lib") - .join("rustlib") - .join("src") - .join("rust") - .join("library") - .into_os_string() - .into_string() - else { - return Err("The sysroot path is invalid UTF8".into()); - }; - self.sysroot_src = path; - - Ok(()) - } } From d095a307ddbdef1f67e89320491c76a1bed1c8eb Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 03:59:21 +0100 Subject: [PATCH 0537/1432] Avoid allocations on every call to Path::join --- src/project.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/project.rs b/src/project.rs index a7414d1f16..c017aa227d 100644 --- a/src/project.rs +++ b/src/project.rs @@ -3,7 +3,7 @@ use glob::glob; use serde::{Deserialize, Serialize}; use std::env; use std::error::Error; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::Command; /// Contains the structure of resulting rust-project.json file @@ -45,15 +45,9 @@ impl RustAnalyzerProject { println!("Determined toolchain: {toolchain}\n"); - let Ok(sysroot_src) = Path::new(toolchain) - .join("lib") - .join("rustlib") - .join("src") - .join("rust") - .join("library") - .into_os_string() - .into_string() - else { + let mut sysroot_src = PathBuf::with_capacity(256); + sysroot_src.extend([toolchain, "lib", "rustlib", "src", "rust", "library"]); + let Ok(sysroot_src) = sysroot_src.into_os_string().into_string() else { bail!("The sysroot path is invalid UTF8"); }; From dca3ea355ea1809318ea545f23f396405d86aa0a Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 14:10:51 +0100 Subject: [PATCH 0538/1432] Remove the home dependency since it is not used --- Cargo.lock | 10 ---------- Cargo.toml | 1 - 2 files changed, 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3950c476c0..9d8606ad3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,15 +255,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "indexmap" version = "2.2.5" @@ -529,7 +520,6 @@ dependencies = [ "clap", "console", "glob", - "home", "indicatif", "notify-debouncer-mini", "predicates", diff --git a/Cargo.toml b/Cargo.toml index 218b79901b..2e6ab3bdb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ edition = "2021" clap = { version = "4.5.2", features = ["derive"] } console = "0.15.8" glob = "0.3.0" -home = "0.5.9" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" regex = "1.10.3" From b932ed1f672532e7dccf6cd23f6b9895c24a4de7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 17:14:41 +0100 Subject: [PATCH 0539/1432] Don't capture stderr --- src/project.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/project.rs b/src/project.rs index c017aa227d..1f42d4eb58 100644 --- a/src/project.rs +++ b/src/project.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use std::env; use std::error::Error; use std::path::PathBuf; -use std::process::Command; +use std::process::{Command, Stdio}; /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file @@ -35,6 +35,7 @@ impl RustAnalyzerProject { let toolchain = Command::new("rustc") .arg("--print") .arg("sysroot") + .stderr(Stdio::inherit()) .output() .context("Failed to get the sysroot from `rustc`. Do you have `rustc` installed?")? .stdout; From d911586788ad411be92e43cdc2f7e88fee7e78a4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 17:21:54 +0100 Subject: [PATCH 0540/1432] Pipe the output to null instead of capturing and ignoring it --- src/exercise.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 664b362bc3..e6a9222cf5 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display, Formatter}; use std::fs::{self, remove_file, File}; use std::io::Read; use std::path::PathBuf; -use std::process::{self, Command}; +use std::process::{self, Command, Stdio}; const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; @@ -148,7 +148,10 @@ path = "{}.rs""#, .args(RUSTC_COLOR_ARGS) .args(RUSTC_EDITION_ARGS) .args(RUSTC_NO_DEBUG_ARGS) - .output() + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() .expect("Failed to compile!"); // Due to an issue with Clippy, a cargo clean is required to catch all lints. // See https://github.com/rust-lang/rust-clippy/issues/2604 @@ -157,7 +160,10 @@ path = "{}.rs""#, Command::new("cargo") .args(["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(RUSTC_COLOR_ARGS) - .output() + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() .expect("Failed to run 'cargo clean'"); Command::new("cargo") .args(["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) From 87e55ccffde51b08be7d90ab53f1bb2462efa85a Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 22:20:00 +0100 Subject: [PATCH 0541/1432] Use the parsed exercises instead of glob --- Cargo.toml | 1 - src/main.rs | 2 +- src/project.rs | 35 +++++++++++++---------------------- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d7b5a0965b..ef49947313 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ edition = "2021" anyhow = "1.0.81" clap = { version = "4.5.2", features = ["derive"] } console = "0.15.8" -glob = "0.3.0" home = "0.5.9" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" diff --git a/src/main.rs b/src/main.rs index 4ce0b30dfd..803e2f8f27 100644 --- a/src/main.rs +++ b/src/main.rs @@ -206,7 +206,7 @@ fn main() -> Result<()> { Subcommands::Lsp => { let mut project = RustAnalyzerProject::build()?; project - .exercises_to_json() + .exercises_to_json(exercises) .expect("Couldn't parse rustlings exercises files"); if project.crates.is_empty() { diff --git a/src/project.rs b/src/project.rs index 1f42d4eb58..534aab0910 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,11 +1,12 @@ use anyhow::{bail, Context, Result}; -use glob::glob; use serde::{Deserialize, Serialize}; use std::env; use std::error::Error; use std::path::PathBuf; use std::process::{Command, Stdio}; +use crate::exercise::Exercise; + /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file #[derive(Serialize, Deserialize)] @@ -69,30 +70,20 @@ impl RustAnalyzerProject { Ok(()) } - /// If path contains .rs extension, add a crate to `rust-project.json` - fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box> { - if let Some(ext) = path.extension() { - if ext == "rs" { - self.crates.push(Crate { - root_module: path.display().to_string(), - edition: "2021".to_string(), - deps: Vec::new(), - // This allows rust_analyzer to work inside #[test] blocks - cfg: vec!["test".to_string()], - }) - } - } - - Ok(()) - } - /// Parse the exercises folder for .rs files, any matches will create /// a new `crate` in rust-project.json which allows rust-analyzer to /// treat it like a normal binary - pub fn exercises_to_json(&mut self) -> Result<(), Box> { - for path in glob("./exercises/**/*")? { - self.path_to_json(path?)?; - } + pub fn exercises_to_json(&mut self, exercises: Vec) -> Result<(), Box> { + self.crates = exercises + .into_iter() + .map(|exercise| Crate { + root_module: exercise.path.display().to_string(), + edition: "2021".to_string(), + deps: Vec::new(), + // This allows rust_analyzer to work inside #[test] blocks + cfg: vec!["test".to_string()], + }) + .collect(); Ok(()) } } From f5135ae4df96ee018896d667f3dffa187c959193 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 22:29:33 +0100 Subject: [PATCH 0542/1432] Remove unneeded check if crates is empty --- src/main.rs | 4 +--- src/project.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 803e2f8f27..1f260ab7a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -209,9 +209,7 @@ fn main() -> Result<()> { .exercises_to_json(exercises) .expect("Couldn't parse rustlings exercises files"); - if project.crates.is_empty() { - println!("Failed find any exercises, make sure you're in the `rustlings` folder"); - } else if project.write_to_disk().is_err() { + if project.write_to_disk().is_err() { println!("Failed to write rust-project.json to disk for rust-analyzer"); } else { println!("Successfully generated rust-project.json"); diff --git a/src/project.rs b/src/project.rs index 534aab0910..835a951a2e 100644 --- a/src/project.rs +++ b/src/project.rs @@ -12,7 +12,7 @@ use crate::exercise::Exercise; #[derive(Serialize, Deserialize)] pub struct RustAnalyzerProject { sysroot_src: String, - pub crates: Vec, + crates: Vec, } #[derive(Serialize, Deserialize)] From a5ba44bd6a939a720cc600e06785bea98baabc37 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 22:30:16 +0100 Subject: [PATCH 0543/1432] RustAnalyzerProject is not deserialized --- src/project.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/project.rs b/src/project.rs index 835a951a2e..347ca461f3 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Context, Result}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use std::env; use std::error::Error; use std::path::PathBuf; @@ -9,13 +9,13 @@ use crate::exercise::Exercise; /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file -#[derive(Serialize, Deserialize)] +#[derive(Serialize)] pub struct RustAnalyzerProject { sysroot_src: String, crates: Vec, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize)] pub struct Crate { root_module: String, edition: String, From 8d3ec24c11654d668ef1e1638a7770ec8beadfb7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 22:41:14 +0100 Subject: [PATCH 0544/1432] Optimize the serialized data types --- src/project.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/project.rs b/src/project.rs index 347ca461f3..54cffe1201 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use serde::Serialize; use std::env; use std::error::Error; @@ -11,24 +11,25 @@ use crate::exercise::Exercise; /// and functions to build the data required to create the file #[derive(Serialize)] pub struct RustAnalyzerProject { - sysroot_src: String, + sysroot_src: PathBuf, crates: Vec, } #[derive(Serialize)] -pub struct Crate { - root_module: String, - edition: String, - deps: Vec, - cfg: Vec, +struct Crate { + root_module: PathBuf, + edition: &'static str, + // Not used, but required in the JSON file. + deps: Vec<()>, + cfg: [&'static str; 1], } impl RustAnalyzerProject { pub fn build() -> Result { // check if RUST_SRC_PATH is set - if let Ok(sysroot_src) = env::var("RUST_SRC_PATH") { + if let Some(path) = env::var_os("RUST_SRC_PATH") { return Ok(Self { - sysroot_src, + sysroot_src: PathBuf::from(path), crates: Vec::new(), }); } @@ -49,9 +50,6 @@ impl RustAnalyzerProject { let mut sysroot_src = PathBuf::with_capacity(256); sysroot_src.extend([toolchain, "lib", "rustlib", "src", "rust", "library"]); - let Ok(sysroot_src) = sysroot_src.into_os_string().into_string() else { - bail!("The sysroot path is invalid UTF8"); - }; Ok(Self { sysroot_src, @@ -77,11 +75,11 @@ impl RustAnalyzerProject { self.crates = exercises .into_iter() .map(|exercise| Crate { - root_module: exercise.path.display().to_string(), - edition: "2021".to_string(), + root_module: exercise.path, + edition: "2021", deps: Vec::new(), // This allows rust_analyzer to work inside #[test] blocks - cfg: vec!["test".to_string()], + cfg: ["test"], }) .collect(); Ok(()) From 8ddbf9635d21a4c0306bd31cca5c4077693ca917 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 23:01:56 +0100 Subject: [PATCH 0545/1432] Add write_project_json --- src/main.rs | 11 +++------- src/project.rs | 59 +++++++++++++++++++++++--------------------------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1f260ab7a3..46aaf1f34f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use crate::exercise::{Exercise, ExerciseList}; -use crate::project::RustAnalyzerProject; +use crate::project::write_project_json; use crate::run::{reset, run}; use crate::verify::verify; use anyhow::Result; @@ -204,13 +204,8 @@ fn main() -> Result<()> { } Subcommands::Lsp => { - let mut project = RustAnalyzerProject::build()?; - project - .exercises_to_json(exercises) - .expect("Couldn't parse rustlings exercises files"); - - if project.write_to_disk().is_err() { - println!("Failed to write rust-project.json to disk for rust-analyzer"); + if let Err(e) = write_project_json(exercises) { + println!("Failed to write rust-project.json to disk for rust-analyzer: {e}"); } else { println!("Successfully generated rust-project.json"); println!("rust-analyzer will now parse exercises, restart your language server or editor") diff --git a/src/project.rs b/src/project.rs index 54cffe1201..acf011d31d 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,7 +1,6 @@ use anyhow::{Context, Result}; use serde::Serialize; use std::env; -use std::error::Error; use std::path::PathBuf; use std::process::{Command, Stdio}; @@ -10,7 +9,7 @@ use crate::exercise::Exercise; /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file #[derive(Serialize)] -pub struct RustAnalyzerProject { +struct RustAnalyzerProject { sysroot_src: PathBuf, crates: Vec, } @@ -25,12 +24,22 @@ struct Crate { } impl RustAnalyzerProject { - pub fn build() -> Result { - // check if RUST_SRC_PATH is set + fn build(exercises: Vec) -> Result { + let crates = exercises + .into_iter() + .map(|exercise| Crate { + root_module: exercise.path, + edition: "2021", + deps: Vec::new(), + // This allows rust_analyzer to work inside #[test] blocks + cfg: ["test"], + }) + .collect(); + if let Some(path) = env::var_os("RUST_SRC_PATH") { return Ok(Self { sysroot_src: PathBuf::from(path), - crates: Vec::new(), + crates, }); } @@ -53,35 +62,21 @@ impl RustAnalyzerProject { Ok(Self { sysroot_src, - crates: Vec::new(), + crates, }) } +} - /// Write rust-project.json to disk - pub fn write_to_disk(&self) -> Result<(), std::io::Error> { - // Using the capacity 2^14 = 16384 since the file length in bytes is higher than 2^13. - // The final length is not known exactly because it depends on the user's sysroot path, - // the current number of exercises etc. - let mut buf = Vec::with_capacity(16384); - serde_json::to_writer(&mut buf, &self).expect("Failed to serialize to JSON"); - std::fs::write("rust-project.json", buf)?; - Ok(()) - } +/// Write `rust-project.json` to disk. +pub fn write_project_json(exercises: Vec) -> Result<()> { + let content = RustAnalyzerProject::build(exercises)?; - /// Parse the exercises folder for .rs files, any matches will create - /// a new `crate` in rust-project.json which allows rust-analyzer to - /// treat it like a normal binary - pub fn exercises_to_json(&mut self, exercises: Vec) -> Result<(), Box> { - self.crates = exercises - .into_iter() - .map(|exercise| Crate { - root_module: exercise.path, - edition: "2021", - deps: Vec::new(), - // This allows rust_analyzer to work inside #[test] blocks - cfg: ["test"], - }) - .collect(); - Ok(()) - } + // Using the capacity 2^14 since the file length in bytes is higher than 2^13. + // The final length is not known exactly because it depends on the user's sysroot path, + // the current number of exercises etc. + let mut buf = Vec::with_capacity(1 << 14); + serde_json::to_writer(&mut buf, &content)?; + std::fs::write("rust-project.json", buf)?; + + Ok(()) } From a158c77d81f2b2870385f70b63511588ed6912ff Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 25 Mar 2024 23:21:14 +0100 Subject: [PATCH 0546/1432] Add comment --- src/project.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/project.rs b/src/project.rs index acf011d31d..0f56de9667 100644 --- a/src/project.rs +++ b/src/project.rs @@ -20,6 +20,8 @@ struct Crate { edition: &'static str, // Not used, but required in the JSON file. deps: Vec<()>, + // Only `test` is used for all crates. + // Therefore, an array is used instead of a `Vec`. cfg: [&'static str; 1], } @@ -31,7 +33,7 @@ impl RustAnalyzerProject { root_module: exercise.path, edition: "2021", deps: Vec::new(), - // This allows rust_analyzer to work inside #[test] blocks + // This allows rust_analyzer to work inside `#[test]` blocks cfg: ["test"], }) .collect(); @@ -54,7 +56,6 @@ impl RustAnalyzerProject { let toolchain = String::from_utf8(toolchain).context("The toolchain path is invalid UTF8")?; let toolchain = toolchain.trim_end(); - println!("Determined toolchain: {toolchain}\n"); let mut sysroot_src = PathBuf::with_capacity(256); From 7a6f71f09092e8a521d53456491e7d9d8a159602 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 02:14:25 +0100 Subject: [PATCH 0547/1432] Fix context of previous lines and improve readability --- src/exercise.rs | 152 +++++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 73 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 136e94399c..e841aed25f 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter}; use std::fs::{self, remove_file, File}; use std::io::{self, BufRead, BufReader}; use std::path::PathBuf; -use std::process::{self, Command}; +use std::process::{self, exit, Command}; use std::{array, env, mem}; use winnow::ascii::{space0, Caseless}; use winnow::combinator::opt; @@ -15,7 +15,8 @@ const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; const CONTEXT: usize = 2; const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml"; -fn not_done(input: &str) -> bool { +// Checks if the line contains the "I AM NOT DONE" comment. +fn contains_not_done_comment(input: &str) -> bool { ( space0::<_, ()>, "//", @@ -219,12 +220,15 @@ path = "{}.rs""#, pub fn state(&self) -> State { let source_file = File::open(&self.path).unwrap_or_else(|e| { - panic!( - "We were unable to open the exercise file {}! {e}", - self.path.display() - ) + println!( + "Failed to open the exercise file {}: {e}", + self.path.display(), + ); + exit(1); }); let mut source_reader = BufReader::new(source_file); + + // Read the next line into `buf` without the newline at the end. let mut read_line = |buf: &mut String| -> io::Result<_> { let n = source_reader.read_line(buf)?; if buf.ends_with('\n') { @@ -236,70 +240,72 @@ path = "{}.rs""#, Ok(n) }; - let mut matched_line_ind: usize = 0; + let mut current_line_number: usize = 1; let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); let mut line = String::with_capacity(256); loop { - match read_line(&mut line) { - Ok(0) => break, - Ok(_) => { - if not_done(&line) { - let mut context = Vec::with_capacity(2 * CONTEXT + 1); - for (ind, prev_line) in prev_lines - .into_iter() - .rev() - .take(matched_line_ind) - .enumerate() - { - context.push(ContextLine { - line: prev_line, - // TODO - number: matched_line_ind - CONTEXT + ind + 1, - important: false, - }); - } - - context.push(ContextLine { - line, - number: matched_line_ind + 1, - important: true, - }); - - for ind in 0..CONTEXT { - let mut next_line = String::with_capacity(256); - let Ok(n) = read_line(&mut next_line) else { - break; - }; - - if n == 0 { - break; - } - - context.push(ContextLine { - line: next_line, - number: matched_line_ind + ind + 2, - important: false, - }); - } - - return State::Pending(context); - } + let n = read_line(&mut line).unwrap_or_else(|e| { + println!( + "Failed to read the exercise file {}: {e}", + self.path.display(), + ); + exit(1); + }); + + // Reached the end of the file and didn't find the comment. + if n == 0 { + return State::Done; + } + + if contains_not_done_comment(&line) { + let mut context = Vec::with_capacity(2 * CONTEXT + 1); + for (ind, prev_line) in prev_lines + .into_iter() + .take(current_line_number - 1) + .enumerate() + .rev() + { + context.push(ContextLine { + line: prev_line, + number: current_line_number - 1 - ind, + important: false, + }); + } - matched_line_ind += 1; - for prev_line in &mut prev_lines { - mem::swap(&mut line, prev_line); + context.push(ContextLine { + line, + number: current_line_number, + important: true, + }); + + for ind in 0..CONTEXT { + let mut next_line = String::with_capacity(256); + let Ok(n) = read_line(&mut next_line) else { + break; + }; + + if n == 0 { + break; } - line.clear(); + + context.push(ContextLine { + line: next_line, + number: current_line_number + 1 + ind, + important: false, + }); } - Err(e) => panic!( - "We were unable to read the exercise file {}! {e}", - self.path.display() - ), + + return State::Pending(context); } - } - State::Done + current_line_number += 1; + // Recycle the buffers. + for prev_line in &mut prev_lines { + mem::swap(&mut line, prev_line); + } + line.clear(); + } } // Check that the exercise looks to be solved using self.state() @@ -428,17 +434,17 @@ mod test { #[test] fn test_not_done() { - assert!(not_done("// I AM NOT DONE")); - assert!(not_done("/// I AM NOT DONE")); - assert!(not_done("// I AM NOT DONE")); - assert!(not_done("/// I AM NOT DONE")); - assert!(not_done("// I AM NOT DONE ")); - assert!(not_done("// I AM NOT DONE!")); - assert!(not_done("// I am not done")); - assert!(not_done("// i am NOT done")); - - assert!(!not_done("I AM NOT DONE")); - assert!(!not_done("// NOT DONE")); - assert!(!not_done("DONE")); + assert!(contains_not_done_comment("// I AM NOT DONE")); + assert!(contains_not_done_comment("/// I AM NOT DONE")); + assert!(contains_not_done_comment("// I AM NOT DONE")); + assert!(contains_not_done_comment("/// I AM NOT DONE")); + assert!(contains_not_done_comment("// I AM NOT DONE ")); + assert!(contains_not_done_comment("// I AM NOT DONE!")); + assert!(contains_not_done_comment("// I am not done")); + assert!(contains_not_done_comment("// i am NOT done")); + + assert!(!contains_not_done_comment("I AM NOT DONE")); + assert!(!contains_not_done_comment("// NOT DONE")); + assert!(!contains_not_done_comment("DONE")); } } From 078f6ffc1cf18546079d03bee99f0903c9e14703 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 02:26:26 +0100 Subject: [PATCH 0548/1432] Add comments --- src/exercise.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index e841aed25f..cdf8d205c5 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -241,6 +241,7 @@ path = "{}.rs""#, }; let mut current_line_number: usize = 1; + // Keep the last `CONTEXT` lines while iterating over the file lines. let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); let mut line = String::with_capacity(256); @@ -260,6 +261,7 @@ path = "{}.rs""#, if contains_not_done_comment(&line) { let mut context = Vec::with_capacity(2 * CONTEXT + 1); + // Previous lines. for (ind, prev_line) in prev_lines .into_iter() .take(current_line_number - 1) @@ -273,18 +275,22 @@ path = "{}.rs""#, }); } + // Current line. context.push(ContextLine { line, number: current_line_number, important: true, }); + // Next lines. for ind in 0..CONTEXT { let mut next_line = String::with_capacity(256); let Ok(n) = read_line(&mut next_line) else { + // If an error occurs, just ignore the next lines. break; }; + // Reached the end of the file. if n == 0 { break; } @@ -300,10 +306,12 @@ path = "{}.rs""#, } current_line_number += 1; - // Recycle the buffers. + // Add the current line as a previous line and shift the older lines by one. for prev_line in &mut prev_lines { mem::swap(&mut line, prev_line); } + // The current line now contains the oldest previous line. + // Recycle it for reading the next line. line.clear(); } } From 853d0593d061119b042a45b602ff52af229dad83 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:47:33 +0100 Subject: [PATCH 0549/1432] Derive Eq when PartialEq is derived --- src/exercise.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 664b362bc3..a13ed2ce32 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -58,7 +58,7 @@ pub struct Exercise { // An enum to track of the state of an Exercise. // An Exercise can be either Done or Pending -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Eq, Debug)] pub enum State { // The state of the exercise once it's been completed Done, @@ -67,7 +67,7 @@ pub enum State { } // The context information of a pending exercise -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Eq, Debug)] pub struct ContextLine { // The source code that is still pending completion pub line: String, From f36efae25deee03cb6f98ce7fc1e59efb7e72985 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:48:06 +0100 Subject: [PATCH 0550/1432] Only use arg instead of args AND arg --- src/run.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/run.rs b/src/run.rs index e0ada4c5c3..6dd0388f8d 100644 --- a/src/run.rs +++ b/src/run.rs @@ -21,7 +21,8 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> { // Resets the exercise by stashing the changes. pub fn reset(exercise: &Exercise) -> Result<(), ()> { let command = Command::new("git") - .args(["stash", "--"]) + .arg("stash") + .arg("--") .arg(&exercise.path) .spawn(); From ed0fcf8e3d05f5420b55370d4ff4ad8e0ded127b Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:49:05 +0100 Subject: [PATCH 0551/1432] Formatting --- src/main.rs | 7 ++----- src/verify.rs | 32 +++++++++++++++----------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/main.rs b/src/main.rs index a06f0c563c..a0b3af2976 100644 --- a/src/main.rs +++ b/src/main.rs @@ -223,10 +223,7 @@ fn main() { Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Err(e) => { - println!( - "Error: Could not watch your progress. Error message was {:?}.", - e - ); + println!("Error: Could not watch your progress. Error message was {e:?}."); println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); std::process::exit(1); } @@ -280,7 +277,7 @@ fn spawn_watch_shell( if parts.is_empty() { println!("no command provided"); } else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() { - println!("failed to execute command `{}`: {}", cmd, e); + println!("failed to execute command `{cmd}`: {e}"); } } else { println!("unknown command: {input}"); diff --git a/src/verify.rs b/src/verify.rs index aee2afa385..3123e455f6 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -24,7 +24,7 @@ pub fn verify<'a>( .progress_chars("#>-"), ); bar.set_position(num_done as u64); - bar.set_message(format!("({:.1} %)", percentage)); + bar.set_message(format!("({percentage:.1} %)")); for exercise in exercises { let compile_result = match exercise.mode { @@ -37,7 +37,7 @@ pub fn verify<'a>( } percentage += 100.0 / total as f32; bar.inc(1); - bar.set_message(format!("({:.1} %)", percentage)); + bar.set_message(format!("({percentage:.1} %)")); if bar.position() == total as u64 { println!( "Progress: You completed {} / {} exercises ({:.1} %).", @@ -191,27 +191,25 @@ fn prompt_for_completion( Mode::Test => "The code is compiling, and the tests pass!", Mode::Clippy => clippy_success_msg, }; - println!(); + if no_emoji { - println!("~*~ {success_msg} ~*~") + println!("\n~*~ {success_msg} ~*~\n"); } else { - println!("πŸŽ‰ πŸŽ‰ {success_msg} πŸŽ‰ πŸŽ‰") + println!("\nπŸŽ‰ πŸŽ‰ {success_msg} πŸŽ‰ πŸŽ‰\n"); } - println!(); if let Some(output) = prompt_output { - println!("Output:"); - println!("{}", separator()); - println!("{output}"); - println!("{}", separator()); - println!(); + println!( + "Output:\n{separator}\n{output}\n{separator}\n", + separator = separator(), + ); } if success_hints { - println!("Hints:"); - println!("{}", separator()); - println!("{}", exercise.hint); - println!("{}", separator()); - println!(); + println!( + "Hints:\n{separator}\n{}\n{separator}\n", + exercise.hint, + separator = separator(), + ); } println!("You can keep working on this exercise,"); @@ -231,7 +229,7 @@ fn prompt_for_completion( "{:>2} {} {}", style(context_line.number).blue().bold(), style("|").blue(), - formatted_line + formatted_line, ); } From 1f2029ae5503024f71203893fe1eab7b90aa80af Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:49:25 +0100 Subject: [PATCH 0552/1432] Add missing semicolon --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index a0b3af2976..6884a0e523 100644 --- a/src/main.rs +++ b/src/main.rs @@ -217,7 +217,7 @@ fn main() { println!("Failed to write rust-project.json to disk for rust-analyzer"); } else { println!("Successfully generated rust-project.json"); - println!("rust-analyzer will now parse exercises, restart your language server or editor") + println!("rust-analyzer will now parse exercises, restart your language server or editor"); } } From 980ffa2a2bb791992ef05ca9b05aadba62ec6abc Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:49:48 +0100 Subject: [PATCH 0553/1432] Use == on simple enums --- src/verify.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/verify.rs b/src/verify.rs index 3123e455f6..e2fa98f2fa 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -51,6 +51,7 @@ pub fn verify<'a>( Ok(()) } +#[derive(PartialEq, Eq)] enum RunMode { Interactive, NonInteractive, @@ -124,7 +125,7 @@ fn compile_and_test( if verbose { println!("{}", output.stdout); } - if let RunMode::Interactive = run_mode { + if run_mode == RunMode::Interactive { Ok(prompt_for_completion(exercise, None, success_hints)) } else { Ok(true) From e89028581cd03c02cb0971a2772fa382667019a3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:49:55 +0100 Subject: [PATCH 0554/1432] Use == instead of eq --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 6884a0e523..559be698ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -289,7 +289,7 @@ fn spawn_watch_shell( } fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { - if name.eq("next") { + if name == "next" { exercises .iter() .find(|e| !e.looks_done()) From a610fc1bc21a04017542208ef70a8010ee00c04c Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:50:10 +0100 Subject: [PATCH 0555/1432] Remove unneeded closure --- src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 559be698ad..eca73fa0b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -335,7 +335,6 @@ fn watch( clear_screen(); - let to_owned_hint = |t: &Exercise| t.hint.to_owned(); let failed_exercise_hint = match verify( exercises.iter(), (0, exercises.len()), @@ -343,7 +342,7 @@ fn watch( success_hints, ) { Ok(_) => return Ok(WatchStatus::Finished), - Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))), + Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), }; spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit)); loop { @@ -380,7 +379,7 @@ fn watch( Err(exercise) => { let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap(); - *failed_exercise_hint = Some(to_owned_hint(exercise)); + *failed_exercise_hint = Some(exercise.hint.clone()); } } } From 87001a68c0cc6b3498a253d0923e9c609355c4ee Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 26 Mar 2024 17:50:29 +0100 Subject: [PATCH 0556/1432] The string doesn't have to be a raw string --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index eca73fa0b2..141549c617 100644 --- a/src/main.rs +++ b/src/main.rs @@ -411,7 +411,7 @@ fn rustc_exists() -> bool { .unwrap_or(false) } -const DEFAULT_OUT: &str = r#"Thanks for installing Rustlings! +const DEFAULT_OUT: &str = "Thanks for installing Rustlings! 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 @@ -437,7 +437,7 @@ started, here's a couple of notes about how Rustlings operates: autocompletion, run the command `rustlings lsp`. Got all that? Great! To get started, run `rustlings watch` in order to get the first -exercise. Make sure to have your editor open!"#; +exercise. Make sure to have your editor open!"; const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | From 76764633b46ee570e2b7d2b8564d37b7292fa337 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 27 Mar 2024 15:16:42 +0100 Subject: [PATCH 0557/1432] Update deps --- Cargo.lock | 56 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 4 ++-- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 11880b7f84..f4853d0c37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -82,9 +82,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "bitflags" @@ -94,9 +94,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bstr" @@ -117,9 +117,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.2" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", @@ -273,9 +273,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "home" @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -340,9 +340,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kqueue" @@ -418,7 +418,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -495,9 +495,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -522,9 +522,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -545,9 +545,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustix" @@ -555,7 +555,7 @@ version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -619,9 +619,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -651,9 +651,9 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "2.0.52" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 36e112351a..2d152cfc5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,11 @@ edition = "2021" [dependencies] anyhow = "1.0.81" -clap = { version = "4.5.2", features = ["derive"] } +clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" -serde_json = "1.0.114" +serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } shlex = "1.3.0" toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } From e7bb832bf3e651f88fa3318df7e6b04b56fb9dee Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 27 Mar 2024 17:03:53 +0100 Subject: [PATCH 0558/1432] Remove outdated info about the command line parser --- CONTRIBUTING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc8ac92358..d66e3de5f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,6 @@ _implement a new feature! ➑️ [open an Issue to discuss it first, then a Pull `rustlings` is basically a glorified `rustc` wrapper. Therefore the source code isn't really that complicated since the bulk of the work is done by `rustc`. -`src/main.rs` contains a simple `argh` CLI that connects to most of the other source files. ### Adding an exercise From 92183a74c4c7b91459c1371bb7a68b5e4c1c23bd Mon Sep 17 00:00:00 2001 From: wznmickey Date: Thu, 28 Mar 2024 00:06:16 +0800 Subject: [PATCH 0559/1432] chore: update the chapter of macros --- exercises/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/README.md b/exercises/README.md index c7effa95b6..4cb966e5ff 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -22,6 +22,6 @@ | iterators | Β§13.2-4 | | threads | Β§16.1-3 | | smart_pointers | Β§15, Β§16.3 | -| macros | Β§19.6 | +| macros | Β§19.5 | | clippy | Β§21.4 | | conversions | n/a | From 971e7f94dc58b6846e3b4e7f3d5df5b4ea72790b Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 27 Mar 2024 17:08:38 +0100 Subject: [PATCH 0560/1432] Update the link to conventionalcommits.org --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d66e3de5f4..4fc7fb79ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,7 @@ changes. There's a couple of things to watch out for: #### Write correct commit messages -We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) +We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. This means that you have to format your commit messages in a specific way. Say you're working on adding a new exercise called `foobar1.rs`. You could write From 669adbeb604b66631040f9322549f3e6afaeef3d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:21:13 +0000 Subject: [PATCH 0561/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 00122e07ce..0f229dbbce 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -378,6 +378,7 @@ authors. 0Ahmed-0
0Ahmed-0

πŸ–‹ guizo792
guizo792

πŸ–‹ Kazuki Matsuo
Kazuki Matsuo

πŸ’» + Paul Leydier
Paul Leydier

πŸ“– From fb8dd57d1f2aa1e3865266f08d91e128f6207c64 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:21:14 +0000 Subject: [PATCH 0562/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f2446f1f76..e5b319cc4c 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2685,6 +2685,15 @@ "contributions": [ "code" ] + }, + { + "login": "paul-leydier", + "name": "Paul Leydier", + "avatar_url": "https://avatars.githubusercontent.com/u/75126792?v=4", + "profile": "https://github.com/paul-leydier", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 8, From 7b20ca9d0415ef94902c897242e3c6fbd154b6a6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:23:45 +0000 Subject: [PATCH 0563/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 0f229dbbce..f229173ae3 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -380,6 +380,9 @@ authors. Kazuki Matsuo
Kazuki Matsuo

πŸ’» Paul Leydier
Paul Leydier

πŸ“– + + wznmickey
wznmickey

πŸ“– + From 4937cb5b7c38dbecf232641c0864d5d9b261aebd Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:23:46 +0000 Subject: [PATCH 0564/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e5b319cc4c..2eae587848 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2694,6 +2694,15 @@ "contributions": [ "doc" ] + }, + { + "login": "wznmickey", + "name": "wznmickey", + "avatar_url": "https://avatars.githubusercontent.com/u/44784663?v=4", + "profile": "http://wznmickey.com", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 8, From 4e7f9ca1b3741fccc28b1f71118b0ffb5cf39aa4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:29:03 +0000 Subject: [PATCH 0565/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index f229173ae3..4a98bb9b36 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -382,6 +382,7 @@ authors. wznmickey
wznmickey

πŸ“– + NicolasRoelandt
NicolasRoelandt

πŸ“– From 9895c1f9bda4d9bf85c4a10157ff4714f57c52fc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:29:04 +0000 Subject: [PATCH 0566/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2eae587848..3095896dbd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2703,6 +2703,15 @@ "contributions": [ "doc" ] + }, + { + "login": "NicolasRoelandt", + "name": "NicolasRoelandt", + "avatar_url": "https://avatars.githubusercontent.com/u/8594193?v=4", + "profile": "https://github.com/NicolasRoelandt", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 8, From 3df59379de1b3333b911c60b867216690d6c5e1b Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 27 Mar 2024 20:28:31 +0100 Subject: [PATCH 0567/1432] Remove the reference to v1 --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8fac7a28bb..6b9c98336d 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! -_...looking for the old, web-based version of Rustlings? Try [here](https://github.com/rust-lang/rustlings/tree/rustlings-1)_ - Alternatively, for a first-time Rust learner, there are several other resources: - [The Book](https://doc.rust-lang.org/book/index.html) - The most comprehensive resource for learning Rust, but a bit theoretical sometimes. You will be using this along with Rustlings! From 842e341895690aa8d59aab42d6294994ef99d10f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 27 Mar 2024 21:24:36 +0100 Subject: [PATCH 0568/1432] threads2: simplify threads2 --- exercises/20_threads/threads2.rs | 11 +++++++---- info.toml | 20 ++++++-------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs index 62dad80d62..60d68241c8 100644 --- a/exercises/20_threads/threads2.rs +++ b/exercises/20_threads/threads2.rs @@ -18,7 +18,9 @@ struct JobStatus { } fn main() { + // TODO: `Arc` isn't enough if you want a **mutable** shared state let status = Arc::new(JobStatus { jobs_completed: 0 }); + let mut handles = vec![]; for _ in 0..10 { let status_shared = Arc::clone(&status); @@ -29,11 +31,12 @@ fn main() { }); handles.push(handle); } + + // Waiting for all jobs to complete for handle in handles { handle.join().unwrap(); - // TODO: Print the value of the JobStatus.jobs_completed. Did you notice - // anything interesting in the output? Do you have to 'join' on all the - // handles? - println!("jobs completed {}", ???); } + + // TODO: Print the value of `JobStatus.jobs_completed` + println!("Jobs completed: {}", ???); } diff --git a/info.toml b/info.toml index b1cd64ccf0..36629b38c9 100644 --- a/info.toml +++ b/info.toml @@ -1136,25 +1136,17 @@ to **immutable** data. But we want to *change* the number of `jobs_completed` so we'll need to also use another type that will only allow one thread to mutate the data at a time. Take a look at this section of the book: https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct -and keep reading if you'd like more hints :) -Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: +Keep reading if you'd like more hints :) + +Do you now have an `Arc>` at the beginning of `main`? Like: ``` let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); ``` -Similar to the code in the example in the book that happens after the text -that says 'Sharing a Mutex Between Multiple Threads'. If not, give that a -try! If you do and would like more hints, keep reading!! - -Make sure neither of your threads are holding onto the lock of the mutex -while they are sleeping, since this will prevent the other thread from -being allowed to get the lock. Locks are automatically released when -they go out of scope. - -If you've learned from the sample solutions, I encourage you to come -back to this exercise and try it again in a few days to reinforce -what you've learned :)""" +Similar to the code in the following example in the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads +""" [[exercises]] name = "threads3" From 0888952cb95946885f364a6218346200fb8f0bb4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:10:05 +0000 Subject: [PATCH 0569/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 4a98bb9b36..42c7dbfe95 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -383,6 +383,7 @@ authors. wznmickey
wznmickey

πŸ“– NicolasRoelandt
NicolasRoelandt

πŸ“– + Josh Bouganim
Josh Bouganim

πŸ’» From 24cb4a3bc973fa97fb40cda2e46a2a73d1aeda48 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:10:06 +0000 Subject: [PATCH 0570/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 3095896dbd..be95fe9f63 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2712,6 +2712,15 @@ "contributions": [ "doc" ] + }, + { + "login": "jbouganim-parallel", + "name": "Josh Bouganim", + "avatar_url": "https://avatars.githubusercontent.com/u/150748285?v=4", + "profile": "https://github.com/jbouganim-parallel", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From 62afbb034f74dc170347d3eff5131e780930d639 Mon Sep 17 00:00:00 2001 From: Daniel Somerfield Date: Wed, 27 Mar 2024 20:37:19 -0700 Subject: [PATCH 0571/1432] Move test array to be in test module as vec --- exercises/11_hashmaps/hashmaps2.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index ab918cd8a7..a860d7b2f4 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -27,14 +27,6 @@ enum Fruit { Pineapple, } -const FRUIT_KINDS: [Fruit; 5] = [ - Fruit::Apple, - Fruit::Banana, - Fruit::Mango, - Fruit::Lychee, - Fruit::Pineapple, -]; - fn fruit_basket(basket: &mut HashMap) { let fruit_kinds = vec![ Fruit::Apple, @@ -92,9 +84,17 @@ mod tests { #[test] fn all_fruit_types_in_basket() { + let fruit_kinds = vec![ + Fruit::Apple, + Fruit::Banana, + Fruit::Mango, + Fruit::Lychee, + Fruit::Pineapple, + ]; + let mut basket = get_fruit_basket(); fruit_basket(&mut basket); - for fruit_kind in FRUIT_KINDS { + for fruit_kind in fruit_kinds { let amount = basket .get(&fruit_kind) .expect(format!("Fruit kind {:?} was not found in basket", fruit_kind).as_str()); From e5efc68a9101d7d7e38263c8a6ee44dda991fc6a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:34:48 +0100 Subject: [PATCH 0572/1432] Done macro --- Cargo.lock | 8 ++++ Cargo.toml | 21 +++++--- rustlings-macros/Cargo.toml | 12 +++++ rustlings-macros/src/lib.rs | 95 +++++++++++++++++++++++++++++++++++++ src/main.rs | 23 +++++++++ 5 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 rustlings-macros/Cargo.toml create mode 100644 rustlings-macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f4853d0c37..e432072d8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,6 +574,7 @@ dependencies = [ "indicatif", "notify-debouncer-mini", "predicates", + "rustlings-macros", "serde", "serde_json", "shlex", @@ -582,6 +583,13 @@ dependencies = [ "winnow", ] +[[package]] +name = "rustlings-macros" +version = "5.6.1" +dependencies = [ + "quote", +] + [[package]] name = "ryu" version = "1.0.17" diff --git a/Cargo.toml b/Cargo.toml index 2d152cfc5f..e08be8bfb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,30 @@ -[package] -name = "rustlings" -description = "Small exercises to get you used to reading and writing Rust code!" +[workspace] +resolver = "2" + +[workspace.package] version = "5.6.1" authors = [ "Liv ", "Carol (Nichols || Goulding) ", ] +license = "MIT" edition = "2021" +[package] +name = "rustlings" +description = "Small exercises to get you used to reading and writing Rust code!" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + [dependencies] anyhow = "1.0.81" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" +rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } shlex = "1.3.0" @@ -21,10 +32,6 @@ toml_edit = { version = "0.22.9", default-features = false, features = ["parse", which = "6.0.1" winnow = "0.6.5" -[[bin]] -name = "rustlings" -path = "src/main.rs" - [dev-dependencies] assert_cmd = "2.0.14" glob = "0.3.0" diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml new file mode 100644 index 0000000000..0114c8f0ac --- /dev/null +++ b/rustlings-macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rustlings-macros" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.35" diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs new file mode 100644 index 0000000000..dd1a5881a1 --- /dev/null +++ b/rustlings-macros/src/lib.rs @@ -0,0 +1,95 @@ +use proc_macro::TokenStream; +use quote::quote; +use std::{fs::read_dir, panic, path::PathBuf}; + +fn path_to_string(path: PathBuf) -> String { + path.into_os_string() + .into_string() + .unwrap_or_else(|original| { + panic!("The path {} is invalid UTF8", original.to_string_lossy()); + }) +} + +#[proc_macro] +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"); + + if entry.file_type().unwrap().is_file() { + let path = entry.path(); + if path.file_name().unwrap() != "README.md" { + files.push(path_to_string(path)); + } + + continue; + } + + let dir_path = entry.path(); + let dir_files = read_dir(&dir_path).unwrap_or_else(|e| { + panic!("Failed to open the directory {}: {e}", dir_path.display()); + }); + let dir_path = path_to_string(dir_path); + let dir_files = dir_files.filter_map(|entry| { + let entry = entry.unwrap_or_else(|e| { + panic!("Failed to read the directory {dir_path}: {e}"); + }); + let path = entry.path(); + + if !entry.file_type().unwrap().is_file() { + panic!("Found {} but expected only files", path.display()); + } + + if path.file_name().unwrap() == "README.md" { + 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)) + }); + + dirs.push(quote! { + EmbeddedFlatDir { + path: #dir_path, + readme: EmbeddedFile { + path: concat!(#dir_path, "/README.md"), + content: ::std::include_bytes!(concat!("../", #dir_path, "/README.md")), + }, + content: vec![ + #(EmbeddedFile { + path: #dir_files, + content: ::std::include_bytes!(concat!("../", #dir_files)), + }),* + ], + } + }); + } + + quote! { + EmbeddedFiles { + info_toml_content: ::std::include_str!("../info.toml"), + exercises_dir: ExercisesDir { + readme: EmbeddedFile { + path: "exercises/README.md", + content: ::std::include_bytes!("../exercises/README.md"), + }, + files: vec![#( + EmbeddedFile { + path: #files, + content: ::std::include_bytes!(concat!("../", #files)), + } + ),*], + dirs: vec![#(#dirs),*], + }, + } + } + .into() +} diff --git a/src/main.rs b/src/main.rs index 8f73dbbaf7..fed8c1170b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,28 @@ mod project; mod run; mod verify; +struct EmbeddedFile { + path: &'static str, + content: &'static [u8], +} + +struct EmbeddedFlatDir { + path: &'static str, + readme: EmbeddedFile, + content: Vec, +} + +struct ExercisesDir { + readme: EmbeddedFile, + files: Vec, + dirs: Vec, +} + +struct EmbeddedFiles { + info_toml_content: &'static str, + exercises_dir: ExercisesDir, +} + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -87,6 +109,7 @@ enum Subcommands { } fn main() -> Result<()> { + let embedded_files = rustlings_macros::include_files!(); let args = Args::parse(); if args.command.is_none() { From dd025391f2f3a4cb0a45e28163b01538b4b525cb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:52:51 +0100 Subject: [PATCH 0573/1432] Make everything static --- rustlings-macros/src/lib.rs | 6 +++--- src/main.rs | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index dd1a5881a1..d8cd05ce76 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -63,7 +63,7 @@ pub fn include_files(_: TokenStream) -> TokenStream { path: concat!(#dir_path, "/README.md"), content: ::std::include_bytes!(concat!("../", #dir_path, "/README.md")), }, - content: vec![ + content: &[ #(EmbeddedFile { path: #dir_files, content: ::std::include_bytes!(concat!("../", #dir_files)), @@ -81,13 +81,13 @@ pub fn include_files(_: TokenStream) -> TokenStream { path: "exercises/README.md", content: ::std::include_bytes!("../exercises/README.md"), }, - files: vec![#( + files: &[#( EmbeddedFile { path: #files, content: ::std::include_bytes!(concat!("../", #files)), } ),*], - dirs: vec![#(#dirs),*], + dirs: &[#(#dirs),*], }, } } diff --git a/src/main.rs b/src/main.rs index fed8c1170b..7822d12225 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,13 +35,13 @@ struct EmbeddedFile { struct EmbeddedFlatDir { path: &'static str, readme: EmbeddedFile, - content: Vec, + content: &'static [EmbeddedFile], } struct ExercisesDir { readme: EmbeddedFile, - files: Vec, - dirs: Vec, + files: &'static [EmbeddedFile], + dirs: &'static [EmbeddedFlatDir], } struct EmbeddedFiles { @@ -49,6 +49,8 @@ struct EmbeddedFiles { exercises_dir: ExercisesDir, } +static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -109,7 +111,6 @@ enum Subcommands { } fn main() -> Result<()> { - let embedded_files = rustlings_macros::include_files!(); let args = Args::parse(); if args.command.is_none() { From 39bdd086a775d87115691b830f65e2a438874fec Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 18:18:20 +0100 Subject: [PATCH 0574/1432] Use concat explicitly from std --- rustlings-macros/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d8cd05ce76..598b5c357a 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -60,13 +60,13 @@ pub fn include_files(_: TokenStream) -> TokenStream { EmbeddedFlatDir { path: #dir_path, readme: EmbeddedFile { - path: concat!(#dir_path, "/README.md"), - content: ::std::include_bytes!(concat!("../", #dir_path, "/README.md")), + path: ::std::concat!(#dir_path, "/README.md"), + content: ::std::include_bytes!(::std::concat!("../", #dir_path, "/README.md")), }, content: &[ #(EmbeddedFile { path: #dir_files, - content: ::std::include_bytes!(concat!("../", #dir_files)), + content: ::std::include_bytes!(::std::concat!("../", #dir_files)), }),* ], } @@ -84,7 +84,7 @@ pub fn include_files(_: TokenStream) -> TokenStream { files: &[#( EmbeddedFile { path: #files, - content: ::std::include_bytes!(concat!("../", #files)), + content: ::std::include_bytes!(::std::concat!("../", #files)), } ),*], dirs: &[#(#dirs),*], From d5ed749e9fde03212fd6fe5d60e2ddfe9b2429c9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 21:06:36 +0100 Subject: [PATCH 0575/1432] Add embedded.rs --- src/embedded.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 25 +----------- 2 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 src/embedded.rs diff --git a/src/embedded.rs b/src/embedded.rs new file mode 100644 index 0000000000..8f6c14e7d3 --- /dev/null +++ b/src/embedded.rs @@ -0,0 +1,101 @@ +use std::{ + fs::{create_dir, File, OpenOptions}, + io::{self, Write}, + path::Path, +}; + +pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); + +#[derive(Clone, Copy)] +pub enum WriteStrategy { + IfNotExists, + Overwrite, +} + +impl WriteStrategy { + fn open>(self, path: P) -> io::Result { + match self { + Self::IfNotExists => OpenOptions::new().create_new(true).write(true).open(path), + Self::Overwrite => OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path), + } + } +} + +struct EmbeddedFile { + path: &'static str, + content: &'static [u8], +} + +impl EmbeddedFile { + fn write_to_disk(&self, strategy: WriteStrategy) -> io::Result<()> { + strategy.open(self.path)?.write_all(self.content) + } +} + +struct EmbeddedFlatDir { + path: &'static str, + readme: EmbeddedFile, + content: &'static [EmbeddedFile], +} + +impl EmbeddedFlatDir { + fn init_on_disk(&self) -> io::Result<()> { + let path = Path::new(self.path); + + if let Err(e) = create_dir(path) { + if !path.is_dir() { + return Err(e); + } + } + + self.readme.write_to_disk(WriteStrategy::Overwrite) + } +} + +struct ExercisesDir { + readme: EmbeddedFile, + files: &'static [EmbeddedFile], + dirs: &'static [EmbeddedFlatDir], +} + +pub struct EmbeddedFiles { + info_toml_content: &'static str, + exercises_dir: ExercisesDir, +} + +impl EmbeddedFiles { + pub fn init_exercises_dir(&self) -> io::Result<()> { + create_dir("exercises")?; + self.exercises_dir + .readme + .write_to_disk(WriteStrategy::Overwrite) + } + + pub fn write_exercise_to_disk(&self, path: &Path, strategy: WriteStrategy) -> io::Result<()> { + if let Some(file) = self + .exercises_dir + .files + .iter() + .find(|file| file.path == path.as_os_str()) + { + return file.write_to_disk(strategy); + } + + for dir in self.exercises_dir.dirs { + if let Some(file) = dir + .content + .iter() + .find(|file| file.path == path.as_os_str()) + { + dir.init_on_disk()?; + return file.write_to_disk(strategy); + } + } + + Err(io::Error::from(io::ErrorKind::NotFound)) + } +} diff --git a/src/main.rs b/src/main.rs index 7822d12225..1e0aa6689a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,35 +22,12 @@ use std::time::Duration; #[macro_use] mod ui; +mod embedded; mod exercise; mod project; mod run; mod verify; -struct EmbeddedFile { - path: &'static str, - content: &'static [u8], -} - -struct EmbeddedFlatDir { - path: &'static str, - readme: EmbeddedFile, - content: &'static [EmbeddedFile], -} - -struct ExercisesDir { - readme: EmbeddedFile, - files: &'static [EmbeddedFile], - dirs: &'static [EmbeddedFlatDir], -} - -struct EmbeddedFiles { - info_toml_content: &'static str, - exercises_dir: ExercisesDir, -} - -static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); - /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] From 5b4103bbac180fcb1de747214647811a3622b476 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 21:10:31 +0100 Subject: [PATCH 0576/1432] Remove unneeded ./ from relative paths --- src/exercise.rs | 4 ++-- src/main.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 19f528a8c8..16e4a41cf4 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -13,7 +13,7 @@ const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; const CONTEXT: usize = 2; -const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml"; +const CLIPPY_CARGO_TOML_PATH: &str = "exercises/22_clippy/Cargo.toml"; // Checks if the line contains the "I AM NOT DONE" comment. fn contains_not_done_comment(input: &str) -> bool { @@ -36,7 +36,7 @@ fn temp_file() -> String { .filter(|c| c.is_alphanumeric()) .collect(); - format!("./temp_{}_{thread_id}", process::id()) + format!("temp_{}_{thread_id}", process::id()) } // The mode of the exercise. diff --git a/src/main.rs b/src/main.rs index 1e0aa6689a..90d0109cae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -342,7 +342,7 @@ fn watch( let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; debouncer .watcher() - .watch(Path::new("./exercises"), RecursiveMode::Recursive)?; + .watch(Path::new("exercises"), RecursiveMode::Recursive)?; clear_screen(); From 3ff9b0cd2a92a531e8c7a9f8a0f86b9fac04d252 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 22:11:16 +0100 Subject: [PATCH 0577/1432] POC done --- src/embedded.rs | 23 ++++++++++++++--- src/exercise.rs | 2 +- src/main.rs | 67 ++++++++++++++++++++++++++++--------------------- src/project.rs | 20 +++++++-------- src/run.rs | 16 +++--------- 5 files changed, 74 insertions(+), 54 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index 8f6c14e7d3..25dbe64108 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -52,7 +52,9 @@ impl EmbeddedFlatDir { } } - self.readme.write_to_disk(WriteStrategy::Overwrite) + self.readme.write_to_disk(WriteStrategy::Overwrite)?; + + Ok(()) } } @@ -63,16 +65,31 @@ struct ExercisesDir { } pub struct EmbeddedFiles { - info_toml_content: &'static str, + pub info_toml_content: &'static str, exercises_dir: ExercisesDir, } impl EmbeddedFiles { pub fn init_exercises_dir(&self) -> io::Result<()> { create_dir("exercises")?; + self.exercises_dir .readme - .write_to_disk(WriteStrategy::Overwrite) + .write_to_disk(WriteStrategy::IfNotExists)?; + + for file in self.exercises_dir.files { + file.write_to_disk(WriteStrategy::IfNotExists)?; + } + + for dir in self.exercises_dir.dirs { + dir.init_on_disk()?; + + for file in dir.content { + file.write_to_disk(WriteStrategy::IfNotExists)?; + } + } + + Ok(()) } pub fn write_exercise_to_disk(&self, path: &Path, strategy: WriteStrategy) -> io::Result<()> { diff --git a/src/exercise.rs b/src/exercise.rs index 16e4a41cf4..7c2e5fde16 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -36,7 +36,7 @@ fn temp_file() -> String { .filter(|c| c.is_alphanumeric()) .collect(); - format!("temp_{}_{thread_id}", process::id()) + format!("./temp_{}_{thread_id}", process::id()) } // The mode of the exercise. diff --git a/src/main.rs b/src/main.rs index 90d0109cae..822cd1adce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,14 @@ use crate::verify::verify; use anyhow::Result; use clap::{Parser, Subcommand}; use console::Emoji; +use embedded::EMBEDDED_FILES; use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use shlex::Shlex; use std::ffi::OsStr; -use std::fs; -use std::io::{self, prelude::*}; +use std::io::{self, prelude::*, stdin, stdout}; use std::path::Path; -use std::process::Command; +use std::process::{exit, Command}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::{Arc, Mutex}; @@ -54,7 +54,7 @@ enum Subcommands { /// The name of the exercise name: String, }, - /// Reset a single exercise using "git stash -- " + /// Reset a single exercise Reset { /// The name of the exercise name: String, @@ -83,13 +83,45 @@ enum Subcommands { #[arg(short, long)] solved: bool, }, - /// Enable rust-analyzer for exercises - Lsp, } fn main() -> Result<()> { let args = Args::parse(); + let exercises = toml_edit::de::from_str::(EMBEDDED_FILES.info_toml_content) + .unwrap() + .exercises; + + if !Path::new("exercises").is_dir() { + let mut stdout = stdout().lock(); + write!( + stdout, + "The `exercises` directory wasn't found in the current directory. +Do you want to initialize Rustlings in the current directory (y/n)? " + )?; + stdout.flush()?; + let mut answer = String::new(); + stdin().read_line(&mut answer)?; + answer.make_ascii_lowercase(); + if answer.trim() != "y" { + exit(1); + } + + EMBEDDED_FILES.init_exercises_dir()?; + if let Err(e) = write_project_json(&exercises) { + writeln!( + stdout, + "Failed to write rust-project.json to disk for rust-analyzer: {e}" + )?; + } else { + writeln!(stdout, "Successfully generated rust-project.json")?; + writeln!( + stdout, + "rust-analyzer will now parse exercises, restart your language server or editor" + )?; + } + } + if args.command.is_none() { println!("\n{WELCOME}\n"); } @@ -101,18 +133,6 @@ fn main() -> Result<()> { std::process::exit(1); } - let info_file = fs::read_to_string("info.toml").unwrap_or_else(|e| { - match e.kind() { - io::ErrorKind::NotFound => println!( - "The program must be run from the rustlings directory\nTry `cd rustlings/`!", - ), - _ => println!("Failed to read the info.toml file: {e}"), - } - std::process::exit(1); - }); - let exercises = toml_edit::de::from_str::(&info_file) - .unwrap() - .exercises; let verbose = args.nocapture; let command = args.command.unwrap_or_else(|| { @@ -205,7 +225,7 @@ fn main() -> Result<()> { Subcommands::Reset { name } => { let exercise = find_exercise(&name, &exercises); - reset(exercise).unwrap_or_else(|_| std::process::exit(1)); + reset(exercise)?; } Subcommands::Hint { name } => { @@ -219,15 +239,6 @@ fn main() -> Result<()> { .unwrap_or_else(|_| std::process::exit(1)); } - Subcommands::Lsp => { - if let Err(e) = write_project_json(exercises) { - println!("Failed to write rust-project.json to disk for rust-analyzer: {e}"); - } else { - println!("Successfully generated rust-project.json"); - println!("rust-analyzer will now parse exercises, restart your language server or editor"); - } - } - Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Err(e) => { println!("Error: Could not watch your progress. Error message was {e:?}."); diff --git a/src/project.rs b/src/project.rs index 0f56de9667..bb6caa5849 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use serde::Serialize; use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use crate::exercise::Exercise; @@ -9,14 +9,14 @@ use crate::exercise::Exercise; /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file #[derive(Serialize)] -struct RustAnalyzerProject { +struct RustAnalyzerProject<'a> { sysroot_src: PathBuf, - crates: Vec, + crates: Vec>, } #[derive(Serialize)] -struct Crate { - root_module: PathBuf, +struct Crate<'a> { + root_module: &'a Path, edition: &'static str, // Not used, but required in the JSON file. deps: Vec<()>, @@ -25,12 +25,12 @@ struct Crate { cfg: [&'static str; 1], } -impl RustAnalyzerProject { - fn build(exercises: Vec) -> Result { +impl<'a> RustAnalyzerProject<'a> { + fn build(exercises: &'a [Exercise]) -> Result { let crates = exercises - .into_iter() + .iter() .map(|exercise| Crate { - root_module: exercise.path, + root_module: &exercise.path, edition: "2021", deps: Vec::new(), // This allows rust_analyzer to work inside `#[test]` blocks @@ -69,7 +69,7 @@ impl RustAnalyzerProject { } /// Write `rust-project.json` to disk. -pub fn write_project_json(exercises: Vec) -> Result<()> { +pub fn write_project_json(exercises: &[Exercise]) -> Result<()> { let content = RustAnalyzerProject::build(exercises)?; // Using the capacity 2^14 since the file length in bytes is higher than 2^13. diff --git a/src/run.rs b/src/run.rs index 6dd0388f8d..792bd8fd79 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,6 +1,7 @@ -use std::process::Command; +use std::io; use std::time::Duration; +use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, Mode}; use crate::verify::test; use indicatif::ProgressBar; @@ -19,17 +20,8 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> { } // Resets the exercise by stashing the changes. -pub fn reset(exercise: &Exercise) -> Result<(), ()> { - let command = Command::new("git") - .arg("stash") - .arg("--") - .arg(&exercise.path) - .spawn(); - - match command { - Ok(_) => Ok(()), - Err(_) => Err(()), - } +pub fn reset(exercise: &Exercise) -> io::Result<()> { + EMBEDDED_FILES.write_exercise_to_disk(&exercise.path, WriteStrategy::Overwrite) } // Invoke the rust compiler on the path of the given exercise From 3959570221c88bf7bebbc7427236ae5c90d9d630 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 29 Mar 2024 01:25:21 +0100 Subject: [PATCH 0578/1432] Bump version to v6 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e432072d8f..d8e5b723d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,7 +564,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "5.6.1" +version = "6.0.0" dependencies = [ "anyhow", "assert_cmd", @@ -585,7 +585,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "5.6.1" +version = "6.0.0" dependencies = [ "quote", ] diff --git a/Cargo.toml b/Cargo.toml index e08be8bfb4..690aecc4fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" [workspace.package] -version = "5.6.1" +version = "6.0.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", From 0f18d599e92189d5f3ba085dcb4c8c4da9c7f584 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 29 Mar 2024 01:25:32 +0100 Subject: [PATCH 0579/1432] Add panic = "abort" --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 690aecc4fd..9224364ba0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,9 @@ winnow = "0.6.5" assert_cmd = "2.0.14" glob = "0.3.0" predicates = "3.1.0" + +[profile.release] +panic = "abort" + +[profile.dev] +panic = "abort" From 36a8e3ac0ee4f59ed587725e3257a79129a981e2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 29 Mar 2024 01:29:41 +0100 Subject: [PATCH 0580/1432] Replace rust-project.json with Cargo.toml --- src/init.rs | 75 ++++++++++++++++++++++++++++++++++ src/main.rs | 108 +++++++++++++++++++++---------------------------- src/project.rs | 83 ------------------------------------- 3 files changed, 122 insertions(+), 144 deletions(-) create mode 100644 src/init.rs delete mode 100644 src/project.rs diff --git a/src/init.rs b/src/init.rs new file mode 100644 index 0000000000..66535354ed --- /dev/null +++ b/src/init.rs @@ -0,0 +1,75 @@ +use anyhow::{bail, Context, Result}; +use std::{ + env::set_current_dir, + fs::{create_dir, OpenOptions}, + io::{self, ErrorKind, Write}, + path::Path, +}; + +use crate::{embedded::EMBEDDED_FILES, exercise::Exercise}; + +fn create_cargo_toml(exercises: &[Exercise]) -> io::Result<()> { + let mut cargo_toml = Vec::with_capacity(1 << 13); + cargo_toml.extend_from_slice( + br#"[package] +name = "rustlings" +version = "0.0.0" +edition = "2021" +publish = false +"#, + ); + for exercise in exercises { + cargo_toml.extend_from_slice(b"\n[[bin]]\nname = \""); + cargo_toml.extend_from_slice(exercise.name.as_bytes()); + cargo_toml.extend_from_slice(b"\"\npath = \""); + cargo_toml.extend_from_slice(exercise.path.to_str().unwrap().as_bytes()); + cargo_toml.extend_from_slice(b"\"\n"); + } + OpenOptions::new() + .create_new(true) + .write(true) + .open("Cargo.toml")? + .write_all(&cargo_toml) +} + +fn create_vscode_dir() -> Result<()> { + create_dir(".vscode").context("Failed to create the directory `.vscode`")?; + let vs_code_extensions_json = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; + OpenOptions::new() + .create_new(true) + .write(true) + .open(".vscode/extensions.json")? + .write_all(vs_code_extensions_json)?; + + Ok(()) +} + +pub fn init_rustlings(exercises: &[Exercise]) -> Result<()> { + let rustlings_path = Path::new("rustlings"); + if let Err(e) = create_dir(rustlings_path) { + if e.kind() == ErrorKind::AlreadyExists { + bail!( + "A directory with the name `rustligs` already exists in the current directory. +You probably already initialized Rustlings. +Run `cd rustlings` +Then run `rustlings` again" + ); + } + return Err(e.into()); + } + + set_current_dir("rustlings") + .context("Failed to change the current directory to `rustlings`")?; + + EMBEDDED_FILES + .init_exercises_dir() + .context("Failed to initialize the `rustlings/exercises` directory")?; + + create_cargo_toml(exercises).context("Failed to create the file `rustlings/Cargo.toml`")?; + + create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?; + + println!("\nDone initialization!\n"); + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 822cd1adce..36c36b5458 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,7 @@ use crate::exercise::{Exercise, ExerciseList}; -use crate::project::write_project_json; use crate::run::{reset, run}; use crate::verify::verify; -use anyhow::Result; +use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use console::Emoji; use embedded::EMBEDDED_FILES; @@ -10,7 +9,7 @@ use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use shlex::Shlex; use std::ffi::OsStr; -use std::io::{self, prelude::*, stdin, stdout}; +use std::io::{self, prelude::*}; use std::path::Path; use std::process::{exit, Command}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -24,7 +23,7 @@ mod ui; mod embedded; mod exercise; -mod project; +mod init; mod run; mod verify; @@ -41,6 +40,8 @@ struct Args { #[derive(Subcommand)] enum Subcommands { + /// Initialize Rustlings + Init, /// Verify all exercises according to the recommended order Verify, /// Rerun `verify` when files were edited @@ -88,40 +89,6 @@ enum Subcommands { fn main() -> Result<()> { let args = Args::parse(); - let exercises = toml_edit::de::from_str::(EMBEDDED_FILES.info_toml_content) - .unwrap() - .exercises; - - if !Path::new("exercises").is_dir() { - let mut stdout = stdout().lock(); - write!( - stdout, - "The `exercises` directory wasn't found in the current directory. -Do you want to initialize Rustlings in the current directory (y/n)? " - )?; - stdout.flush()?; - let mut answer = String::new(); - stdin().read_line(&mut answer)?; - answer.make_ascii_lowercase(); - if answer.trim() != "y" { - exit(1); - } - - EMBEDDED_FILES.init_exercises_dir()?; - if let Err(e) = write_project_json(&exercises) { - writeln!( - stdout, - "Failed to write rust-project.json to disk for rust-analyzer: {e}" - )?; - } else { - writeln!(stdout, "Successfully generated rust-project.json")?; - writeln!( - stdout, - "rust-analyzer will now parse exercises, restart your language server or editor" - )?; - } - } - if args.command.is_none() { println!("\n{WELCOME}\n"); } @@ -133,14 +100,32 @@ Do you want to initialize Rustlings in the current directory (y/n)? " std::process::exit(1); } - let verbose = args.nocapture; + let exercises = toml_edit::de::from_str::(EMBEDDED_FILES.info_toml_content) + .unwrap() + .exercises; + if matches!(args.command, Some(Subcommands::Init)) { + init::init_rustlings(&exercises).context("Initialization failed")?; + println!("{DEFAULT_OUT}\n"); + return Ok(()); + } else if !Path::new("exercises").is_dir() { + println!( + "\nThe `exercises` directory wasn't found in the current directory. +If you are just starting with Rustlings and want to initialize it, +run the command `rustlings init`" + ); + exit(1); + } + + let verbose = args.nocapture; let command = args.command.unwrap_or_else(|| { println!("{DEFAULT_OUT}\n"); std::process::exit(0); }); match command { + // `Init` is handled above. + Subcommands::Init => (), Subcommands::List { paths, names, @@ -421,9 +406,16 @@ fn watch( } } -const DEFAULT_OUT: &str = "Thanks for installing Rustlings! +const WELCOME: &str = r" welcome to... + _ _ _ + _ __ _ _ ___| |_| (_)_ __ __ _ ___ + | '__| | | / __| __| | | '_ \ / _` / __| + | | | |_| \__ \ |_| | | | | | (_| \__ \ + |_| \__,_|___/\__|_|_|_| |_|\__, |___/ + |___/"; -Is this your first time? Don't worry, Rustlings was made for beginners! We are +const DEFAULT_OUT: &str = + "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: @@ -446,8 +438,20 @@ started, here's a couple of notes about how Rustlings operates: 5. If you want to use `rust-analyzer` with exercises, which provides features like autocompletion, run the command `rustlings lsp`. -Got all that? Great! To get started, run `rustlings watch` in order to get the first -exercise. Make sure to have your editor open!"; +Got all that? Great! To get started, go into the new directory `rustlings` by +running `cd rustlings`. +Then, run `rustlings watch` in order to get the first exercise. +Make sure to have your editor open in the new `rustlings` directory!"; + +const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: + hint - prints the current exercise's hint + clear - clears the screen + quit - quits watch mode + ! - executes a command, like `!rustc --explain E0381` + help - displays this help message + +Watch mode automatically re-evaluates the current exercise +when you edit a file's contents."; const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | @@ -475,21 +479,3 @@ You can also contribute your own exercises to help the greater community! Before reporting an issue or contributing, please read our guidelines: https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; - -const WELCOME: &str = r" welcome to... - _ _ _ - _ __ _ _ ___| |_| (_)_ __ __ _ ___ - | '__| | | / __| __| | | '_ \ / _` / __| - | | | |_| \__ \ |_| | | | | | (_| \__ \ - |_| \__,_|___/\__|_|_|_| |_|\__, |___/ - |___/"; - -const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: - hint - prints the current exercise's hint - clear - clears the screen - quit - quits watch mode - ! - executes a command, like `!rustc --explain E0381` - help - displays this help message - -Watch mode automatically re-evaluates the current exercise -when you edit a file's contents."; diff --git a/src/project.rs b/src/project.rs deleted file mode 100644 index bb6caa5849..0000000000 --- a/src/project.rs +++ /dev/null @@ -1,83 +0,0 @@ -use anyhow::{Context, Result}; -use serde::Serialize; -use std::env; -use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; - -use crate::exercise::Exercise; - -/// Contains the structure of resulting rust-project.json file -/// and functions to build the data required to create the file -#[derive(Serialize)] -struct RustAnalyzerProject<'a> { - sysroot_src: PathBuf, - crates: Vec>, -} - -#[derive(Serialize)] -struct Crate<'a> { - root_module: &'a Path, - edition: &'static str, - // Not used, but required in the JSON file. - deps: Vec<()>, - // Only `test` is used for all crates. - // Therefore, an array is used instead of a `Vec`. - cfg: [&'static str; 1], -} - -impl<'a> RustAnalyzerProject<'a> { - fn build(exercises: &'a [Exercise]) -> Result { - let crates = exercises - .iter() - .map(|exercise| Crate { - root_module: &exercise.path, - edition: "2021", - deps: Vec::new(), - // This allows rust_analyzer to work inside `#[test]` blocks - cfg: ["test"], - }) - .collect(); - - if let Some(path) = env::var_os("RUST_SRC_PATH") { - return Ok(Self { - sysroot_src: PathBuf::from(path), - crates, - }); - } - - let toolchain = Command::new("rustc") - .arg("--print") - .arg("sysroot") - .stderr(Stdio::inherit()) - .output() - .context("Failed to get the sysroot from `rustc`. Do you have `rustc` installed?")? - .stdout; - - let toolchain = - String::from_utf8(toolchain).context("The toolchain path is invalid UTF8")?; - let toolchain = toolchain.trim_end(); - println!("Determined toolchain: {toolchain}\n"); - - let mut sysroot_src = PathBuf::with_capacity(256); - sysroot_src.extend([toolchain, "lib", "rustlib", "src", "rust", "library"]); - - Ok(Self { - sysroot_src, - crates, - }) - } -} - -/// Write `rust-project.json` to disk. -pub fn write_project_json(exercises: &[Exercise]) -> Result<()> { - let content = RustAnalyzerProject::build(exercises)?; - - // Using the capacity 2^14 since the file length in bytes is higher than 2^13. - // The final length is not known exactly because it depends on the user's sysroot path, - // the current number of exercises etc. - let mut buf = Vec::with_capacity(1 << 14); - serde_json::to_writer(&mut buf, &content)?; - std::fs::write("rust-project.json", buf)?; - - Ok(()) -} From a561a0f7f0378ac98ee4f025f5023c320af794b8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 29 Mar 2024 01:51:08 +0100 Subject: [PATCH 0581/1432] Avoid reinitialization by mistake --- src/init.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/init.rs b/src/init.rs index 66535354ed..e640c258a4 100644 --- a/src/init.rs +++ b/src/init.rs @@ -45,6 +45,16 @@ fn create_vscode_dir() -> Result<()> { } pub fn init_rustlings(exercises: &[Exercise]) -> Result<()> { + if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { + bail!( + "A directory with the name `exercises` and a file with the name `Cargo.toml` already exist +in the current directory. It looks like Rustlings was already initialized here. +Run `rustlings` for instructions on getting started with the exercises. + +If you didn't already initialize Rustlings, please initialize it in another directory." + ); + } + let rustlings_path = Path::new("rustlings"); if let Err(e) = create_dir(rustlings_path) { if e.kind() == ErrorKind::AlreadyExists { From 2b01811fe9344fa4afdef95fb934745176cab1b2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 29 Mar 2024 01:51:22 +0100 Subject: [PATCH 0582/1432] Fix typo --- src/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index e640c258a4..1ec8484779 100644 --- a/src/init.rs +++ b/src/init.rs @@ -59,7 +59,7 @@ If you didn't already initialize Rustlings, please initialize it in another dire if let Err(e) = create_dir(rustlings_path) { if e.kind() == ErrorKind::AlreadyExists { bail!( - "A directory with the name `rustligs` already exists in the current directory. + "A directory with the name `rustlings` already exists in the current directory. You probably already initialized Rustlings. Run `cd rustlings` Then run `rustlings` again" From 8e3cc9d70c627ace4553e4fe62af3443e970e64f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 29 Mar 2024 01:52:05 +0100 Subject: [PATCH 0583/1432] Improve printed information --- src/init.rs | 2 -- src/main.rs | 17 ++++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/init.rs b/src/init.rs index 1ec8484779..1edcb23c33 100644 --- a/src/init.rs +++ b/src/init.rs @@ -79,7 +79,5 @@ Then run `rustlings` again" create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?; - println!("\nDone initialization!\n"); - Ok(()) } diff --git a/src/main.rs b/src/main.rs index 36c36b5458..76b63736c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,13 +106,16 @@ fn main() -> Result<()> { if matches!(args.command, Some(Subcommands::Init)) { init::init_rustlings(&exercises).context("Initialization failed")?; - println!("{DEFAULT_OUT}\n"); + println!( + "\nDone initialization!\n +Run `cd rustlings` to go into the generated directory. +Then run `rustlings` for further instructions on getting started." + ); return Ok(()); } else if !Path::new("exercises").is_dir() { println!( "\nThe `exercises` directory wasn't found in the current directory. -If you are just starting with Rustlings and want to initialize it, -run the command `rustlings init`" +If you are just starting with Rustlings, run the command `rustlings init` to initialize it." ); exit(1); } @@ -435,13 +438,9 @@ started, here's a couple of notes about how Rustlings operates: 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, and sometimes, other learners do too so you can help each other out! -5. If you want to use `rust-analyzer` with exercises, which provides features like - autocompletion, run the command `rustlings lsp`. -Got all that? Great! To get started, go into the new directory `rustlings` by -running `cd rustlings`. -Then, run `rustlings watch` in order to get the first exercise. -Make sure to have your editor open in the new `rustlings` directory!"; +Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. +Make sure to have your editor open in the `rustlings` directory!"; const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: hint - prints the current exercise's hint From c6597d0010b869109e4504069d3e1aaa6b6834e6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:03:57 +0000 Subject: [PATCH 0584/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 42c7dbfe95..d82a5caba7 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -384,6 +384,7 @@ authors. wznmickey
wznmickey

πŸ“– NicolasRoelandt
NicolasRoelandt

πŸ“– Josh Bouganim
Josh Bouganim

πŸ’» + Dan
Dan

πŸ’» From c2b7f458060e65f7ae041b0b6eb9eaba58eaea1b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:03:58 +0000 Subject: [PATCH 0585/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index be95fe9f63..36952f7e7e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2721,6 +2721,15 @@ "contributions": [ "code" ] + }, + { + "login": "loshz", + "name": "Dan", + "avatar_url": "https://avatars.githubusercontent.com/u/3449337?v=4", + "profile": "https://loshz.com", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 8, From fe7d775818021acee7d5ae40c5cf9fdac69b2122 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 30 Mar 2024 18:52:49 +0100 Subject: [PATCH 0586/1432] Remove the installation scripts --- install.ps1 | 94 --------------------------- install.sh | 184 ---------------------------------------------------- 2 files changed, 278 deletions(-) delete mode 100644 install.ps1 delete mode 100755 install.sh diff --git a/install.ps1 b/install.ps1 deleted file mode 100644 index 844b0134cd..0000000000 --- a/install.ps1 +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env pwsh - -#Requires -Version 5 -param($path = "$home/rustlings") - -Write-Host "Let's get you set up with Rustlings!" - -Write-Host "Checking requirements..." -if (Get-Command git -ErrorAction SilentlyContinue) { - Write-Host "SUCCESS: Git is installed" -} else { - Write-Host "WARNING: Git does not seem to be installed." - Write-Host "Please download Git using your package manager or over https://git-scm.com/!" - exit 1 -} - -if (Get-Command rustc -ErrorAction SilentlyContinue) { - Write-Host "SUCCESS: Rust is installed" -} else { - Write-Host "WARNING: Rust does not seem to be installed." - Write-Host "Please download Rust using https://rustup.rs!" - exit 1 -} - -if (Get-Command cargo -ErrorAction SilentlyContinue) { - Write-Host "SUCCESS: Cargo is installed" -} else { - Write-Host "WARNING: Cargo does not seem to be installed." - Write-Host "Please download Rust and Cargo using https://rustup.rs!" - exit 1 -} - -# Function that compares two versions strings v1 and v2 given in arguments (e.g 1.31 and 1.33.0). -# Returns 1 if v1 > v2, 0 if v1 == v2, 2 if v1 < v2. -function vercomp($v1, $v2) { - if ($v1 -eq $v2) { - return 0 - } - - $v1 = $v1.Replace(".", "0") - $v2 = $v2.Replace(".", "0") - if ($v1.Length -gt $v2.Length) { - $v2 = $v2.PadRight($v1.Length, "0") - } else { - $v1 = $v1.PadRight($v2.Length, "0") - } - - if ($v1 -gt $v2) { - return 1 - } else { - return 2 - } -} - -$rustVersion = $(rustc --version).Split(" ")[1] -$minRustVersion = "1.70" -if ((vercomp $rustVersion $minRustVersion) -eq 2) { - Write-Host "WARNING: Rust version is too old: $rustVersion - needs at least $minRustVersion" - Write-Host "Please update Rust with 'rustup update'" - exit 1 -} else { - Write-Host "SUCCESS: Rust is up to date" -} - -Write-Host "Cloning Rustlings at $path" -git clone -q https://github.com/rust-lang/rustlings $path -if (!($LASTEXITCODE -eq 0)) { - exit 1 -} - -# UseBasicParsing is deprecated, pwsh 6 or above will automatically use it, -# but anyone running pwsh 5 will have to pass the argument. -$version = Invoke-WebRequest -UseBasicParsing https://api.github.com/repos/rust-lang/rustlings/releases/latest ` - | ConvertFrom-Json | Select-Object -ExpandProperty tag_name - -Write-Host "Checking out version $version..." -Set-Location $path -git checkout -q tags/$version - -Write-Host "Installing the 'rustlings' executable..." -cargo install --force --path . -if (!(Get-Command rustlings -ErrorAction SilentlyContinue)) { - Write-Host "WARNING: Please check that you have '~/.cargo/bin' in your PATH environment variable!" -} - -# Checking whether Clippy is installed. -# Due to a bug in Cargo, this must be done with Rustup: https://github.com/rust-lang/rustup/issues/1514 -$clippy = (rustup component list | Select-String "clippy" | Select-String "installed") | Out-String -if (!$clippy) { - Write-Host "Installing the 'cargo-clippy' executable..." - rustup component add clippy -} - -Write-Host "All done! Navigate to $path and run 'rustlings' to get started!" diff --git a/install.sh b/install.sh deleted file mode 100755 index fdbe8d4386..0000000000 --- a/install.sh +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -echo -e "\nLet's get you set up with Rustlings!" - -echo "Checking requirements..." -if [ -x "$(command -v git)" ] -then - echo "SUCCESS: Git is installed" -else - echo "ERROR: Git does not seem to be installed." - echo "Please download Git using your package manager or over https://git-scm.com/!" - exit 1 -fi - -if [ -x "$(command -v cc)" ] -then - echo "SUCCESS: cc is installed" -else - echo "ERROR: cc does not seem to be installed." - echo "Please download (g)cc using your package manager." - echo "OSX: xcode-select --install" - echo "Deb: sudo apt install gcc" - echo "Yum: sudo yum -y install gcc" - exit 1 -fi - -if [ -x "$(command -v rustup)" ] -then - echo "SUCCESS: rustup is installed" -else - echo "ERROR: rustup does not seem to be installed." - echo "Please download rustup using https://rustup.rs!" - exit 1 -fi - -if [ -x "$(command -v rustc)" ] -then - echo "SUCCESS: Rust is installed" -else - echo "ERROR: Rust does not seem to be installed." - echo "Please download Rust using rustup!" - exit 1 -fi - -if [ -x "$(command -v cargo)" ] -then - echo "SUCCESS: Cargo is installed" -else - echo "ERROR: Cargo does not seem to be installed." - echo "Please download Rust and Cargo using rustup!" - exit 1 -fi - -# Look up python installations, starting with 3 with a fallback of 2 -if [ -x "$(command -v python3)" ] -then - PY="$(command -v python3)" -elif [ -x "$(command -v python)" ] -then - PY="$(command -v python)" -elif [ -x "$(command -v python2)" ] -then - PY="$(command -v python2)" -else - echo "ERROR: No working python installation was found" - echo "Please install python and add it to the PATH variable" - exit 1 -fi - -# Function that compares two versions strings v1 and v2 given in arguments (e.g 1.31 and 1.33.0). -# Returns 1 if v1 > v2, 0 if v1 == v2, 2 if v1 < v2. -function vercomp() { - if [[ $1 == $2 ]] - then - return 0 - fi - v1=( ${1//./ } ) - v2=( ${2//./ } ) - len1=${#v1[@]} - len2=${#v2[@]} - max_len=$len1 - if [[ $max_len -lt $len2 ]] - then - max_len=$len2 - fi - - #pad right in short arr - if [[ len1 -gt len2 ]]; - then - for ((i = len2; i < len1; i++)); - do - v2[$i]=0 - done - else - for ((i = len1; i < len2; i++)); - do - v1[$i]=0 - done - fi - - for i in `seq 0 $((max_len-1))` - do - # Fill empty fields with zeros in v1 - if [ -z "${v1[$i]}" ] - then - v1[$i]=0 - fi - # And in v2 - if [ -z "${v2[$i]}" ] - then - v2[$i]=0 - fi - if [ ${v1[$i]} -gt ${v2[$i]} ] - then - return 1 - fi - if [ ${v1[$i]} -lt ${v2[$i]} ] - then - return 2 - fi - done - return 0 -} - -RustVersion=$(rustc --version | cut -d " " -f 2) -MinRustVersion=1.70 -vercomp "$RustVersion" $MinRustVersion || ec=$? -if [ ${ec:-0} -eq 2 ] -then - echo "ERROR: Rust version is too old: $RustVersion - needs at least $MinRustVersion" - echo "Please update Rust with 'rustup update'" - exit 1 -else - echo "SUCCESS: Rust is up to date" -fi - -Path=${1:-rustlings/} -echo "Cloning Rustlings at $Path..." -git clone -q https://github.com/rust-lang/rustlings.git "$Path" - -cd "$Path" - -Version=$(curl -s https://api.github.com/repos/rust-lang/rustlings/releases/latest | ${PY} -c "import json,sys;obj=json.load(sys.stdin);print(obj['tag_name']) if 'tag_name' in obj else sys.exit(f\"Error: {obj['message']}\");") -CargoBin="${CARGO_HOME:-$HOME/.cargo}/bin" - -if [[ -z ${Version} ]] -then - echo "The latest tag version could not be fetched remotely." - echo "Using the local git repository..." - Version=$(ls -tr .git/refs/tags/ | tail -1) - if [[ -z ${Version} ]] - then - echo "No valid tag version found" - echo "Rustlings will be installed using the main branch" - Version="main" - else - Version="tags/${Version}" - fi -else - Version="tags/${Version}" -fi - -echo "Checking out version $Version..." -git checkout -q ${Version} - -echo "Installing the 'rustlings' executable..." -cargo install --force --path . - -if ! [ -x "$(command -v rustlings)" ] -then - echo "WARNING: Please check that you have '$CargoBin' in your PATH environment variable!" -fi - -# Checking whether Clippy is installed. -# Due to a bug in Cargo, this must be done with Rustup: https://github.com/rust-lang/rustup/issues/1514 -Clippy=$(rustup component list | grep "clippy" | grep "installed") -if [ -z "$Clippy" ] -then - echo "Installing the 'cargo-clippy' executable..." - rustup component add clippy -fi - -echo "All done! Run 'rustlings' to get started." From 79ca821e26711123c959e919eed2a630fa102cd5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 30 Mar 2024 20:48:30 +0100 Subject: [PATCH 0587/1432] Fix tests --- src/exercise.rs | 10 +++++----- src/main.rs | 12 +++++++++--- tests/fixture/failure/{ => exercises}/compFailure.rs | 0 .../failure/{ => exercises}/compNoExercise.rs | 0 tests/fixture/failure/{ => exercises}/testFailure.rs | 0 .../fixture/failure/{ => exercises}/testNotPassed.rs | 0 tests/fixture/failure/info.toml | 4 ++-- .../state/{ => exercises}/finished_exercise.rs | 0 .../state/{ => exercises}/pending_exercise.rs | 0 .../state/{ => exercises}/pending_test_exercise.rs | 0 tests/fixture/state/info.toml | 7 +++---- tests/fixture/success/{ => exercises}/compSuccess.rs | 0 tests/fixture/success/{ => exercises}/testSuccess.rs | 0 tests/fixture/success/info.toml | 4 ++-- 14 files changed, 21 insertions(+), 16 deletions(-) rename tests/fixture/failure/{ => exercises}/compFailure.rs (100%) rename tests/fixture/failure/{ => exercises}/compNoExercise.rs (100%) rename tests/fixture/failure/{ => exercises}/testFailure.rs (100%) rename tests/fixture/failure/{ => exercises}/testNotPassed.rs (100%) rename tests/fixture/state/{ => exercises}/finished_exercise.rs (100%) rename tests/fixture/state/{ => exercises}/pending_exercise.rs (100%) rename tests/fixture/state/{ => exercises}/pending_test_exercise.rs (100%) rename tests/fixture/success/{ => exercises}/compSuccess.rs (100%) rename tests/fixture/success/{ => exercises}/testSuccess.rs (100%) diff --git a/src/exercise.rs b/src/exercise.rs index 7c2e5fde16..1125916880 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -354,7 +354,7 @@ mod test { File::create(temp_file()).unwrap(); let exercise = Exercise { name: String::from("example"), - path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), + path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"), mode: Mode::Compile, hint: String::from(""), }; @@ -372,7 +372,7 @@ mod test { let exercise = Exercise { name: String::from("example"), // We want a file that does actually compile - path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), + path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"), mode: *mode, hint: String::from(""), }; @@ -385,7 +385,7 @@ mod test { fn test_pending_state() { let exercise = Exercise { name: "pending_exercise".into(), - path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), + path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"), mode: Mode::Compile, hint: String::new(), }; @@ -426,7 +426,7 @@ mod test { fn test_finished_exercise() { let exercise = Exercise { name: "finished_exercise".into(), - path: PathBuf::from("tests/fixture/state/finished_exercise.rs"), + path: PathBuf::from("tests/fixture/state/exercises/finished_exercise.rs"), mode: Mode::Compile, hint: String::new(), }; @@ -438,7 +438,7 @@ mod test { fn test_exercise_with_output() { let exercise = Exercise { name: "exercise_with_output".into(), - path: PathBuf::from("tests/fixture/success/testSuccess.rs"), + path: PathBuf::from("tests/fixture/success/exercises/testSuccess.rs"), mode: Mode::Test, hint: String::new(), }; diff --git a/src/main.rs b/src/main.rs index 76b63736c3..2ac44d55ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use shlex::Shlex; use std::ffi::OsStr; +use std::fs; use std::io::{self, prelude::*}; use std::path::Path; use std::process::{exit, Command}; @@ -100,9 +101,14 @@ fn main() -> Result<()> { std::process::exit(1); } - let exercises = toml_edit::de::from_str::(EMBEDDED_FILES.info_toml_content) - .unwrap() - .exercises; + // Read a local `info.toml` if it exists. Mainly to let the tests work for now. + let exercises = if let Ok(file_content) = fs::read_to_string("info.toml") { + toml_edit::de::from_str::(&file_content) + } else { + toml_edit::de::from_str::(EMBEDDED_FILES.info_toml_content) + } + .context("Failed to parse `info.toml`")? + .exercises; if matches!(args.command, Some(Subcommands::Init)) { init::init_rustlings(&exercises).context("Initialization failed")?; diff --git a/tests/fixture/failure/compFailure.rs b/tests/fixture/failure/exercises/compFailure.rs similarity index 100% rename from tests/fixture/failure/compFailure.rs rename to tests/fixture/failure/exercises/compFailure.rs diff --git a/tests/fixture/failure/compNoExercise.rs b/tests/fixture/failure/exercises/compNoExercise.rs similarity index 100% rename from tests/fixture/failure/compNoExercise.rs rename to tests/fixture/failure/exercises/compNoExercise.rs diff --git a/tests/fixture/failure/testFailure.rs b/tests/fixture/failure/exercises/testFailure.rs similarity index 100% rename from tests/fixture/failure/testFailure.rs rename to tests/fixture/failure/exercises/testFailure.rs diff --git a/tests/fixture/failure/testNotPassed.rs b/tests/fixture/failure/exercises/testNotPassed.rs similarity index 100% rename from tests/fixture/failure/testNotPassed.rs rename to tests/fixture/failure/exercises/testNotPassed.rs diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index e5949f9be1..9474ee3f21 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -1,11 +1,11 @@ [[exercises]] name = "compFailure" -path = "compFailure.rs" +path = "exercises/compFailure.rs" mode = "compile" hint = "" [[exercises]] name = "testFailure" -path = "testFailure.rs" +path = "exercises/testFailure.rs" mode = "test" hint = "Hello!" diff --git a/tests/fixture/state/finished_exercise.rs b/tests/fixture/state/exercises/finished_exercise.rs similarity index 100% rename from tests/fixture/state/finished_exercise.rs rename to tests/fixture/state/exercises/finished_exercise.rs diff --git a/tests/fixture/state/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs similarity index 100% rename from tests/fixture/state/pending_exercise.rs rename to tests/fixture/state/exercises/pending_exercise.rs diff --git a/tests/fixture/state/pending_test_exercise.rs b/tests/fixture/state/exercises/pending_test_exercise.rs similarity index 100% rename from tests/fixture/state/pending_test_exercise.rs rename to tests/fixture/state/exercises/pending_test_exercise.rs diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index 547b3a48e7..8de5d604f9 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -1,18 +1,17 @@ [[exercises]] name = "pending_exercise" -path = "pending_exercise.rs" +path = "exercises/pending_exercise.rs" mode = "compile" hint = """""" [[exercises]] name = "pending_test_exercise" -path = "pending_test_exercise.rs" +path = "exercises/pending_test_exercise.rs" mode = "test" hint = """""" [[exercises]] name = "finished_exercise" -path = "finished_exercise.rs" +path = "exercises/finished_exercise.rs" mode = "compile" hint = """""" - diff --git a/tests/fixture/success/compSuccess.rs b/tests/fixture/success/exercises/compSuccess.rs similarity index 100% rename from tests/fixture/success/compSuccess.rs rename to tests/fixture/success/exercises/compSuccess.rs diff --git a/tests/fixture/success/testSuccess.rs b/tests/fixture/success/exercises/testSuccess.rs similarity index 100% rename from tests/fixture/success/testSuccess.rs rename to tests/fixture/success/exercises/testSuccess.rs diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index 68d35388ee..17ed8c6229 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -1,11 +1,11 @@ [[exercises]] name = "compSuccess" -path = "compSuccess.rs" +path = "exercises/compSuccess.rs" mode = "compile" hint = """""" [[exercises]] name = "testSuccess" -path = "testSuccess.rs" +path = "exercises/testSuccess.rs" mode = "test" hint = """""" From 23f0fae1c8eddfa1ac679d8167ec63b554c554b9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 30 Mar 2024 21:13:28 +0100 Subject: [PATCH 0588/1432] Show a success message after resetting --- src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2ac44d55ab..1926f6acb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -212,19 +212,17 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini Subcommands::Run { name } => { let exercise = find_exercise(&name, &exercises); - run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1)); } Subcommands::Reset { name } => { let exercise = find_exercise(&name, &exercises); - reset(exercise)?; + println!("The file {} has been reset!", exercise.path.display()); } Subcommands::Hint { name } => { let exercise = find_exercise(&name, &exercises); - println!("{}", exercise.hint); } From b5e17c965d1fee01336fdfabd93c575555a44d62 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 30 Mar 2024 21:15:11 +0100 Subject: [PATCH 0589/1432] Add an error message when a file is not embedded --- src/embedded.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/embedded.rs b/src/embedded.rs index 25dbe64108..f65b8aef01 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -113,6 +113,9 @@ impl EmbeddedFiles { } } - Err(io::Error::from(io::ErrorKind::NotFound)) + Err(io::Error::new( + io::ErrorKind::NotFound, + format!("{} not found in the embedded files", path.display()), + )) } } From 1e1f0317134fc3588f2eea4a118bd72aba3f9b34 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 00:49:19 +0100 Subject: [PATCH 0590/1432] Fix path comparison --- src/embedded.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index f65b8aef01..56b4b61833 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -97,17 +97,13 @@ impl EmbeddedFiles { .exercises_dir .files .iter() - .find(|file| file.path == path.as_os_str()) + .find(|file| Path::new(file.path) == path) { return file.write_to_disk(strategy); } for dir in self.exercises_dir.dirs { - if let Some(file) = dir - .content - .iter() - .find(|file| file.path == path.as_os_str()) - { + if let Some(file) = dir.content.iter().find(|file| Path::new(file.path) == path) { dir.init_on_disk()?; return file.write_to_disk(strategy); } From b711dd692afaf42830efb04c491616d3f069fbdf Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 02:04:41 +0100 Subject: [PATCH 0591/1432] Add .gitignore --- src/init.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/init.rs b/src/init.rs index 1edcb23c33..d958c96e15 100644 --- a/src/init.rs +++ b/src/init.rs @@ -32,6 +32,15 @@ publish = false .write_all(&cargo_toml) } +fn create_gitignore() -> io::Result<()> { + let gitignore = b"/target"; + 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`")?; let vs_code_extensions_json = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; @@ -77,6 +86,8 @@ Then run `rustlings` again" create_cargo_toml(exercises).context("Failed to create the file `rustlings/Cargo.toml`")?; + create_gitignore().context("Failed to create the file `rustlings/.gitignore`")?; + create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?; Ok(()) From 82b563f1654860ba3590d91ec3c0f321e3130ae2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 16:55:33 +0200 Subject: [PATCH 0592/1432] Use Cargo instead of rustc --- src/exercise.rs | 260 +++++++++++------------------------------------- src/main.rs | 25 ++--- src/run.rs | 53 +++------- src/verify.rs | 121 ++++++++++------------ 4 files changed, 132 insertions(+), 327 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 1125916880..83d444fc2d 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,21 +1,21 @@ +use anyhow::{Context, Result}; use serde::Deserialize; -use std::fmt::{self, Display, Formatter}; -use std::fs::{self, remove_file, File}; +use std::fmt::{self, Debug, Display, Formatter}; +use std::fs::{self, File}; use std::io::{self, BufRead, BufReader}; use std::path::PathBuf; -use std::process::{self, exit, Command, Stdio}; -use std::{array, env, mem}; +use std::process::{exit, Command, Output}; +use std::{array, mem}; use winnow::ascii::{space0, Caseless}; use winnow::combinator::opt; use winnow::Parser; -const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; -const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; -const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; +use crate::embedded::EMBEDDED_FILES; + +// The number of context lines above and below a highlighted line. const CONTEXT: usize = 2; -const CLIPPY_CARGO_TOML_PATH: &str = "exercises/22_clippy/Cargo.toml"; -// Checks if the line contains the "I AM NOT DONE" comment. +// Check if the line contains the "I AM NOT DONE" comment. fn contains_not_done_comment(input: &str) -> bool { ( space0::<_, ()>, @@ -28,26 +28,15 @@ fn contains_not_done_comment(input: &str) -> bool { .is_ok() } -// Get a temporary file name that is hopefully unique -#[inline] -fn temp_file() -> String { - let thread_id: String = format!("{:?}", std::thread::current().id()) - .chars() - .filter(|c| c.is_alphanumeric()) - .collect(); - - format!("./temp_{}_{thread_id}", process::id()) -} - // The mode of the exercise. -#[derive(Deserialize, Copy, Clone, Debug)] +#[derive(Deserialize, Copy, Clone)] #[serde(rename_all = "lowercase")] pub enum Mode { - // Indicates that the exercise should be compiled as a binary + // The exercise should be compiled as a binary Compile, - // Indicates that the exercise should be compiled as a test harness + // The exercise should be compiled as a test harness Test, - // Indicates that the exercise should be linted with clippy + // The exercise should be linted with clippy Clippy, } @@ -56,171 +45,72 @@ pub struct ExerciseList { pub exercises: Vec, } -// A representation of a rustlings exercise. -// This is deserialized from the accompanying info.toml file -#[derive(Deserialize, Debug)] +impl ExerciseList { + pub fn parse() -> Result { + // Read a local `info.toml` if it exists. + // Mainly to let the tests work for now. + if let Ok(file_content) = fs::read_to_string("info.toml") { + toml_edit::de::from_str(&file_content) + } else { + toml_edit::de::from_str(EMBEDDED_FILES.info_toml_content) + } + .context("Failed to parse `info.toml`") + } +} + +// Deserialized from the `info.toml` file. +#[derive(Deserialize)] pub struct Exercise { // Name of the exercise pub name: String, // The path to the file containing the exercise's source code pub path: PathBuf, - // The mode of the exercise (Test, Compile, or Clippy) + // The mode of the exercise pub mode: Mode, // The hint text associated with the exercise pub hint: String, } -// An enum to track of the state of an Exercise. -// An Exercise can be either Done or Pending +// The state of an Exercise. #[derive(PartialEq, Eq, Debug)] pub enum State { - // The state of the exercise once it's been completed Done, - // The state of the exercise while it's not completed yet Pending(Vec), } -// The context information of a pending exercise +// The context information of a pending exercise. #[derive(PartialEq, Eq, Debug)] pub struct ContextLine { - // The source code that is still pending completion + // The source code line pub line: String, - // The line number of the source code still pending completion + // The line number pub number: usize, - // Whether or not this is important + // Whether this is important and should be highlighted pub important: bool, } -// The result of compiling an exercise -pub struct CompiledExercise<'a> { - exercise: &'a Exercise, - _handle: FileHandle, -} - -impl<'a> CompiledExercise<'a> { - // Run the compiled exercise - pub fn run(&self) -> Result { - self.exercise.run() - } -} - -// A representation of an already executed binary -#[derive(Debug)] -pub struct ExerciseOutput { - // The textual contents of the standard output of the binary - pub stdout: String, - // The textual contents of the standard error of the binary - pub stderr: String, -} - -struct FileHandle; - -impl Drop for FileHandle { - fn drop(&mut self) { - clean(); - } -} - impl Exercise { - pub fn compile(&self) -> Result { - let cmd = match self.mode { - Mode::Compile => Command::new("rustc") - .args([self.path.to_str().unwrap(), "-o", &temp_file()]) - .args(RUSTC_COLOR_ARGS) - .args(RUSTC_EDITION_ARGS) - .args(RUSTC_NO_DEBUG_ARGS) - .output(), - Mode::Test => Command::new("rustc") - .args(["--test", self.path.to_str().unwrap(), "-o", &temp_file()]) - .args(RUSTC_COLOR_ARGS) - .args(RUSTC_EDITION_ARGS) - .args(RUSTC_NO_DEBUG_ARGS) - .output(), - Mode::Clippy => { - let cargo_toml = format!( - r#"[package] -name = "{}" -version = "0.0.1" -edition = "2021" -[[bin]] -name = "{}" -path = "{}.rs""#, - self.name, self.name, self.name - ); - let cargo_toml_error_msg = if env::var("NO_EMOJI").is_ok() { - "Failed to write Clippy Cargo.toml file." - } else { - "Failed to write πŸ“Ž Clippy πŸ“Ž Cargo.toml file." - }; - fs::write(CLIPPY_CARGO_TOML_PATH, cargo_toml).expect(cargo_toml_error_msg); - // To support the ability to run the clippy exercises, build - // an executable, in addition to running clippy. With a - // compilation failure, this would silently fail. But we expect - // clippy to reflect the same failure while compiling later. - Command::new("rustc") - .args([self.path.to_str().unwrap(), "-o", &temp_file()]) - .args(RUSTC_COLOR_ARGS) - .args(RUSTC_EDITION_ARGS) - .args(RUSTC_NO_DEBUG_ARGS) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .expect("Failed to compile!"); - // Due to an issue with Clippy, a cargo clean is required to catch all lints. - // See https://github.com/rust-lang/rust-clippy/issues/2604 - // This is already fixed on Clippy's master branch. See this issue to track merging into Cargo: - // https://github.com/rust-lang/rust-clippy/issues/3837 - Command::new("cargo") - .args(["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) - .args(RUSTC_COLOR_ARGS) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .expect("Failed to run 'cargo clean'"); - Command::new("cargo") - .args(["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) - .args(RUSTC_COLOR_ARGS) - .args(["--", "-D", "warnings", "-D", "clippy::float_cmp"]) - .output() - } - } - .expect("Failed to run 'compile' command."); - - if cmd.status.success() { - Ok(CompiledExercise { - exercise: self, - _handle: FileHandle, - }) - } else { - clean(); - Err(ExerciseOutput { - stdout: String::from_utf8_lossy(&cmd.stdout).to_string(), - stderr: String::from_utf8_lossy(&cmd.stderr).to_string(), - }) - } - } - - fn run(&self) -> Result { - let arg = match self.mode { - Mode::Test => "--show-output", - _ => "", - }; - let cmd = Command::new(temp_file()) - .arg(arg) + fn cargo_cmd(&self, command: &str, args: &[&str]) -> Result { + Command::new("cargo") + .arg(command) + .arg("--color") + .arg("always") + .arg("-q") + .arg("--bin") + .arg(&self.name) + .args(args) .output() - .expect("Failed to run 'run' command"); - - let output = ExerciseOutput { - stdout: String::from_utf8_lossy(&cmd.stdout).to_string(), - stderr: String::from_utf8_lossy(&cmd.stderr).to_string(), - }; + .context("Failed to run Cargo") + } - if cmd.status.success() { - Ok(output) - } else { - Err(output) + pub fn run(&self) -> Result { + match self.mode { + Mode::Compile => self.cargo_cmd("run", &[]), + Mode::Test => self.cargo_cmd("test", &["--", "--nocapture"]), + Mode::Clippy => self.cargo_cmd( + "clippy", + &["--", "-D", "warnings", "-D", "clippy::float_cmp"], + ), } } @@ -335,51 +225,13 @@ path = "{}.rs""#, impl Display for Exercise { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.path.to_str().unwrap()) + self.path.fmt(f) } } -#[inline] -fn clean() { - let _ignored = remove_file(temp_file()); -} - #[cfg(test)] mod test { use super::*; - use std::path::Path; - - #[test] - fn test_clean() { - File::create(temp_file()).unwrap(); - let exercise = Exercise { - name: String::from("example"), - path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"), - mode: Mode::Compile, - hint: String::from(""), - }; - let compiled = exercise.compile().unwrap(); - drop(compiled); - assert!(!Path::new(&temp_file()).exists()); - } - - #[test] - #[cfg(target_os = "windows")] - fn test_no_pdb_file() { - [Mode::Compile, Mode::Test] // Clippy doesn't like to test - .iter() - .for_each(|mode| { - let exercise = Exercise { - name: String::from("example"), - // We want a file that does actually compile - path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"), - mode: *mode, - hint: String::from(""), - }; - let _ = exercise.compile().unwrap(); - assert!(!Path::new(&format!("{}.pdb", temp_file())).exists()); - }); - } #[test] fn test_pending_state() { @@ -442,8 +294,8 @@ mod test { mode: Mode::Test, hint: String::new(), }; - let out = exercise.compile().unwrap().run().unwrap(); - assert!(out.stdout.contains("THIS TEST TOO SHALL PASS")); + let out = exercise.run().unwrap(); + assert_eq!(out.stdout, b"THIS TEST TOO SHALL PASS"); } #[test] diff --git a/src/main.rs b/src/main.rs index 1926f6acb0..1c736f3125 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,20 +4,18 @@ use crate::verify::verify; use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use console::Emoji; -use embedded::EMBEDDED_FILES; use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use shlex::Shlex; use std::ffi::OsStr; -use std::fs; -use std::io::{self, prelude::*}; +use std::io::{BufRead, Write}; use std::path::Path; use std::process::{exit, Command}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::{Arc, Mutex}; -use std::thread; use std::time::Duration; +use std::{io, thread}; #[macro_use] mod ui; @@ -94,21 +92,16 @@ fn main() -> Result<()> { println!("\n{WELCOME}\n"); } - if which::which("rustc").is_err() { - println!("We cannot find `rustc`."); - println!("Try running `rustc --version` to diagnose your problem."); - println!("For instructions on how to install Rust, check the README."); + if which::which("cargo").is_err() { + println!( + "Failed to find `cargo`. +Did you already install Rust? +Try running `cargo --version` to diagnose the problem." + ); std::process::exit(1); } - // Read a local `info.toml` if it exists. Mainly to let the tests work for now. - let exercises = if let Ok(file_content) = fs::read_to_string("info.toml") { - toml_edit::de::from_str::(&file_content) - } else { - toml_edit::de::from_str::(EMBEDDED_FILES.info_toml_content) - } - .context("Failed to parse `info.toml`")? - .exercises; + let exercises = ExerciseList::parse()?.exercises; if matches!(args.command, Some(Subcommands::Init)) { init::init_rustlings(&exercises).context("Initialization failed")?; diff --git a/src/run.rs b/src/run.rs index 792bd8fd79..2c9f99f621 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,4 +1,5 @@ -use std::io; +use anyhow::{bail, Result}; +use std::io::{self, stdout, Write}; use std::time::Duration; use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; @@ -10,13 +11,11 @@ use indicatif::ProgressBar; // and run the ensuing binary. // The verbose argument helps determine whether or not to show // the output from the test harnesses (if the mode of the exercise is test) -pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> { +pub fn run(exercise: &Exercise, verbose: bool) -> Result<()> { match exercise.mode { - Mode::Test => test(exercise, verbose)?, - Mode::Compile => compile_and_run(exercise)?, - Mode::Clippy => compile_and_run(exercise)?, + Mode::Test => test(exercise, verbose), + Mode::Compile | Mode::Clippy => compile_and_run(exercise), } - Ok(()) } // Resets the exercise by stashing the changes. @@ -27,41 +26,21 @@ pub fn reset(exercise: &Exercise) -> io::Result<()> { // Invoke the rust compiler on the path of the given exercise // and run the ensuing binary. // This is strictly for non-test binaries, so output is displayed -fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { +fn compile_and_run(exercise: &Exercise) -> Result<()> { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {exercise}...")); + progress_bar.set_message(format!("Running {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); - let compilation_result = exercise.compile(); - let compilation = match compilation_result { - Ok(compilation) => compilation, - Err(output) => { - progress_bar.finish_and_clear(); - warn!( - "Compilation of {} failed!, Compiler error message:\n", - exercise - ); - println!("{}", output.stderr); - return Err(()); - } - }; - - progress_bar.set_message(format!("Running {exercise}...")); - let result = compilation.run(); + let output = exercise.run()?; progress_bar.finish_and_clear(); - match result { - Ok(output) => { - println!("{}", output.stdout); - success!("Successfully ran {}", exercise); - Ok(()) - } - Err(output) => { - println!("{}", output.stdout); - println!("{}", output.stderr); - - warn!("Ran {} with errors", exercise); - Err(()) - } + stdout().write_all(&output.stdout)?; + if !output.status.success() { + stdout().write_all(&output.stderr)?; + warn!("Ran {} with errors", exercise); + bail!("TODO"); } + + success!("Successfully ran {}", exercise); + Ok(()) } diff --git a/src/verify.rs b/src/verify.rs index 5275bf781c..56c6779612 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,7 +1,14 @@ -use crate::exercise::{CompiledExercise, Exercise, Mode, State}; +use anyhow::{bail, Result}; use console::style; use indicatif::{ProgressBar, ProgressStyle}; -use std::{env, time::Duration}; +use std::{ + env, + io::{stdout, Write}, + process::Output, + time::Duration, +}; + +use crate::exercise::{Exercise, Mode, State}; // Verify that the provided container of Exercise objects // can be compiled and run without any failures. @@ -58,50 +65,44 @@ enum RunMode { } // Compile and run the resulting test harness of the given Exercise -pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> { +pub fn test(exercise: &Exercise, verbose: bool) -> Result<()> { compile_and_test(exercise, RunMode::NonInteractive, verbose, false)?; Ok(()) } // Invoke the rust compiler without running the resulting binary -fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { +fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); - let _ = compile(exercise, &progress_bar)?; + let _ = exercise.run()?; progress_bar.finish_and_clear(); Ok(prompt_for_completion(exercise, None, success_hints)) } // Compile the given Exercise and run the resulting binary in an interactive mode -fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result { +fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {exercise}...")); + progress_bar.set_message(format!("Running {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); - let compilation = compile(exercise, &progress_bar)?; - - progress_bar.set_message(format!("Running {exercise}...")); - let result = compilation.run(); + let output = exercise.run()?; progress_bar.finish_and_clear(); - let output = match result { - Ok(output) => output, - Err(output) => { - warn!("Ran {} with errors", exercise); - println!("{}", output.stdout); - println!("{}", output.stderr); - return Err(()); + if !output.status.success() { + warn!("Ran {} with errors", exercise); + { + let mut stdout = stdout().lock(); + stdout.write_all(&output.stdout)?; + stdout.write_all(&output.stderr)?; + stdout.flush()?; } - }; + bail!("TODO"); + } - Ok(prompt_for_completion( - exercise, - Some(output.stdout), - success_hints, - )) + Ok(prompt_for_completion(exercise, Some(output), success_hints)) } // Compile the given Exercise as a test harness and display @@ -111,62 +112,42 @@ fn compile_and_test( run_mode: RunMode, verbose: bool, success_hints: bool, -) -> Result { +) -> Result { let progress_bar = ProgressBar::new_spinner(); progress_bar.set_message(format!("Testing {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); - let compilation = compile(exercise, &progress_bar)?; - let result = compilation.run(); + let output = exercise.run()?; progress_bar.finish_and_clear(); - match result { - Ok(output) => { - if verbose { - println!("{}", output.stdout); - } - if run_mode == RunMode::Interactive { - Ok(prompt_for_completion(exercise, None, success_hints)) - } else { - Ok(true) - } - } - Err(output) => { - warn!( - "Testing of {} failed! Please try again. Here's the output:", - exercise - ); - println!("{}", output.stdout); - Err(()) + if !output.status.success() { + warn!( + "Testing of {} failed! Please try again. Here's the output:", + exercise + ); + { + let mut stdout = stdout().lock(); + stdout.write_all(&output.stdout)?; + stdout.write_all(&output.stderr)?; + stdout.flush()?; } + bail!("TODO"); } -} -// Compile the given Exercise and return an object with information -// about the state of the compilation -fn compile<'a>( - exercise: &'a Exercise, - progress_bar: &ProgressBar, -) -> Result, ()> { - let compilation_result = exercise.compile(); - - match compilation_result { - Ok(compilation) => Ok(compilation), - Err(output) => { - progress_bar.finish_and_clear(); - warn!( - "Compiling of {} failed! Please try again. Here's the output:", - exercise - ); - println!("{}", output.stderr); - Err(()) - } + if verbose { + stdout().write_all(&output.stdout)?; + } + + if run_mode == RunMode::Interactive { + Ok(prompt_for_completion(exercise, None, success_hints)) + } else { + Ok(true) } } fn prompt_for_completion( exercise: &Exercise, - prompt_output: Option, + prompt_output: Option, success_hints: bool, ) -> bool { let context = match exercise.state() { @@ -200,10 +181,10 @@ fn prompt_for_completion( } if let Some(output) = prompt_output { - println!( - "Output:\n{separator}\n{output}\n{separator}\n", - separator = separator(), - ); + let separator = separator(); + println!("Output:\n{separator}"); + stdout().write_all(&output.stdout).unwrap(); + println!("\n{separator}\n"); } if success_hints { println!( From c1de4d46aad38d315e061b7262f773f48c6aab63 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 18:25:54 +0200 Subject: [PATCH 0593/1432] Some improvements to error handling --- src/exercise.rs | 23 ++++++-------- src/main.rs | 84 +++++++++++++++++++++++-------------------------- src/verify.rs | 14 ++++----- 3 files changed, 55 insertions(+), 66 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 83d444fc2d..48aaedd07a 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -114,14 +114,9 @@ impl Exercise { } } - pub fn state(&self) -> State { - let source_file = File::open(&self.path).unwrap_or_else(|e| { - println!( - "Failed to open the exercise file {}: {e}", - self.path.display(), - ); - exit(1); - }); + pub fn state(&self) -> Result { + let source_file = File::open(&self.path) + .with_context(|| format!("Failed to open the exercise file {}", self.path.display()))?; let mut source_reader = BufReader::new(source_file); // Read the next line into `buf` without the newline at the end. @@ -152,7 +147,7 @@ impl Exercise { // Reached the end of the file and didn't find the comment. if n == 0 { - return State::Done; + return Ok(State::Done); } if contains_not_done_comment(&line) { @@ -198,7 +193,7 @@ impl Exercise { }); } - return State::Pending(context); + return Ok(State::Pending(context)); } current_line_number += 1; @@ -218,8 +213,8 @@ impl Exercise { // without actually having solved anything. // The only other way to truly check this would to compile and run // the exercise; which would be both costly and counterintuitive - pub fn looks_done(&self) -> bool { - self.state() == State::Done + pub fn looks_done(&self) -> Result { + self.state().map(|state| state == State::Done) } } @@ -271,7 +266,7 @@ mod test { }, ]; - assert_eq!(state, State::Pending(expected)); + assert_eq!(state.unwrap(), State::Pending(expected)); } #[test] @@ -283,7 +278,7 @@ mod test { hint: String::new(), }; - assert_eq!(exercise.state(), State::Done); + assert_eq!(exercise.state().unwrap(), State::Done); } #[test] diff --git a/src/main.rs b/src/main.rs index 1c736f3125..72bff4d966 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,14 +92,11 @@ fn main() -> Result<()> { println!("\n{WELCOME}\n"); } - if which::which("cargo").is_err() { - println!( - "Failed to find `cargo`. + which::which("cargo").context( + "Failed to find `cargo`. Did you already install Rust? -Try running `cargo --version` to diagnose the problem." - ); - std::process::exit(1); - } +Try running `cargo --version` to diagnose the problem.", + )?; let exercises = ExerciseList::parse()?.exercises; @@ -122,7 +119,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini let verbose = args.nocapture; let command = args.command.unwrap_or_else(|| { println!("{DEFAULT_OUT}\n"); - std::process::exit(0); + exit(0); }); match command { @@ -160,7 +157,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini let filter_cond = filters .iter() .any(|f| exercise.name.contains(f) || fname.contains(f)); - let looks_done = exercise.looks_done(); + let looks_done = exercise.looks_done()?; let status = if looks_done { exercises_done += 1; "Done" @@ -185,8 +182,8 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini let mut handle = stdout.lock(); handle.write_all(line.as_bytes()).unwrap_or_else(|e| { match e.kind() { - std::io::ErrorKind::BrokenPipe => std::process::exit(0), - _ => std::process::exit(1), + std::io::ErrorKind::BrokenPipe => exit(0), + _ => exit(1), }; }); } @@ -200,35 +197,34 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exercises.len(), percentage_progress ); - std::process::exit(0); + exit(0); } Subcommands::Run { name } => { - let exercise = find_exercise(&name, &exercises); - run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1)); + let exercise = find_exercise(&name, &exercises)?; + run(exercise, verbose).unwrap_or_else(|_| exit(1)); } Subcommands::Reset { name } => { - let exercise = find_exercise(&name, &exercises); + let exercise = find_exercise(&name, &exercises)?; reset(exercise)?; println!("The file {} has been reset!", exercise.path.display()); } Subcommands::Hint { name } => { - let exercise = find_exercise(&name, &exercises); + let exercise = find_exercise(&name, &exercises)?; println!("{}", exercise.hint); } Subcommands::Verify => { - verify(&exercises, (0, exercises.len()), verbose, false) - .unwrap_or_else(|_| std::process::exit(1)); + verify(&exercises, (0, exercises.len()), verbose, false).unwrap_or_else(|_| exit(1)); } Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Err(e) => { println!("Error: Could not watch your progress. Error message was {e:?}."); println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); - std::process::exit(1); + exit(1); } Ok(WatchStatus::Finished) => { println!( @@ -295,25 +291,23 @@ fn spawn_watch_shell( }); } -fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { +fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<&'a Exercise> { if name == "next" { - exercises - .iter() - .find(|e| !e.looks_done()) - .unwrap_or_else(|| { - println!("πŸŽ‰ Congratulations! You have done all the exercises!"); - println!("πŸ”š There are no more exercises to do next!"); - std::process::exit(1) - }) - } else { - exercises - .iter() - .find(|e| e.name == name) - .unwrap_or_else(|| { - println!("No exercise found for '{name}'!"); - std::process::exit(1) - }) + for exercise in exercises { + if !exercise.looks_done()? { + return Ok(exercise); + } + } + + println!("πŸŽ‰ Congratulations! You have done all the exercises!"); + println!("πŸ”š There are no more exercises to do next!"); + exit(0); } + + exercises + .iter() + .find(|e| e.name == name) + .with_context(|| format!("No exercise found for '{name}'!")) } enum WatchStatus { @@ -363,17 +357,17 @@ fn watch( && event_path.exists() { let filepath = event_path.as_path().canonicalize().unwrap(); - let pending_exercises = - exercises - .iter() - .find(|e| filepath.ends_with(&e.path)) - .into_iter() - .chain(exercises.iter().filter(|e| { - !e.looks_done() && !filepath.ends_with(&e.path) - })); + // TODO: Remove unwrap + let pending_exercises = exercises + .iter() + .find(|e| filepath.ends_with(&e.path)) + .into_iter() + .chain(exercises.iter().filter(|e| { + !e.looks_done().unwrap() && !filepath.ends_with(&e.path) + })); let num_done = exercises .iter() - .filter(|e| e.looks_done() && !filepath.ends_with(&e.path)) + .filter(|e| e.looks_done().unwrap() && !filepath.ends_with(&e.path)) .count(); clear_screen(); match verify( diff --git a/src/verify.rs b/src/verify.rs index 56c6779612..adfd3b26d8 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -79,7 +79,7 @@ fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { let _ = exercise.run()?; progress_bar.finish_and_clear(); - Ok(prompt_for_completion(exercise, None, success_hints)) + prompt_for_completion(exercise, None, success_hints) } // Compile the given Exercise and run the resulting binary in an interactive mode @@ -102,7 +102,7 @@ fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Re bail!("TODO"); } - Ok(prompt_for_completion(exercise, Some(output), success_hints)) + prompt_for_completion(exercise, Some(output), success_hints) } // Compile the given Exercise as a test harness and display @@ -139,7 +139,7 @@ fn compile_and_test( } if run_mode == RunMode::Interactive { - Ok(prompt_for_completion(exercise, None, success_hints)) + prompt_for_completion(exercise, None, success_hints) } else { Ok(true) } @@ -149,9 +149,9 @@ fn prompt_for_completion( exercise: &Exercise, prompt_output: Option, success_hints: bool, -) -> bool { - let context = match exercise.state() { - State::Done => return true, +) -> Result { + let context = match exercise.state()? { + State::Done => return Ok(true), State::Pending(context) => context, }; match exercise.mode { @@ -215,7 +215,7 @@ fn prompt_for_completion( ); } - false + Ok(false) } fn separator() -> console::StyledObject<&'static str> { From 7090fffeae88a2afdeb42ae3301c4842416ab729 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 18:59:01 +0200 Subject: [PATCH 0594/1432] Fix tests --- .gitignore | 7 +++---- Cargo.toml | 5 +++++ tests/fixture/failure/Cargo.toml | 21 +++++++++++++++++++++ tests/fixture/state/Cargo.toml | 17 +++++++++++++++++ tests/fixture/success/Cargo.toml | 13 +++++++++++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tests/fixture/failure/Cargo.toml create mode 100644 tests/fixture/state/Cargo.toml create mode 100644 tests/fixture/success/Cargo.toml diff --git a/.gitignore b/.gitignore index f319d39dfa..d6c77083e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,10 @@ -*.swp target/ +tests/fixture/*/Cargo.lock + +*.swp **/*.rs.bk .DS_Store *.pdb -exercises/clippy/Cargo.toml -exercises/clippy/Cargo.lock -rust-project.json .idea .vscode/* !.vscode/extensions.json diff --git a/Cargo.toml b/Cargo.toml index 9224364ba0..5fc75f989b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,10 @@ [workspace] resolver = "2" +exclude = [ + "tests/fixture/failure", + "tests/fixture/state", + "tests/fixture/success", +] [workspace.package] version = "6.0.0" diff --git a/tests/fixture/failure/Cargo.toml b/tests/fixture/failure/Cargo.toml new file mode 100644 index 0000000000..dd728c348a --- /dev/null +++ b/tests/fixture/failure/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "tests" +version = "0.0.0" +edition = "2021" +publish = false + +[[bin]] +name = "compFailure" +path = "exercises/compFailure.rs" + +[[bin]] +name = "compNoExercise" +path = "exercises/compNoExercise.rs" + +[[bin]] +name = "testFailure" +path = "exercises/testFailure.rs" + +[[bin]] +name = "testNotPassed" +path = "exercises/testNotPassed.rs" diff --git a/tests/fixture/state/Cargo.toml b/tests/fixture/state/Cargo.toml new file mode 100644 index 0000000000..5cfa42ba43 --- /dev/null +++ b/tests/fixture/state/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tests" +version = "0.0.0" +edition = "2021" +publish = false + +[[bin]] +name = "pending_exercise" +path = "exercises/pending_exercise.rs" + +[[bin]] +name = "pending_test_exercise" +path = "exercises/pending_test_exercise.rs" + +[[bin]] +name = "finished_exercise" +path = "exercises/finished_exercise.rs" diff --git a/tests/fixture/success/Cargo.toml b/tests/fixture/success/Cargo.toml new file mode 100644 index 0000000000..c0059284d8 --- /dev/null +++ b/tests/fixture/success/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tests" +version = "0.0.0" +edition = "2021" +publish = false + +[[bin]] +name = "compSuccess" +path = "exercises/compSuccess.rs" + +[[bin]] +name = "testSuccess" +path = "exercises/testSuccess.rs" From fb32d0b86fd2f3f0c1e82fecbf2cf4931a7b1ff5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 18:59:07 +0200 Subject: [PATCH 0595/1432] Remove redundant test --- src/exercise.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 48aaedd07a..e7045d60a1 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -281,18 +281,6 @@ mod test { assert_eq!(exercise.state().unwrap(), State::Done); } - #[test] - fn test_exercise_with_output() { - let exercise = Exercise { - name: "exercise_with_output".into(), - path: PathBuf::from("tests/fixture/success/exercises/testSuccess.rs"), - mode: Mode::Test, - hint: String::new(), - }; - let out = exercise.run().unwrap(); - assert_eq!(out.stdout, b"THIS TEST TOO SHALL PASS"); - } - #[test] fn test_not_done() { assert!(contains_not_done_comment("// I AM NOT DONE")); From 7560aec66b4a109c32ea59daa65580ab2ac26333 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 20:08:23 +0200 Subject: [PATCH 0596/1432] Inline reset --- src/main.rs | 7 +++++-- src/run.rs | 11 ++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 72bff4d966..0f298ddea3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ +use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, ExerciseList}; -use crate::run::{reset, run}; +use crate::run::run; use crate::verify::verify; use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; @@ -207,7 +208,9 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini Subcommands::Reset { name } => { let exercise = find_exercise(&name, &exercises)?; - reset(exercise)?; + EMBEDDED_FILES + .write_exercise_to_disk(&exercise.path, WriteStrategy::Overwrite) + .with_context(|| format!("Failed to reset the exercise {exercise}"))?; println!("The file {} has been reset!", exercise.path.display()); } diff --git a/src/run.rs b/src/run.rs index 2c9f99f621..3f93f14681 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,8 +1,7 @@ use anyhow::{bail, Result}; -use std::io::{self, stdout, Write}; +use std::io::{stdout, Write}; use std::time::Duration; -use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, Mode}; use crate::verify::test; use indicatif::ProgressBar; @@ -18,13 +17,7 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<()> { } } -// Resets the exercise by stashing the changes. -pub fn reset(exercise: &Exercise) -> io::Result<()> { - EMBEDDED_FILES.write_exercise_to_disk(&exercise.path, WriteStrategy::Overwrite) -} - -// Invoke the rust compiler on the path of the given exercise -// and run the ensuing binary. +// Compile and run an exercise. // This is strictly for non-test binaries, so output is displayed fn compile_and_run(exercise: &Exercise) -> Result<()> { let progress_bar = ProgressBar::new_spinner(); From 8ad18de54cdad2e94d40d7d4cb67e4a6a274c293 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 20:11:08 +0200 Subject: [PATCH 0597/1432] Use var_os to avoid conversion to String --- src/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui.rs b/src/ui.rs index d8177b9f50..22d60d964a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -3,7 +3,7 @@ macro_rules! print_emoji { use console::{style, Emoji}; use std::env; let formatstr = format!($fmt, $ex); - if env::var("NO_EMOJI").is_ok() { + if env::var_os("NO_EMOJI").is_some() { println!("{} {}", style($sign).$color(), style(formatstr).$color()); } else { println!( From 14f3585816ae12091956efcc45c1e4aefc2f91ce Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Apr 2024 02:11:52 +0200 Subject: [PATCH 0598/1432] Make `cargo run` work --- .gitignore | 3 +- Cargo.toml | 2 + dev/Cargo.toml | 104 ++++++++++++++++++++++++++++++++++ src/bin/gen-dev-cargo-toml.rs | 56 ++++++++++++++++++ src/exercise.rs | 14 ++++- tests/dev_cargo_bins.rs | 39 +++++++++++++ 6 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 dev/Cargo.toml create mode 100644 src/bin/gen-dev-cargo-toml.rs create mode 100644 tests/dev_cargo_bins.rs diff --git a/.gitignore b/.gitignore index d6c77083e9..0bbbc542b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ target/ -tests/fixture/*/Cargo.lock +/tests/fixture/*/Cargo.lock +/dev/Cargo.lock *.swp **/*.rs.bk diff --git a/Cargo.toml b/Cargo.toml index 5fc75f989b..86187b4ba2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ exclude = [ "tests/fixture/failure", "tests/fixture/state", "tests/fixture/success", + "dev", ] [workspace.package] @@ -18,6 +19,7 @@ edition = "2021" [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" +default-run = "rustlings" version.workspace = true authors.workspace = true license.workspace = true diff --git a/dev/Cargo.toml b/dev/Cargo.toml new file mode 100644 index 0000000000..4ad488669f --- /dev/null +++ b/dev/Cargo.toml @@ -0,0 +1,104 @@ +bin = [ + { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, + { name = "intro2", path = "../exercises/00_intro/intro2.rs" }, + { name = "variables1", path = "../exercises/01_variables/variables1.rs" }, + { name = "variables2", path = "../exercises/01_variables/variables2.rs" }, + { name = "variables3", path = "../exercises/01_variables/variables3.rs" }, + { name = "variables4", path = "../exercises/01_variables/variables4.rs" }, + { name = "variables5", path = "../exercises/01_variables/variables5.rs" }, + { name = "variables6", path = "../exercises/01_variables/variables6.rs" }, + { name = "functions1", path = "../exercises/02_functions/functions1.rs" }, + { name = "functions2", path = "../exercises/02_functions/functions2.rs" }, + { name = "functions3", path = "../exercises/02_functions/functions3.rs" }, + { name = "functions4", path = "../exercises/02_functions/functions4.rs" }, + { name = "functions5", path = "../exercises/02_functions/functions5.rs" }, + { name = "if1", path = "../exercises/03_if/if1.rs" }, + { name = "if2", path = "../exercises/03_if/if2.rs" }, + { name = "if3", path = "../exercises/03_if/if3.rs" }, + { name = "quiz1", path = "../exercises/quiz1.rs" }, + { name = "primitive_types1", path = "../exercises/04_primitive_types/primitive_types1.rs" }, + { name = "primitive_types2", path = "../exercises/04_primitive_types/primitive_types2.rs" }, + { name = "primitive_types3", path = "../exercises/04_primitive_types/primitive_types3.rs" }, + { name = "primitive_types4", path = "../exercises/04_primitive_types/primitive_types4.rs" }, + { name = "primitive_types5", path = "../exercises/04_primitive_types/primitive_types5.rs" }, + { name = "primitive_types6", path = "../exercises/04_primitive_types/primitive_types6.rs" }, + { name = "vecs1", path = "../exercises/05_vecs/vecs1.rs" }, + { name = "vecs2", path = "../exercises/05_vecs/vecs2.rs" }, + { name = "move_semantics1", path = "../exercises/06_move_semantics/move_semantics1.rs" }, + { name = "move_semantics2", path = "../exercises/06_move_semantics/move_semantics2.rs" }, + { name = "move_semantics3", path = "../exercises/06_move_semantics/move_semantics3.rs" }, + { name = "move_semantics4", path = "../exercises/06_move_semantics/move_semantics4.rs" }, + { name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" }, + { name = "move_semantics6", path = "../exercises/06_move_semantics/move_semantics6.rs" }, + { name = "structs1", path = "../exercises/07_structs/structs1.rs" }, + { name = "structs2", path = "../exercises/07_structs/structs2.rs" }, + { name = "structs3", path = "../exercises/07_structs/structs3.rs" }, + { name = "enums1", path = "../exercises/08_enums/enums1.rs" }, + { name = "enums2", path = "../exercises/08_enums/enums2.rs" }, + { name = "enums3", path = "../exercises/08_enums/enums3.rs" }, + { name = "strings1", path = "../exercises/09_strings/strings1.rs" }, + { name = "strings2", path = "../exercises/09_strings/strings2.rs" }, + { name = "strings3", path = "../exercises/09_strings/strings3.rs" }, + { name = "strings4", path = "../exercises/09_strings/strings4.rs" }, + { name = "modules1", path = "../exercises/10_modules/modules1.rs" }, + { name = "modules2", path = "../exercises/10_modules/modules2.rs" }, + { name = "modules3", path = "../exercises/10_modules/modules3.rs" }, + { name = "hashmaps1", path = "../exercises/11_hashmaps/hashmaps1.rs" }, + { name = "hashmaps2", path = "../exercises/11_hashmaps/hashmaps2.rs" }, + { name = "hashmaps3", path = "../exercises/11_hashmaps/hashmaps3.rs" }, + { name = "quiz2", path = "../exercises/quiz2.rs" }, + { name = "options1", path = "../exercises/12_options/options1.rs" }, + { name = "options2", path = "../exercises/12_options/options2.rs" }, + { name = "options3", path = "../exercises/12_options/options3.rs" }, + { name = "errors1", path = "../exercises/13_error_handling/errors1.rs" }, + { name = "errors2", path = "../exercises/13_error_handling/errors2.rs" }, + { name = "errors3", path = "../exercises/13_error_handling/errors3.rs" }, + { name = "errors4", path = "../exercises/13_error_handling/errors4.rs" }, + { name = "errors5", path = "../exercises/13_error_handling/errors5.rs" }, + { name = "errors6", path = "../exercises/13_error_handling/errors6.rs" }, + { name = "generics1", path = "../exercises/14_generics/generics1.rs" }, + { name = "generics2", path = "../exercises/14_generics/generics2.rs" }, + { name = "traits1", path = "../exercises/15_traits/traits1.rs" }, + { name = "traits2", path = "../exercises/15_traits/traits2.rs" }, + { name = "traits3", path = "../exercises/15_traits/traits3.rs" }, + { name = "traits4", path = "../exercises/15_traits/traits4.rs" }, + { name = "traits5", path = "../exercises/15_traits/traits5.rs" }, + { name = "quiz3", path = "../exercises/quiz3.rs" }, + { name = "lifetimes1", path = "../exercises/16_lifetimes/lifetimes1.rs" }, + { name = "lifetimes2", path = "../exercises/16_lifetimes/lifetimes2.rs" }, + { name = "lifetimes3", path = "../exercises/16_lifetimes/lifetimes3.rs" }, + { name = "tests1", path = "../exercises/17_tests/tests1.rs" }, + { name = "tests2", path = "../exercises/17_tests/tests2.rs" }, + { name = "tests3", path = "../exercises/17_tests/tests3.rs" }, + { name = "tests4", path = "../exercises/17_tests/tests4.rs" }, + { name = "iterators1", path = "../exercises/18_iterators/iterators1.rs" }, + { name = "iterators2", path = "../exercises/18_iterators/iterators2.rs" }, + { name = "iterators3", path = "../exercises/18_iterators/iterators3.rs" }, + { name = "iterators4", path = "../exercises/18_iterators/iterators4.rs" }, + { name = "iterators5", path = "../exercises/18_iterators/iterators5.rs" }, + { name = "box1", path = "../exercises/19_smart_pointers/box1.rs" }, + { name = "rc1", path = "../exercises/19_smart_pointers/rc1.rs" }, + { name = "arc1", path = "../exercises/19_smart_pointers/arc1.rs" }, + { name = "cow1", path = "../exercises/19_smart_pointers/cow1.rs" }, + { name = "threads1", path = "../exercises/20_threads/threads1.rs" }, + { name = "threads2", path = "../exercises/20_threads/threads2.rs" }, + { name = "threads3", path = "../exercises/20_threads/threads3.rs" }, + { name = "macros1", path = "../exercises/21_macros/macros1.rs" }, + { name = "macros2", path = "../exercises/21_macros/macros2.rs" }, + { name = "macros3", path = "../exercises/21_macros/macros3.rs" }, + { name = "macros4", path = "../exercises/21_macros/macros4.rs" }, + { name = "clippy1", path = "../exercises/22_clippy/clippy1.rs" }, + { name = "clippy2", path = "../exercises/22_clippy/clippy2.rs" }, + { name = "clippy3", path = "../exercises/22_clippy/clippy3.rs" }, + { name = "using_as", path = "../exercises/23_conversions/using_as.rs" }, + { name = "from_into", path = "../exercises/23_conversions/from_into.rs" }, + { name = "from_str", path = "../exercises/23_conversions/from_str.rs" }, + { name = "try_from_into", path = "../exercises/23_conversions/try_from_into.rs" }, + { name = "as_ref_mut", path = "../exercises/23_conversions/as_ref_mut.rs" }, +] + +[package] +name = "rustlings" +version = "0.0.0" +edition = "2021" +publish = false diff --git a/src/bin/gen-dev-cargo-toml.rs b/src/bin/gen-dev-cargo-toml.rs new file mode 100644 index 0000000000..20167a1e64 --- /dev/null +++ b/src/bin/gen-dev-cargo-toml.rs @@ -0,0 +1,56 @@ +use anyhow::{bail, Context, Result}; +use serde::Deserialize; +use std::{ + fs::{self, create_dir}, + io::ErrorKind, +}; + +#[derive(Deserialize)] +struct Exercise { + name: String, + path: String, +} + +#[derive(Deserialize)] +struct InfoToml { + exercises: Vec, +} + +fn main() -> Result<()> { + let exercises = toml_edit::de::from_str::( + &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"bin = [\n"); + + for exercise in exercises { + buf.extend_from_slice(b" { name = \""); + buf.extend_from_slice(exercise.name.as_bytes()); + buf.extend_from_slice(b"\", path = \"../"); + buf.extend_from_slice(exercise.path.as_bytes()); + buf.extend_from_slice(b"\" },\n"); + } + + buf.extend_from_slice( + br#"] + +[package] +name = "rustlings" +version = "0.0.0" +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/src/exercise.rs b/src/exercise.rs index e7045d60a1..450acf4553 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -91,9 +91,17 @@ pub struct ContextLine { impl Exercise { fn cargo_cmd(&self, command: &str, args: &[&str]) -> Result { - Command::new("cargo") - .arg(command) - .arg("--color") + let mut cmd = Command::new("cargo"); + 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() { + cmd.arg("--manifest-path").arg("dev/Cargo.toml"); + } + + cmd.arg("--color") .arg("always") .arg("-q") .arg("--bin") diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs new file mode 100644 index 0000000000..7f1771b629 --- /dev/null +++ b/tests/dev_cargo_bins.rs @@ -0,0 +1,39 @@ +// Makes sure that `dev/Cargo.toml` is synced with `info.toml`. +// When this test fails, you just need to run `cargo run --bin gen-dev-cargo-toml`. + +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize)] +struct Exercise { + name: String, + path: String, +} + +#[derive(Deserialize)] +struct InfoToml { + exercises: Vec, +} + +#[test] +fn dev_cargo_bins() { + let content = fs::read_to_string("exercises/Cargo.toml").unwrap(); + + let exercises = toml_edit::de::from_str::(&fs::read_to_string("info.toml").unwrap()) + .unwrap() + .exercises; + + let mut start_ind = 0; + for exercise in exercises { + let name_start = start_ind + content[start_ind..].find('"').unwrap() + 1; + let name_end = name_start + content[name_start..].find('"').unwrap(); + assert_eq!(exercise.name, &content[name_start..name_end]); + + // +3 to skip `../` at the begeinning of the path. + let path_start = name_end + content[name_end + 1..].find('"').unwrap() + 5; + let path_end = path_start + content[path_start..].find('"').unwrap(); + assert_eq!(exercise.path, &content[path_start..path_end]); + + start_ind = path_end + 1; + } +} From 2f30eac27f2b57148081dbe1c489e6c47f01d6a9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Apr 2024 17:36:42 +0200 Subject: [PATCH 0599/1432] Remove unneeded .iter() --- src/main.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0f298ddea3..f9e0f83094 100644 --- a/src/main.rs +++ b/src/main.rs @@ -339,12 +339,8 @@ fn watch( clear_screen(); - let failed_exercise_hint = match verify( - exercises.iter(), - (0, exercises.len()), - verbose, - success_hints, - ) { + let failed_exercise_hint = match verify(exercises, (0, exercises.len()), verbose, success_hints) + { Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), }; From fdd7de00bd37e43a4e464d1cb5cc10c3753b3688 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Apr 2024 18:21:56 +0200 Subject: [PATCH 0600/1432] Improvements to `verify` --- src/main.rs | 37 ++++++++++++++++--------------------- src/verify.rs | 17 ++++++----------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/src/main.rs b/src/main.rs index f9e0f83094..7b7b16552d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,6 @@ use console::Emoji; use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use shlex::Shlex; -use std::ffi::OsStr; use std::io::{BufRead, Write}; use std::path::Path; use std::process::{exit, Command}; @@ -344,44 +343,40 @@ fn watch( Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), }; + spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit)); + + let mut pending_exercises = Vec::with_capacity(exercises.len()); loop { match rx.recv_timeout(Duration::from_secs(1)) { Ok(event) => match event { Ok(events) => { for event in events { - let event_path = event.path; if event.kind == DebouncedEventKind::Any - && event_path.extension() == Some(OsStr::new("rs")) - && event_path.exists() + && event.path.extension().is_some_and(|ext| ext == "rs") { - let filepath = event_path.as_path().canonicalize().unwrap(); - // TODO: Remove unwrap - let pending_exercises = exercises - .iter() - .find(|e| filepath.ends_with(&e.path)) - .into_iter() - .chain(exercises.iter().filter(|e| { - !e.looks_done().unwrap() && !filepath.ends_with(&e.path) - })); - let num_done = exercises - .iter() - .filter(|e| e.looks_done().unwrap() && !filepath.ends_with(&e.path)) - .count(); + pending_exercises.extend(exercises.iter().filter(|exercise| { + !exercise.looks_done().unwrap_or(false) + || event.path.ends_with(&exercise.path) + })); + let num_done = exercises.len() - pending_exercises.len(); + clear_screen(); + match verify( - pending_exercises, + pending_exercises.iter().copied(), (num_done, exercises.len()), verbose, success_hints, ) { Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => { - let mut failed_exercise_hint = - failed_exercise_hint.lock().unwrap(); - *failed_exercise_hint = Some(exercise.hint.clone()); + let hint = exercise.hint.clone(); + *failed_exercise_hint.lock().unwrap() = Some(hint); } } + + pending_exercises.clear(); } } } diff --git a/src/verify.rs b/src/verify.rs index adfd3b26d8..6e048a190a 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -16,7 +16,7 @@ use crate::exercise::{Exercise, Mode, State}; // If the Exercise being verified is a test, the verbose boolean // determines whether or not the test harness outputs are displayed. pub fn verify<'a>( - exercises: impl IntoIterator, + pending_exercises: impl IntoIterator, progress: (usize, usize), verbose: bool, success_hints: bool, @@ -33,7 +33,7 @@ pub fn verify<'a>( bar.set_position(num_done as u64); bar.set_message(format!("({percentage:.1} %)")); - for exercise in exercises { + for exercise in pending_exercises { let compile_result = match exercise.mode { Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints), Mode::Compile => compile_and_run_interactively(exercise, success_hints), @@ -45,16 +45,11 @@ pub fn verify<'a>( percentage += 100.0 / total as f32; bar.inc(1); bar.set_message(format!("({percentage:.1} %)")); - if bar.position() == total as u64 { - println!( - "Progress: You completed {} / {} exercises ({:.1} %).", - bar.position(), - total, - percentage - ); - bar.finish(); - } } + + bar.finish(); + println!("You completed all exercises!"); + Ok(()) } From def8d2c569a8a637396960c8513a0b1bdf88ef0c Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Apr 2024 18:38:01 +0200 Subject: [PATCH 0601/1432] Add VerifyState --- src/main.rs | 34 ++++++++++++++++------------------ src/verify.rs | 19 ++++++++++++------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7b7b16552d..c8c6584842 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,10 @@ use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, ExerciseList}; use crate::run::run; use crate::verify::verify; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; use console::Emoji; -use notify_debouncer_mini::notify::{self, RecursiveMode}; +use notify_debouncer_mini::notify::RecursiveMode; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use shlex::Shlex; use std::io::{BufRead, Write}; @@ -16,6 +16,7 @@ use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::{Arc, Mutex}; use std::time::Duration; use std::{io, thread}; +use verify::VerifyState; #[macro_use] mod ui; @@ -218,9 +219,10 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini println!("{}", exercise.hint); } - Subcommands::Verify => { - verify(&exercises, (0, exercises.len()), verbose, false).unwrap_or_else(|_| exit(1)); - } + Subcommands::Verify => match verify(&exercises, (0, exercises.len()), verbose, false)? { + VerifyState::AllExercisesDone => println!("All exercises done!"), + VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), + }, Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Err(e) => { @@ -317,11 +319,7 @@ enum WatchStatus { Unfinished, } -fn watch( - exercises: &[Exercise], - verbose: bool, - success_hints: bool, -) -> notify::Result { +fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> Result { /* Clears the terminal with an ANSI escape code. Works in UNIX and newer Windows terminals. */ fn clear_screen() { @@ -338,11 +336,11 @@ fn watch( clear_screen(); - let failed_exercise_hint = match verify(exercises, (0, exercises.len()), verbose, success_hints) - { - Ok(_) => return Ok(WatchStatus::Finished), - Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), - }; + let failed_exercise_hint = + match verify(exercises, (0, exercises.len()), verbose, success_hints)? { + VerifyState::AllExercisesDone => return Ok(WatchStatus::Finished), + VerifyState::Failed(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), + }; spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit)); @@ -368,9 +366,9 @@ fn watch( (num_done, exercises.len()), verbose, success_hints, - ) { - Ok(_) => return Ok(WatchStatus::Finished), - Err(exercise) => { + )? { + VerifyState::AllExercisesDone => return Ok(WatchStatus::Finished), + VerifyState::Failed(exercise) => { let hint = exercise.hint.clone(); *failed_exercise_hint.lock().unwrap() = Some(hint); } diff --git a/src/verify.rs b/src/verify.rs index 6e048a190a..02bff9959a 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -10,6 +10,11 @@ use std::{ use crate::exercise::{Exercise, Mode, State}; +pub enum VerifyState<'a> { + AllExercisesDone, + Failed(&'a Exercise), +} + // Verify that the provided container of Exercise objects // can be compiled and run without any failures. // Any such failures will be reported to the end user. @@ -20,7 +25,7 @@ pub fn verify<'a>( progress: (usize, usize), verbose: bool, success_hints: bool, -) -> Result<(), &'a Exercise> { +) -> Result> { let (num_done, total) = progress; let bar = ProgressBar::new(total as u64); let mut percentage = num_done as f32 / total as f32 * 100.0; @@ -35,12 +40,12 @@ pub fn verify<'a>( for exercise in pending_exercises { let compile_result = match exercise.mode { - Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints), - Mode::Compile => compile_and_run_interactively(exercise, success_hints), - Mode::Clippy => compile_only(exercise, success_hints), + Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints)?, + Mode::Compile => compile_and_run_interactively(exercise, success_hints)?, + Mode::Clippy => compile_only(exercise, success_hints)?, }; - if !compile_result.unwrap_or(false) { - return Err(exercise); + if !compile_result { + return Ok(VerifyState::Failed(exercise)); } percentage += 100.0 / total as f32; bar.inc(1); @@ -50,7 +55,7 @@ pub fn verify<'a>( bar.finish(); println!("You completed all exercises!"); - Ok(()) + Ok(VerifyState::AllExercisesDone) } #[derive(PartialEq, Eq)] From 190945352a2316154d9856a5d882893326e0136a Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Apr 2024 18:52:43 +0200 Subject: [PATCH 0602/1432] Add comments about dev/Cargo.toml --- dev/Cargo.toml | 3 +++ src/bin/gen-dev-cargo-toml.rs | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 4ad488669f..e4e7be78e6 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,3 +1,6 @@ +# This file is a hack to allow using `cargo r` to test `rustlings` during development. +# You shouldn't edit it manually. It is created and updated by running `cargo run --bin gen-dev-cargo-toml`. + bin = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, { name = "intro2", path = "../exercises/00_intro/intro2.rs" }, diff --git a/src/bin/gen-dev-cargo-toml.rs b/src/bin/gen-dev-cargo-toml.rs index 20167a1e64..65cc2447c2 100644 --- a/src/bin/gen-dev-cargo-toml.rs +++ b/src/bin/gen-dev-cargo-toml.rs @@ -1,3 +1,7 @@ +// Generates `dev/Cargo.toml` such that it is synced with `info.toml`. +// `dev/Cargo.toml` is a hack to allow using `cargo r` to test `rustlings` +// during development. + use anyhow::{bail, Context, Result}; use serde::Deserialize; use std::{ @@ -25,7 +29,12 @@ fn main() -> Result<()> { let mut buf = Vec::with_capacity(1 << 14); - buf.extend_from_slice(b"bin = [\n"); + buf.extend_from_slice( + b"# This file is a hack to allow using `cargo r` to test `rustlings` during development. +# You shouldn't edit it manually. It is created and updated by running `cargo run --bin gen-dev-cargo-toml`. + +bin = [\n", + ); for exercise in exercises { buf.extend_from_slice(b" { name = \""); From 569a68eb73b82040588138b0ba1daabca1a7d415 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 15:44:48 +0200 Subject: [PATCH 0603/1432] Minify generated Cargo.toml --- src/init.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/init.rs b/src/init.rs index d958c96e15..b52b613989 100644 --- a/src/init.rs +++ b/src/init.rs @@ -10,21 +10,25 @@ use crate::{embedded::EMBEDDED_FILES, exercise::Exercise}; fn create_cargo_toml(exercises: &[Exercise]) -> io::Result<()> { let mut cargo_toml = Vec::with_capacity(1 << 13); + cargo_toml.extend_from_slice(b"bin = [\n"); + for exercise in exercises { + cargo_toml.extend_from_slice(b" { name = \""); + cargo_toml.extend_from_slice(exercise.name.as_bytes()); + cargo_toml.extend_from_slice(b"\", path = \""); + cargo_toml.extend_from_slice(exercise.path.to_str().unwrap().as_bytes()); + cargo_toml.extend_from_slice(b"\" },\n"); + } + cargo_toml.extend_from_slice( - br#"[package] + br#"] + +[package] name = "rustlings" version = "0.0.0" edition = "2021" publish = false "#, ); - for exercise in exercises { - cargo_toml.extend_from_slice(b"\n[[bin]]\nname = \""); - cargo_toml.extend_from_slice(exercise.name.as_bytes()); - cargo_toml.extend_from_slice(b"\"\npath = \""); - cargo_toml.extend_from_slice(exercise.path.to_str().unwrap().as_bytes()); - cargo_toml.extend_from_slice(b"\"\n"); - } OpenOptions::new() .create_new(true) .write(true) From b6c434c445d91a9e886e5639b078635e5eca4eb3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 15:45:53 +0200 Subject: [PATCH 0604/1432] Remove optional version field --- dev/Cargo.toml | 1 - src/bin/gen-dev-cargo-toml.rs | 1 - src/init.rs | 1 - tests/fixture/failure/Cargo.toml | 1 - tests/fixture/state/Cargo.toml | 1 - tests/fixture/success/Cargo.toml | 1 - 6 files changed, 6 deletions(-) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index e4e7be78e6..7868b97c3d 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -102,6 +102,5 @@ bin = [ [package] name = "rustlings" -version = "0.0.0" edition = "2021" publish = false diff --git a/src/bin/gen-dev-cargo-toml.rs b/src/bin/gen-dev-cargo-toml.rs index 65cc2447c2..ff8f31db7c 100644 --- a/src/bin/gen-dev-cargo-toml.rs +++ b/src/bin/gen-dev-cargo-toml.rs @@ -49,7 +49,6 @@ bin = [\n", [package] name = "rustlings" -version = "0.0.0" edition = "2021" publish = false "#, diff --git a/src/init.rs b/src/init.rs index b52b613989..6af32351b8 100644 --- a/src/init.rs +++ b/src/init.rs @@ -24,7 +24,6 @@ fn create_cargo_toml(exercises: &[Exercise]) -> io::Result<()> { [package] name = "rustlings" -version = "0.0.0" edition = "2021" publish = false "#, diff --git a/tests/fixture/failure/Cargo.toml b/tests/fixture/failure/Cargo.toml index dd728c348a..e111cf2bfa 100644 --- a/tests/fixture/failure/Cargo.toml +++ b/tests/fixture/failure/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "tests" -version = "0.0.0" edition = "2021" publish = false diff --git a/tests/fixture/state/Cargo.toml b/tests/fixture/state/Cargo.toml index 5cfa42ba43..c8d74e47fd 100644 --- a/tests/fixture/state/Cargo.toml +++ b/tests/fixture/state/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "tests" -version = "0.0.0" edition = "2021" publish = false diff --git a/tests/fixture/success/Cargo.toml b/tests/fixture/success/Cargo.toml index c0059284d8..f26a44f19e 100644 --- a/tests/fixture/success/Cargo.toml +++ b/tests/fixture/success/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "tests" -version = "0.0.0" edition = "2021" publish = false From 2b6f9fb6a7a33f074aa609b2da1ac084bc3ecd6b Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 20:21:55 +0200 Subject: [PATCH 0605/1432] Add Ratatui --- Cargo.lock | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + 2 files changed, 312 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8e5b723d1..38f8170c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -11,6 +23,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anstream" version = "0.6.13" @@ -109,6 +127,21 @@ dependencies = [ "serde", ] +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -143,10 +176,10 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] @@ -161,6 +194,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "console" version = "0.15.8" @@ -189,6 +235,31 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.5.0", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "difflib" version = "0.4.0" @@ -270,6 +341,16 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -309,6 +390,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + [[package]] name = "inotify" version = "0.9.6" @@ -338,6 +425,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -382,17 +478,36 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mio" @@ -457,6 +572,41 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "portable-atomic" version = "1.6.0" @@ -511,6 +661,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ratatui" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" +dependencies = [ + "bitflags 2.5.0", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "itertools", + "lru", + "paste", + "stability", + "strum", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -570,10 +740,12 @@ dependencies = [ "assert_cmd", "clap", "console", + "crossterm", "glob", "indicatif", "notify-debouncer-mini", "predicates", + "ratatui", "rustlings-macros", "serde", "serde_json", @@ -590,6 +762,12 @@ dependencies = [ "quote", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.17" @@ -605,6 +783,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.197" @@ -622,7 +806,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] @@ -651,17 +835,102 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "stability" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] [[package]] name = "syn" -version = "2.0.55" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -702,6 +971,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.11" @@ -714,6 +989,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -928,3 +1209,23 @@ name = "winsafe" version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] diff --git a/Cargo.toml b/Cargo.toml index 86187b4ba2..2a22fce1c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,10 @@ edition.workspace = true anyhow = "1.0.81" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" +crossterm = "0.27.0" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" +ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } From 9ea744a7104f441ef505db0a96e852f93d8c0bf4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 20:27:30 +0200 Subject: [PATCH 0606/1432] Remove deps not needed in the TUI --- Cargo.lock | 42 ------------------------------------------ Cargo.toml | 2 -- 2 files changed, 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38f8170c5a..4aaec38c24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,19 +377,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "indicatif" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" -dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", -] - [[package]] name = "indoc" version = "2.0.5" @@ -416,15 +403,6 @@ dependencies = [ "libc", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "itertools" version = "0.12.1" @@ -566,12 +544,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "once_cell" version = "1.19.0" @@ -607,12 +579,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - [[package]] name = "predicates" version = "3.1.0" @@ -742,14 +708,12 @@ dependencies = [ "console", "crossterm", "glob", - "indicatif", "notify-debouncer-mini", "predicates", "ratatui", "rustlings-macros", "serde", "serde_json", - "shlex", "toml_edit", "which", "winnow", @@ -829,12 +793,6 @@ dependencies = [ "serde", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signal-hook" version = "0.3.17" diff --git a/Cargo.toml b/Cargo.toml index 2a22fce1c6..3c18741707 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,13 +30,11 @@ anyhow = "1.0.81" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" crossterm = "0.27.0" -indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } -shlex = "1.3.0" toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } which = "6.0.1" winnow = "0.6.5" From 34375b2ebfbdb0b6504a56c82635c8c9d3d6ce59 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 21:06:11 +0200 Subject: [PATCH 0607/1432] Clean up as a preparation for the TUI --- src/main.rs | 44 +++------- src/run.rs | 38 +++------ src/verify.rs | 227 ++++++++++---------------------------------------- 3 files changed, 65 insertions(+), 244 deletions(-) diff --git a/src/main.rs b/src/main.rs index c8c6584842..20ec290fd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,10 +7,9 @@ use clap::{Parser, Subcommand}; use console::Emoji; use notify_debouncer_mini::notify::RecursiveMode; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; -use shlex::Shlex; use std::io::{BufRead, Write}; use std::path::Path; -use std::process::{exit, Command}; +use std::process::exit; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::{Arc, Mutex}; @@ -31,9 +30,6 @@ mod verify; #[derive(Parser)] #[command(version)] struct Args { - /// Show outputs from the test exercises - #[arg(long)] - nocapture: bool, #[command(subcommand)] command: Option, } @@ -45,11 +41,7 @@ enum Subcommands { /// Verify all exercises according to the recommended order Verify, /// Rerun `verify` when files were edited - Watch { - /// Show hints on success - #[arg(long)] - success_hints: bool, - }, + Watch, /// Run/Test a single exercise Run { /// The name of the exercise @@ -117,7 +109,6 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let verbose = args.nocapture; let command = args.command.unwrap_or_else(|| { println!("{DEFAULT_OUT}\n"); exit(0); @@ -203,7 +194,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini Subcommands::Run { name } => { let exercise = find_exercise(&name, &exercises)?; - run(exercise, verbose).unwrap_or_else(|_| exit(1)); + run(exercise).unwrap_or_else(|_| exit(1)); } Subcommands::Reset { name } => { @@ -219,12 +210,12 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini println!("{}", exercise.hint); } - Subcommands::Verify => match verify(&exercises, (0, exercises.len()), verbose, false)? { + Subcommands::Verify => match verify(&exercises, (0, exercises.len()))? { VerifyState::AllExercisesDone => println!("All exercises done!"), VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), }, - Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { + Subcommands::Watch => match watch(&exercises) { Err(e) => { println!("Error: Could not watch your progress. Error message was {e:?}."); println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); @@ -277,17 +268,6 @@ fn spawn_watch_shell( println!("Bye!"); } else if input == "help" { println!("{WATCH_MODE_HELP_MESSAGE}"); - } else if let Some(cmd) = input.strip_prefix('!') { - let mut parts = Shlex::new(cmd); - - let Some(program) = parts.next() else { - println!("no command provided"); - continue; - }; - - if let Err(e) = Command::new(program).args(parts).status() { - println!("failed to execute command `{cmd}`: {e}"); - } } else { println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}"); } @@ -319,7 +299,7 @@ enum WatchStatus { Unfinished, } -fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> Result { +fn watch(exercises: &[Exercise]) -> Result { /* Clears the terminal with an ANSI escape code. Works in UNIX and newer Windows terminals. */ fn clear_screen() { @@ -336,11 +316,10 @@ fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> Result return Ok(WatchStatus::Finished), - VerifyState::Failed(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), - }; + let failed_exercise_hint = match verify(exercises, (0, exercises.len()))? { + VerifyState::AllExercisesDone => return Ok(WatchStatus::Finished), + VerifyState::Failed(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), + }; spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit)); @@ -364,8 +343,6 @@ fn watch(exercises: &[Exercise], verbose: bool, success_hints: bool) -> Result return Ok(WatchStatus::Finished), VerifyState::Failed(exercise) => { @@ -429,7 +406,6 @@ const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: hint - prints the current exercise's hint clear - clears the screen quit - quits watch mode - ! - executes a command, like `!rustc --explain E0381` help - displays this help message Watch mode automatically re-evaluates the current exercise diff --git a/src/run.rs b/src/run.rs index 3f93f14681..0a09eccf34 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,39 +1,27 @@ -use anyhow::{bail, Result}; +use anyhow::Result; use std::io::{stdout, Write}; -use std::time::Duration; -use crate::exercise::{Exercise, Mode}; -use crate::verify::test; -use indicatif::ProgressBar; +use crate::exercise::Exercise; // Invoke the rust compiler on the path of the given exercise, // and run the ensuing binary. // The verbose argument helps determine whether or not to show // the output from the test harnesses (if the mode of the exercise is test) -pub fn run(exercise: &Exercise, verbose: bool) -> Result<()> { - match exercise.mode { - Mode::Test => test(exercise, verbose), - Mode::Compile | Mode::Clippy => compile_and_run(exercise), - } -} - -// Compile and run an exercise. -// This is strictly for non-test binaries, so output is displayed -fn compile_and_run(exercise: &Exercise) -> Result<()> { - let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Running {exercise}...")); - progress_bar.enable_steady_tick(Duration::from_millis(100)); - +pub fn run(exercise: &Exercise) -> Result<()> { let output = exercise.run()?; - progress_bar.finish_and_clear(); - stdout().write_all(&output.stdout)?; - if !output.status.success() { - stdout().write_all(&output.stderr)?; + { + let mut stdout = stdout().lock(); + stdout.write_all(&output.stdout)?; + stdout.write_all(&output.stderr)?; + stdout.flush()?; + } + + if output.status.success() { + success!("Successfully ran {}", exercise); + } else { warn!("Ran {} with errors", exercise); - bail!("TODO"); } - success!("Successfully ran {}", exercise); Ok(()) } diff --git a/src/verify.rs b/src/verify.rs index ef966f6021..5b053940a9 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,12 +1,6 @@ -use anyhow::{bail, Result}; +use anyhow::Result; use console::style; -use indicatif::{ProgressBar, ProgressStyle}; -use std::{ - env, - io::{stdout, Write}, - process::Output, - time::Duration, -}; +use std::io::{stdout, Write}; use crate::exercise::{Exercise, Mode, State}; @@ -23,201 +17,64 @@ pub enum VerifyState<'a> { pub fn verify<'a>( pending_exercises: impl IntoIterator, progress: (usize, usize), - verbose: bool, - success_hints: bool, ) -> Result> { - let (num_done, total) = progress; - let bar = ProgressBar::new(total as u64); - let mut percentage = num_done as f32 / total as f32 * 100.0; - bar.set_style( - ProgressStyle::default_bar() - .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") - .expect("Progressbar template should be valid!") - .progress_chars("#>-"), + let (mut num_done, total) = progress; + println!( + "Progress: {num_done}/{total} ({:.1}%)\n", + num_done as f32 / total as f32 * 100.0, ); - bar.set_position(num_done as u64); - bar.set_message(format!("({percentage:.1} %)")); for exercise in pending_exercises { - let compile_result = match exercise.mode { - Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints)?, - Mode::Compile => compile_and_run_interactively(exercise, success_hints)?, - Mode::Clippy => compile_only(exercise, success_hints)?, - }; - if !compile_result { - return Ok(VerifyState::Failed(exercise)); - } - percentage += 100.0 / total as f32; - bar.inc(1); - bar.set_message(format!("({percentage:.1} %)")); - } - - bar.finish(); - println!("You completed all exercises!"); - - Ok(VerifyState::AllExercisesDone) -} - -#[derive(PartialEq, Eq)] -enum RunMode { - Interactive, - NonInteractive, -} - -// Compile and run the resulting test harness of the given Exercise -pub fn test(exercise: &Exercise, verbose: bool) -> Result<()> { - compile_and_test(exercise, RunMode::NonInteractive, verbose, false)?; - Ok(()) -} - -// Invoke the rust compiler without running the resulting binary -fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { - let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {exercise}...")); - progress_bar.enable_steady_tick(Duration::from_millis(100)); - - let _ = exercise.run()?; - progress_bar.finish_and_clear(); + let output = exercise.run()?; - prompt_for_completion(exercise, None, success_hints) -} - -// Compile the given Exercise and run the resulting binary in an interactive mode -fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result { - let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Running {exercise}...")); - progress_bar.enable_steady_tick(Duration::from_millis(100)); - - let output = exercise.run()?; - progress_bar.finish_and_clear(); - - if !output.status.success() { - warn!("Ran {} with errors", exercise); { let mut stdout = stdout().lock(); stdout.write_all(&output.stdout)?; stdout.write_all(&output.stderr)?; stdout.flush()?; } - bail!("TODO"); - } - - prompt_for_completion(exercise, Some(output), success_hints) -} -// Compile the given Exercise as a test harness and display -// the output if verbose is set to true -fn compile_and_test( - exercise: &Exercise, - run_mode: RunMode, - verbose: bool, - success_hints: bool, -) -> Result { - let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Testing {exercise}...")); - progress_bar.enable_steady_tick(Duration::from_millis(100)); - - let output = exercise.run()?; - progress_bar.finish_and_clear(); - - if !output.status.success() { - warn!( - "Testing of {} failed! Please try again. Here's the output:", - exercise - ); - { - let mut stdout = stdout().lock(); - stdout.write_all(&output.stdout)?; - stdout.write_all(&output.stderr)?; - stdout.flush()?; + if !output.status.success() { + return Ok(VerifyState::Failed(exercise)); } - bail!("TODO"); - } - - if verbose { - stdout().write_all(&output.stdout)?; - } - - if run_mode == RunMode::Interactive { - prompt_for_completion(exercise, None, success_hints) - } else { - Ok(true) - } -} - -fn prompt_for_completion( - exercise: &Exercise, - prompt_output: Option, - success_hints: bool, -) -> Result { - let context = match exercise.state()? { - State::Done => return Ok(true), - State::Pending(context) => context, - }; - match exercise.mode { - Mode::Compile => success!("Successfully ran {}!", exercise), - Mode::Test => success!("Successfully tested {}!", exercise), - Mode::Clippy => success!("Successfully compiled {}!", exercise), - } - - let no_emoji = env::var("NO_EMOJI").is_ok(); - let clippy_success_msg = if no_emoji { - "The code is compiling, and Clippy is happy!" - } else { - "The code is compiling, and πŸ“Ž Clippy πŸ“Ž is happy!" - }; - - let success_msg = match exercise.mode { - Mode::Compile => "The code is compiling!", - Mode::Test => "The code is compiling, and the tests pass!", - Mode::Clippy => clippy_success_msg, - }; - - if no_emoji { - println!("\n~*~ {success_msg} ~*~\n"); - } else { - println!("\nπŸŽ‰ πŸŽ‰ {success_msg} πŸŽ‰ πŸŽ‰\n"); - } - - if let Some(output) = prompt_output { - let separator = separator(); - println!("Output:\n{separator}"); - stdout().write_all(&output.stdout).unwrap(); - println!("\n{separator}\n"); - } - if success_hints { - println!( - "Hints:\n{separator}\n{}\n{separator}\n", - exercise.hint, - separator = separator(), - ); - } + println!(); + match exercise.mode { + Mode::Compile => success!("Successfully ran {}!", exercise), + Mode::Test => success!("Successfully tested {}!", exercise), + Mode::Clippy => success!("Successfully checked {}!", exercise), + } - println!("You can keep working on this exercise,"); - println!( - "or jump into the next one by removing the {} comment:", - style("`I AM NOT DONE`").bold() - ); - println!(); - for context_line in context { - let formatted_line = if context_line.important { - format!("{}", style(context_line.line).bold()) - } else { - context_line.line - }; + if let State::Pending(context) = exercise.state()? { + println!( + "\nYou can keep working on this exercise, +or jump into the next one by removing the {} comment:\n", + style("`I AM NOT DONE`").bold() + ); + + for context_line in context { + let formatted_line = if context_line.important { + format!("{}", style(context_line.line).bold()) + } else { + context_line.line + }; + + println!( + "{:>2} {} {}", + style(context_line.number).blue().bold(), + style("|").blue(), + formatted_line, + ); + } + return Ok(VerifyState::Failed(exercise)); + } + num_done += 1; println!( - "{:>2} {} {}", - style(context_line.number).blue().bold(), - style("|").blue(), - formatted_line, + "Progress: {num_done}/{total} ({:.1}%)\n", + num_done as f32 / total as f32 * 100.0, ); } - Ok(false) -} - -fn separator() -> console::StyledObject<&'static str> { - style("====================").bold() + Ok(VerifyState::AllExercisesDone) } From 445441ce25ec8658bcdec6b2038d17e893a5903f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 23:16:57 +0200 Subject: [PATCH 0608/1432] Make gen-dev-cargo-toml a separate package so that `cargo install` only installs `rustlings` --- Cargo.lock | 9 +++++++++ Cargo.toml | 14 +++++++++++--- dev/Cargo.toml | 4 ++-- gen-dev-cargo-toml/Cargo.toml | 10 ++++++++++ .../src/main.rs | 6 +++--- tests/dev_cargo_bins.rs | 2 +- 6 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 gen-dev-cargo-toml/Cargo.toml rename src/bin/gen-dev-cargo-toml.rs => gen-dev-cargo-toml/src/main.rs (86%) diff --git a/Cargo.lock b/Cargo.lock index 4aaec38c24..e03980caae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,6 +330,15 @@ dependencies = [ "libc", ] +[[package]] +name = "gen-dev-cargo-toml" +version = "0.0.0" +dependencies = [ + "anyhow", + "serde", + "toml_edit", +] + [[package]] name = "glob" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 3c18741707..d80550a0b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,9 @@ exclude = [ "tests/fixture/success", "dev", ] +members = [ + "gen-dev-cargo-toml", +] [workspace.package] version = "6.0.0" @@ -16,6 +19,11 @@ authors = [ license = "MIT" edition = "2021" +[workspace.dependencies] +anyhow = "1.0.81" +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!" @@ -26,7 +34,7 @@ license.workspace = true edition.workspace = true [dependencies] -anyhow = "1.0.81" +anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" crossterm = "0.27.0" @@ -34,8 +42,8 @@ notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" -serde = { version = "1.0.197", features = ["derive"] } -toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } +serde.workspace = true +toml_edit.workspace = true which = "6.0.1" winnow = "0.6.5" diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 7868b97c3d..ed9b3ed357 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,5 +1,5 @@ -# This file is a hack to allow using `cargo r` to test `rustlings` during development. -# You shouldn't edit it manually. It is created and updated by running `cargo run --bin gen-dev-cargo-toml`. +# 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 = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, diff --git a/gen-dev-cargo-toml/Cargo.toml b/gen-dev-cargo-toml/Cargo.toml new file mode 100644 index 0000000000..8922ae8c39 --- /dev/null +++ b/gen-dev-cargo-toml/Cargo.toml @@ -0,0 +1,10 @@ +[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/src/bin/gen-dev-cargo-toml.rs b/gen-dev-cargo-toml/src/main.rs similarity index 86% rename from src/bin/gen-dev-cargo-toml.rs rename to gen-dev-cargo-toml/src/main.rs index ff8f31db7c..622762ad60 100644 --- a/src/bin/gen-dev-cargo-toml.rs +++ b/gen-dev-cargo-toml/src/main.rs @@ -1,5 +1,5 @@ // Generates `dev/Cargo.toml` such that it is synced with `info.toml`. -// `dev/Cargo.toml` is a hack to allow using `cargo r` to test `rustlings` +// `dev/Cargo.toml` is a hack to allow using `cargo run` to test `rustlings` // during development. use anyhow::{bail, Context, Result}; @@ -30,8 +30,8 @@ fn main() -> Result<()> { let mut buf = Vec::with_capacity(1 << 14); buf.extend_from_slice( - b"# This file is a hack to allow using `cargo r` to test `rustlings` during development. -# You shouldn't edit it manually. It is created and updated by running `cargo run --bin gen-dev-cargo-toml`. + 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", ); diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs index 7f1771b629..ad4832f8a7 100644 --- a/tests/dev_cargo_bins.rs +++ b/tests/dev_cargo_bins.rs @@ -1,5 +1,5 @@ // Makes sure that `dev/Cargo.toml` is synced with `info.toml`. -// When this test fails, you just need to run `cargo run --bin gen-dev-cargo-toml`. +// When this test fails, you just need to run `cargo run -p gen-dev-cargo-toml`. use serde::Deserialize; use std::fs; From 919ba88413fcc495ebde288960079f6f627eb5b7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 00:43:36 +0200 Subject: [PATCH 0609/1432] Use the pretty format when testing even with -q --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 450acf4553..d5ca254eb6 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -114,7 +114,7 @@ impl Exercise { pub fn run(&self) -> Result { match self.mode { Mode::Compile => self.cargo_cmd("run", &[]), - Mode::Test => self.cargo_cmd("test", &["--", "--nocapture"]), + Mode::Test => self.cargo_cmd("test", &["--", "--nocapture", "--format", "pretty"]), Mode::Clippy => self.cargo_cmd( "clippy", &["--", "-D", "warnings", "-D", "clippy::float_cmp"], From 5a233398ebe7078767404bd05ca06e08b37fb3d4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 00:44:43 +0200 Subject: [PATCH 0610/1432] Fix tests --- src/run.rs | 10 +++++----- tests/dev_cargo_bins.rs | 2 +- tests/integration_tests.rs | 13 +------------ 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/run.rs b/src/run.rs index 0a09eccf34..ee2d3b4fa9 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use std::io::{stdout, Write}; use crate::exercise::Exercise; @@ -17,11 +17,11 @@ pub fn run(exercise: &Exercise) -> Result<()> { stdout.flush()?; } - if output.status.success() { - success!("Successfully ran {}", exercise); - } else { - warn!("Ran {} with errors", exercise); + if !output.status.success() { + bail!("Ran {exercise} with errors"); } + success!("Successfully ran {}", exercise); + Ok(()) } diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs index ad4832f8a7..c3faea92cc 100644 --- a/tests/dev_cargo_bins.rs +++ b/tests/dev_cargo_bins.rs @@ -17,7 +17,7 @@ struct InfoToml { #[test] fn dev_cargo_bins() { - let content = fs::read_to_string("exercises/Cargo.toml").unwrap(); + let content = fs::read_to_string("dev/Cargo.toml").unwrap(); let exercises = toml_edit::de::from_str::(&fs::read_to_string("info.toml").unwrap()) .unwrap() diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index d1694a396f..d853521f66 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -194,24 +194,13 @@ fn run_test_exercise_does_not_prompt() { #[test] fn run_single_test_success_with_output() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["--nocapture", "run", "testSuccess"]) - .current_dir("tests/fixture/success/") - .assert() - .code(0) - .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS")); -} - -#[test] -fn run_single_test_success_without_output() { Command::cargo_bin("rustlings") .unwrap() .args(["run", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .code(0) - .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS").not()); + .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS")); } #[test] From 157fe016e5f335e04b4dd322623d35a244faa2ab Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 00:49:22 +0200 Subject: [PATCH 0611/1432] Remove ui.rs --- src/main.rs | 3 --- src/run.rs | 3 ++- src/ui.rs | 28 ---------------------------- src/verify.rs | 7 ++++--- 4 files changed, 6 insertions(+), 35 deletions(-) delete mode 100644 src/ui.rs diff --git a/src/main.rs b/src/main.rs index 20ec290fd6..c62837d3a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,9 +17,6 @@ use std::time::Duration; use std::{io, thread}; use verify::VerifyState; -#[macro_use] -mod ui; - mod embedded; mod exercise; mod init; diff --git a/src/run.rs b/src/run.rs index ee2d3b4fa9..38f4e0e22c 100644 --- a/src/run.rs +++ b/src/run.rs @@ -21,7 +21,8 @@ pub fn run(exercise: &Exercise) -> Result<()> { bail!("Ran {exercise} with errors"); } - success!("Successfully ran {}", exercise); + // TODO: Color + println!("Successfully ran {exercise}"); Ok(()) } diff --git a/src/ui.rs b/src/ui.rs deleted file mode 100644 index 22d60d964a..0000000000 --- a/src/ui.rs +++ /dev/null @@ -1,28 +0,0 @@ -macro_rules! print_emoji { - ($emoji:expr, $sign:expr, $color: ident, $fmt:literal, $ex:expr) => {{ - use console::{style, Emoji}; - use std::env; - let formatstr = format!($fmt, $ex); - if env::var_os("NO_EMOJI").is_some() { - println!("{} {}", style($sign).$color(), style(formatstr).$color()); - } else { - println!( - "{} {}", - style(Emoji($emoji, $sign)).$color(), - style(formatstr).$color() - ); - } - }}; -} - -macro_rules! warn { - ($fmt:literal, $ex:expr) => {{ - print_emoji!("⚠️ ", "!", red, $fmt, $ex); - }}; -} - -macro_rules! success { - ($fmt:literal, $ex:expr) => {{ - print_emoji!("βœ… ", "βœ“", green, $fmt, $ex); - }}; -} diff --git a/src/verify.rs b/src/verify.rs index 5b053940a9..5beb20693a 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -39,10 +39,11 @@ pub fn verify<'a>( } println!(); + // TODO: Color match exercise.mode { - Mode::Compile => success!("Successfully ran {}!", exercise), - Mode::Test => success!("Successfully tested {}!", exercise), - Mode::Clippy => success!("Successfully checked {}!", exercise), + Mode::Compile => println!("Successfully ran {exercise}!"), + Mode::Test => println!("Successfully tested {exercise}!"), + Mode::Clippy => println!("Successfully checked {exercise}!"), } if let State::Pending(context) = exercise.state()? { From 1d2c2cffd2f5a85714c3902bec6e8b198fede12f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 00:59:13 +0200 Subject: [PATCH 0612/1432] Remove .gitattributes --- .gitattributes | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index efdba87644..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -* text=auto -*.sh text eol=lf From 0bf51c6a0de117d7f28ddf4a253bfc0306f2e78b Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 00:59:21 +0200 Subject: [PATCH 0613/1432] Ignore .ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0bbbc542b7..0ea1fb6d02 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ target/ *.o public/ .direnv/ +.ignore # Local Netlify folder .netlify From b0f19fd862d659d2d4b01f2faa6b006fe2c60561 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 03:04:53 +0200 Subject: [PATCH 0614/1432] Start with the TUI --- Cargo.lock | 26 ------ Cargo.toml | 1 - src/consts.rs | 59 ++++++++++++ src/main.rs | 245 ++++---------------------------------------------- src/tui.rs | 92 +++++++++++++++++++ src/verify.rs | 16 ++-- 6 files changed, 180 insertions(+), 259 deletions(-) create mode 100644 src/consts.rs create mode 100644 src/tui.rs diff --git a/Cargo.lock b/Cargo.lock index e03980caae..33d3030a80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,19 +207,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "console" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.52.0", -] - [[package]] name = "crossbeam-channel" version = "0.5.12" @@ -278,12 +265,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "equivalent" version = "1.0.1" @@ -447,12 +428,6 @@ dependencies = [ "libc", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.153" @@ -714,7 +689,6 @@ dependencies = [ "anyhow", "assert_cmd", "clap", - "console", "crossterm", "glob", "notify-debouncer-mini", diff --git a/Cargo.toml b/Cargo.toml index d80550a0b8..da09ba18fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ edition.workspace = true [dependencies] anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } -console = "0.15.8" crossterm = "0.27.0" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000000..40bf150fd7 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,59 @@ +pub const WELCOME: &str = r" welcome to... + _ _ _ + _ __ _ _ ___| |_| (_)_ __ __ _ ___ + | '__| | | / __| __| | | '_ \ / _` / __| + | | | |_| \__ \ |_| | | | | | (_| \__ \ + |_| \__,_|___/\__|_|_|_| |_|\__, |___/ + |___/"; + +pub const DEFAULT_OUT: &str = + "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: + +1. The central concept behind Rustlings is that you solve exercises. These + exercises usually have some sort of syntax error in them, which will cause + them to fail compilation or testing. Sometimes there's a logic error instead + of a syntax error. No matter what error, it's your job to find it and fix it! + You'll know when you fixed it because then, the exercise will compile and + Rustlings will be able to move on to the next exercise. +2. If you run Rustlings in watch mode (which we recommend), it'll automatically + start with the first exercise. Don't get confused by an error message popping + up as soon as you run Rustlings! This is part of the exercise that you're + supposed to solve, so open the exercise file in an editor and start your + detective work! +3. If you're stuck on an exercise, there is a helpful hint you can view by typing + 'hint' (in watch mode), or running `rustlings hint exercise_name`. +4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! + (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, + and sometimes, other learners do too so you can help each other out! + +Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. +Make sure to have your editor open in the `rustlings` directory!"; + +pub const FENISH_LINE: &str = "+----------------------------------------------------+ +| You made it to the Fe-nish line! | ++-------------------------- ------------------------+ + \\/\x1b[31m + β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ + β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ + β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ + β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ + β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ + β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ + β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ + β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ + β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m + +We hope you enjoyed learning about the various aspects of Rust! +If you noticed any issues, please don't hesitate to report them to our repo. +You can also contribute your own exercises to help the greater community! + +Before reporting an issue or contributing, please read our guidelines: +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; diff --git a/src/main.rs b/src/main.rs index c62837d3a8..47afd019af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,22 @@ +use crate::consts::{DEFAULT_OUT, WELCOME}; use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, ExerciseList}; use crate::run::run; +use crate::tui::tui; use crate::verify::verify; use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; -use console::Emoji; -use notify_debouncer_mini::notify::RecursiveMode; -use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; -use std::io::{BufRead, Write}; +use std::io::Write; use std::path::Path; use std::process::exit; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::mpsc::{channel, RecvTimeoutError}; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use std::{io, thread}; use verify::VerifyState; +mod consts; mod embedded; mod exercise; mod init; mod run; +mod tui; mod verify; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code @@ -37,7 +33,7 @@ enum Subcommands { Init, /// Verify all exercises according to the recommended order Verify, - /// Rerun `verify` when files were edited + /// Same as just running `rustlings` without a subcommand. Watch, /// Run/Test a single exercise Run { @@ -106,21 +102,20 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let command = args.command.unwrap_or_else(|| { - println!("{DEFAULT_OUT}\n"); - exit(0); - }); - - match command { + match args.command { + None | Some(Subcommands::Watch) => { + println!("{DEFAULT_OUT}\n"); + tui(&exercises)?; + } // `Init` is handled above. - Subcommands::Init => (), - Subcommands::List { + Some(Subcommands::Init) => (), + Some(Subcommands::List { paths, names, filter, unsolved, solved, - } => { + }) => { if !paths && !names { println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); } @@ -188,90 +183,30 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini ); exit(0); } - - Subcommands::Run { name } => { + Some(Subcommands::Run { name }) => { let exercise = find_exercise(&name, &exercises)?; run(exercise).unwrap_or_else(|_| exit(1)); } - - Subcommands::Reset { name } => { + Some(Subcommands::Reset { name }) => { let exercise = find_exercise(&name, &exercises)?; EMBEDDED_FILES .write_exercise_to_disk(&exercise.path, WriteStrategy::Overwrite) .with_context(|| format!("Failed to reset the exercise {exercise}"))?; println!("The file {} has been reset!", exercise.path.display()); } - - Subcommands::Hint { name } => { + Some(Subcommands::Hint { name }) => { let exercise = find_exercise(&name, &exercises)?; println!("{}", exercise.hint); } - - Subcommands::Verify => match verify(&exercises, (0, exercises.len()))? { + Some(Subcommands::Verify) => match verify(&exercises, (0, exercises.len()))? { VerifyState::AllExercisesDone => println!("All exercises done!"), VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), }, - - Subcommands::Watch => match watch(&exercises) { - Err(e) => { - println!("Error: Could not watch your progress. Error message was {e:?}."); - println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); - exit(1); - } - Ok(WatchStatus::Finished) => { - println!( - "{emoji} All exercises completed! {emoji}", - emoji = Emoji("πŸŽ‰", "β˜…") - ); - println!("\n{FENISH_LINE}\n"); - } - Ok(WatchStatus::Unfinished) => { - println!("We hope you're enjoying learning about Rust!"); - println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again"); - } - }, } Ok(()) } -fn spawn_watch_shell( - failed_exercise_hint: Arc>>, - should_quit: Arc, -) { - println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); - - thread::spawn(move || { - let mut input = String::with_capacity(32); - let mut stdin = io::stdin().lock(); - - loop { - // Recycle input buffer. - input.clear(); - - if let Err(e) = stdin.read_line(&mut input) { - println!("error reading command: {e}"); - } - - let input = input.trim(); - if input == "hint" { - if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { - println!("{hint}"); - } - } else if input == "clear" { - println!("\x1B[2J\x1B[1;1H"); - } else if input == "quit" { - should_quit.store(true, Ordering::SeqCst); - println!("Bye!"); - } else if input == "help" { - println!("{WATCH_MODE_HELP_MESSAGE}"); - } else { - println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}"); - } - } - }); -} - fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<&'a Exercise> { if name == "next" { for exercise in exercises { @@ -290,147 +225,3 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<&'a Exerci .find(|e| e.name == name) .with_context(|| format!("No exercise found for '{name}'!")) } - -enum WatchStatus { - Finished, - Unfinished, -} - -fn watch(exercises: &[Exercise]) -> Result { - /* Clears the terminal with an ANSI escape code. - Works in UNIX and newer Windows terminals. */ - fn clear_screen() { - println!("\x1Bc"); - } - - let (tx, rx) = channel(); - let should_quit = Arc::new(AtomicBool::new(false)); - - let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; - debouncer - .watcher() - .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - - clear_screen(); - - let failed_exercise_hint = match verify(exercises, (0, exercises.len()))? { - VerifyState::AllExercisesDone => return Ok(WatchStatus::Finished), - VerifyState::Failed(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), - }; - - spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit)); - - let mut pending_exercises = Vec::with_capacity(exercises.len()); - loop { - match rx.recv_timeout(Duration::from_secs(1)) { - Ok(event) => match event { - Ok(events) => { - for event in events { - if event.kind == DebouncedEventKind::Any - && event.path.extension().is_some_and(|ext| ext == "rs") - { - pending_exercises.extend(exercises.iter().filter(|exercise| { - !exercise.looks_done().unwrap_or(false) - || event.path.ends_with(&exercise.path) - })); - let num_done = exercises.len() - pending_exercises.len(); - - clear_screen(); - - match verify( - pending_exercises.iter().copied(), - (num_done, exercises.len()), - )? { - VerifyState::AllExercisesDone => return Ok(WatchStatus::Finished), - VerifyState::Failed(exercise) => { - let hint = exercise.hint.clone(); - *failed_exercise_hint.lock().unwrap() = Some(hint); - } - } - - pending_exercises.clear(); - } - } - } - Err(e) => println!("watch error: {e:?}"), - }, - Err(RecvTimeoutError::Timeout) => { - // the timeout expired, just check the `should_quit` variable below then loop again - } - Err(e) => println!("watch error: {e:?}"), - } - // Check if we need to exit - if should_quit.load(Ordering::SeqCst) { - return Ok(WatchStatus::Unfinished); - } - } -} - -const WELCOME: &str = r" welcome to... - _ _ _ - _ __ _ _ ___| |_| (_)_ __ __ _ ___ - | '__| | | / __| __| | | '_ \ / _` / __| - | | | |_| \__ \ |_| | | | | | (_| \__ \ - |_| \__,_|___/\__|_|_|_| |_|\__, |___/ - |___/"; - -const DEFAULT_OUT: &str = - "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: - -1. The central concept behind Rustlings is that you solve exercises. These - exercises usually have some sort of syntax error in them, which will cause - them to fail compilation or testing. Sometimes there's a logic error instead - of a syntax error. No matter what error, it's your job to find it and fix it! - You'll know when you fixed it because then, the exercise will compile and - Rustlings will be able to move on to the next exercise. -2. If you run Rustlings in watch mode (which we recommend), it'll automatically - start with the first exercise. Don't get confused by an error message popping - up as soon as you run Rustlings! This is part of the exercise that you're - supposed to solve, so open the exercise file in an editor and start your - detective work! -3. If you're stuck on an exercise, there is a helpful hint you can view by typing - 'hint' (in watch mode), or running `rustlings hint exercise_name`. -4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, - and sometimes, other learners do too so you can help each other out! - -Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. -Make sure to have your editor open in the `rustlings` directory!"; - -const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: - hint - prints the current exercise's hint - clear - clears the screen - quit - quits watch mode - help - displays this help message - -Watch mode automatically re-evaluates the current exercise -when you edit a file's contents."; - -const FENISH_LINE: &str = "+----------------------------------------------------+ -| You made it to the Fe-nish line! | -+-------------------------- ------------------------+ - \\/\x1b[31m - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ - β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ - β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ - β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ - β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m - -We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, please don't hesitate to report them to our repo. -You can also contribute your own exercises to help the greater community! - -Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; diff --git a/src/tui.rs b/src/tui.rs new file mode 100644 index 0000000000..bb87365205 --- /dev/null +++ b/src/tui.rs @@ -0,0 +1,92 @@ +use anyhow::Result; +use crossterm::{ + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, +}; +use notify_debouncer_mini::{new_debouncer, notify::RecursiveMode, DebouncedEventKind}; +use ratatui::{backend::CrosstermBackend, Terminal}; +use std::{ + io::stdout, + path::Path, + sync::mpsc::{channel, RecvTimeoutError}, + time::Duration, +}; + +use crate::{ + exercise::Exercise, + verify::{verify, VerifyState}, +}; + +fn watch(exercises: &[Exercise]) -> Result<()> { + let (tx, rx) = channel(); + + let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; + debouncer + .watcher() + .watch(Path::new("exercises"), RecursiveMode::Recursive)?; + + let mut failed_exercise_hint = match verify(exercises, (0, exercises.len()))? { + VerifyState::AllExercisesDone => return Ok(()), + VerifyState::Failed(exercise) => Some(&exercise.hint), + }; + + let mut pending_exercises = Vec::with_capacity(exercises.len()); + loop { + match rx.recv_timeout(Duration::from_secs(1)) { + Ok(event) => match event { + Ok(events) => { + for event in events { + if event.kind == DebouncedEventKind::Any + && event.path.extension().is_some_and(|ext| ext == "rs") + { + pending_exercises.extend(exercises.iter().filter(|exercise| { + !exercise.looks_done().unwrap_or(false) + || event.path.ends_with(&exercise.path) + })); + let num_done = exercises.len() - pending_exercises.len(); + + match verify( + pending_exercises.iter().copied(), + (num_done, exercises.len()), + )? { + VerifyState::AllExercisesDone => return Ok(()), + VerifyState::Failed(exercise) => { + failed_exercise_hint = Some(&exercise.hint); + } + } + + pending_exercises.clear(); + } + } + } + Err(e) => println!("watch error: {e:?}"), + }, + Err(RecvTimeoutError::Timeout) => { + // the timeout expired, just check the `should_quit` variable below then loop again + } + Err(e) => println!("watch error: {e:?}"), + } + + // TODO: Check if we need to exit + } +} + +pub fn tui(exercises: &[Exercise]) -> Result<()> { + let mut stdout = stdout().lock(); + stdout.execute(EnterAlternateScreen)?; + enable_raw_mode()?; + let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; + terminal.clear()?; + + watch(exercises)?; + + drop(terminal); + stdout.execute(LeaveAlternateScreen)?; + disable_raw_mode()?; + + // TODO + println!("We hope you're enjoying learning about Rust!"); + println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again"); + + Ok(()) +} diff --git a/src/verify.rs b/src/verify.rs index 5beb20693a..aec2185c27 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use console::style; +use crossterm::style::{Attribute, ContentStyle, Stylize}; use std::io::{stdout, Write}; use crate::exercise::{Exercise, Mode, State}; @@ -50,20 +50,26 @@ pub fn verify<'a>( println!( "\nYou can keep working on this exercise, or jump into the next one by removing the {} comment:\n", - style("`I AM NOT DONE`").bold() + "`I AM NOT DONE`".bold() ); for context_line in context { let formatted_line = if context_line.important { - format!("{}", style(context_line.line).bold()) + format!("{}", context_line.line.bold()) } else { context_line.line }; println!( "{:>2} {} {}", - style(context_line.number).blue().bold(), - style("|").blue(), + ContentStyle { + foreground_color: Some(crossterm::style::Color::Blue), + background_color: None, + underline_color: None, + attributes: Attribute::Bold.into() + } + .apply(context_line.number), + "|".blue(), formatted_line, ); } From 3f2d41de9ecd174ff2b099d3000bf7eca781779d Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 03:05:07 +0200 Subject: [PATCH 0615/1432] Start with the state --- src/main.rs | 1 + src/state.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/state.rs diff --git a/src/main.rs b/src/main.rs index 47afd019af..50517850df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ mod embedded; mod exercise; mod init; mod run; +mod state; mod tui; mod verify; diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000000..e3e3299050 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,32 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::{fs, io, path::PathBuf}; + +#[derive(Serialize, Deserialize)] +pub struct ExerciseState { + pub path: PathBuf, + pub done: bool, +} + +#[derive(Serialize, Deserialize)] +pub struct State { + pub progress: Vec, +} + +impl State { + pub fn read() -> Result { + let file_content = + fs::read(".rustlings.json").context("Failed to read the file `.rustlings.json`")?; + + serde_json::de::from_slice(&file_content) + .context("Failed to deserialize the file `.rustlings.json`") + } + + pub fn write(&self) -> io::Result<()> { + // TODO: Capacity + let mut buf = Vec::with_capacity(1 << 12); + serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state"); + dbg!(buf.len()); + Ok(()) + } +} From 60155294e94acd661e4fe20cf8b72412167c772d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 6 Apr 2024 01:45:54 +0200 Subject: [PATCH 0616/1432] Rename packages --- dev/Cargo.toml | 2 +- gen-dev-cargo-toml/src/main.rs | 2 +- tests/fixture/failure/Cargo.toml | 2 +- tests/fixture/state/Cargo.toml | 2 +- tests/fixture/success/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index ed9b3ed357..1d230ebb58 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -101,6 +101,6 @@ bin = [ ] [package] -name = "rustlings" +name = "rustlings-dev" edition = "2021" publish = false diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs index 622762ad60..9a7c1bbda8 100644 --- a/gen-dev-cargo-toml/src/main.rs +++ b/gen-dev-cargo-toml/src/main.rs @@ -48,7 +48,7 @@ bin = [\n", br#"] [package] -name = "rustlings" +name = "rustlings-dev" edition = "2021" publish = false "#, diff --git a/tests/fixture/failure/Cargo.toml b/tests/fixture/failure/Cargo.toml index e111cf2bfa..7ee2f068eb 100644 --- a/tests/fixture/failure/Cargo.toml +++ b/tests/fixture/failure/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tests" +name = "failure" edition = "2021" publish = false diff --git a/tests/fixture/state/Cargo.toml b/tests/fixture/state/Cargo.toml index c8d74e47fd..adbd8ab1c3 100644 --- a/tests/fixture/state/Cargo.toml +++ b/tests/fixture/state/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tests" +name = "state" edition = "2021" publish = false diff --git a/tests/fixture/success/Cargo.toml b/tests/fixture/success/Cargo.toml index f26a44f19e..028cf35a63 100644 --- a/tests/fixture/success/Cargo.toml +++ b/tests/fixture/success/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tests" +name = "success" edition = "2021" publish = false From 06e7216c833f46299c0314bbab47f8df9fc355a3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 6 Apr 2024 01:46:09 +0200 Subject: [PATCH 0617/1432] Elimintate an itermediate variable --- tests/integration_tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index d853521f66..ccdd910ea5 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -7,8 +7,7 @@ use std::process::Command; #[test] fn runs_without_arguments() { - let mut cmd = Command::cargo_bin("rustlings").unwrap(); - cmd.assert().success(); + Command::cargo_bin("rustlings").unwrap().assert().success(); } #[test] From de9a0ed5221934b43a27921455f484e006c3ec20 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 6 Apr 2024 01:46:22 +0200 Subject: [PATCH 0618/1432] Update state --- src/state.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/state.rs b/src/state.rs index e3e3299050..60f6a3795f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,31 +1,37 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use std::{fs, io, path::PathBuf}; +use std::fs; -#[derive(Serialize, Deserialize)] -pub struct ExerciseState { - pub path: PathBuf, - pub done: bool, -} +use crate::exercise::Exercise; #[derive(Serialize, Deserialize)] pub struct State { - pub progress: Vec, + pub progress: Vec, } impl State { - pub fn read() -> Result { - let file_content = - fs::read(".rustlings.json").context("Failed to read the file `.rustlings.json`")?; + fn read(exercises: &[Exercise]) -> Option { + let file_content = fs::read(".rustlings.json").ok()?; + + let slf: Self = serde_json::de::from_slice(&file_content).ok()?; + + if slf.progress.len() != exercises.len() { + return None; + } + + Some(slf) + } - serde_json::de::from_slice(&file_content) - .context("Failed to deserialize the file `.rustlings.json`") + pub fn read_or_default(exercises: &[Exercise]) -> Self { + Self::read(exercises).unwrap_or_else(|| Self { + progress: vec![false; exercises.len()], + }) } - pub fn write(&self) -> io::Result<()> { + pub fn write(&self) -> Result<()> { // TODO: Capacity let mut buf = Vec::with_capacity(1 << 12); - serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state"); + serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; dbg!(buf.len()); Ok(()) } From c2daad8340c04eaa84525f6ee832972667068fd6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 01:15:47 +0200 Subject: [PATCH 0619/1432] Return an error instead of exiting --- src/exercise.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index d5ca254eb6..d01d427a1b 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::fs::{self, File}; use std::io::{self, BufRead, BufReader}; use std::path::PathBuf; -use std::process::{exit, Command, Output}; +use std::process::{Command, Output}; use std::{array, mem}; use winnow::ascii::{space0, Caseless}; use winnow::combinator::opt; @@ -145,13 +145,9 @@ impl Exercise { let mut line = String::with_capacity(256); loop { - let n = read_line(&mut line).unwrap_or_else(|e| { - println!( - "Failed to read the exercise file {}: {e}", - self.path.display(), - ); - exit(1); - }); + let n = read_line(&mut line).with_context(|| { + format!("Failed to read the exercise file {}", self.path.display()) + })?; // Reached the end of the file and didn't find the comment. if n == 0 { From 18342b3aa3bd43c2c013614935f45e7d6bbaea8f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 01:16:56 +0200 Subject: [PATCH 0620/1432] Verify starting with some index --- src/verify.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/verify.rs b/src/verify.rs index aec2185c27..c4368cc75b 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -14,17 +14,16 @@ pub enum VerifyState<'a> { // Any such failures will be reported to the end user. // If the Exercise being verified is a test, the verbose boolean // determines whether or not the test harness outputs are displayed. -pub fn verify<'a>( - pending_exercises: impl IntoIterator, - progress: (usize, usize), -) -> Result> { - let (mut num_done, total) = progress; - println!( - "Progress: {num_done}/{total} ({:.1}%)\n", - num_done as f32 / total as f32 * 100.0, - ); +pub fn verify(exercises: &[Exercise], mut current_exercise_ind: usize) -> Result> { + while current_exercise_ind < exercises.len() { + let exercise = &exercises[current_exercise_ind]; + + println!( + "Progress: {current_exercise_ind}/{} ({:.1}%)\n", + exercises.len(), + current_exercise_ind as f32 / exercises.len() as f32 * 100.0, + ); - for exercise in pending_exercises { let output = exercise.run()?; { @@ -76,11 +75,7 @@ or jump into the next one by removing the {} comment:\n", return Ok(VerifyState::Failed(exercise)); } - num_done += 1; - println!( - "Progress: {num_done}/{total} ({:.1}%)\n", - num_done as f32 / total as f32 * 100.0, - ); + current_exercise_ind += 1; } Ok(VerifyState::AllExercisesDone) From 0819bbe21fc86315d3acdcdb2bc14b21f3acb788 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 01:17:53 +0200 Subject: [PATCH 0621/1432] Can't use Ratatui for the watch mode :( --- src/main.rs | 22 ++--- src/tui.rs | 92 -------------------- src/watch.rs | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 103 deletions(-) delete mode 100644 src/tui.rs create mode 100644 src/watch.rs diff --git a/src/main.rs b/src/main.rs index 50517850df..e8218efe85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ -use crate::consts::{DEFAULT_OUT, WELCOME}; +use crate::consts::WELCOME; use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, ExerciseList}; use crate::run::run; -use crate::tui::tui; use crate::verify::verify; use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; +use state::State; use std::io::Write; use std::path::Path; use std::process::exit; @@ -17,8 +17,8 @@ mod exercise; mod init; mod run; mod state; -mod tui; mod verify; +mod watch; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] @@ -75,10 +75,6 @@ enum Subcommands { fn main() -> Result<()> { let args = Args::parse(); - if args.command.is_none() { - println!("\n{WELCOME}\n"); - } - which::which("cargo").context( "Failed to find `cargo`. Did you already install Rust? @@ -97,16 +93,20 @@ Then run `rustlings` for further instructions on getting started." return Ok(()); } else if !Path::new("exercises").is_dir() { println!( - "\nThe `exercises` directory wasn't found in the current directory. + " +{WELCOME} + +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." ); exit(1); } + let state = State::read_or_default(&exercises); + match args.command { None | Some(Subcommands::Watch) => { - println!("{DEFAULT_OUT}\n"); - tui(&exercises)?; + watch::watch(&state, &exercises)?; } // `Init` is handled above. Some(Subcommands::Init) => (), @@ -199,7 +199,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini let exercise = find_exercise(&name, &exercises)?; println!("{}", exercise.hint); } - Some(Subcommands::Verify) => match verify(&exercises, (0, exercises.len()))? { + Some(Subcommands::Verify) => match verify(&exercises, 0)? { VerifyState::AllExercisesDone => println!("All exercises done!"), VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), }, diff --git a/src/tui.rs b/src/tui.rs deleted file mode 100644 index bb87365205..0000000000 --- a/src/tui.rs +++ /dev/null @@ -1,92 +0,0 @@ -use anyhow::Result; -use crossterm::{ - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, - ExecutableCommand, -}; -use notify_debouncer_mini::{new_debouncer, notify::RecursiveMode, DebouncedEventKind}; -use ratatui::{backend::CrosstermBackend, Terminal}; -use std::{ - io::stdout, - path::Path, - sync::mpsc::{channel, RecvTimeoutError}, - time::Duration, -}; - -use crate::{ - exercise::Exercise, - verify::{verify, VerifyState}, -}; - -fn watch(exercises: &[Exercise]) -> Result<()> { - let (tx, rx) = channel(); - - let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; - debouncer - .watcher() - .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - - let mut failed_exercise_hint = match verify(exercises, (0, exercises.len()))? { - VerifyState::AllExercisesDone => return Ok(()), - VerifyState::Failed(exercise) => Some(&exercise.hint), - }; - - let mut pending_exercises = Vec::with_capacity(exercises.len()); - loop { - match rx.recv_timeout(Duration::from_secs(1)) { - Ok(event) => match event { - Ok(events) => { - for event in events { - if event.kind == DebouncedEventKind::Any - && event.path.extension().is_some_and(|ext| ext == "rs") - { - pending_exercises.extend(exercises.iter().filter(|exercise| { - !exercise.looks_done().unwrap_or(false) - || event.path.ends_with(&exercise.path) - })); - let num_done = exercises.len() - pending_exercises.len(); - - match verify( - pending_exercises.iter().copied(), - (num_done, exercises.len()), - )? { - VerifyState::AllExercisesDone => return Ok(()), - VerifyState::Failed(exercise) => { - failed_exercise_hint = Some(&exercise.hint); - } - } - - pending_exercises.clear(); - } - } - } - Err(e) => println!("watch error: {e:?}"), - }, - Err(RecvTimeoutError::Timeout) => { - // the timeout expired, just check the `should_quit` variable below then loop again - } - Err(e) => println!("watch error: {e:?}"), - } - - // TODO: Check if we need to exit - } -} - -pub fn tui(exercises: &[Exercise]) -> Result<()> { - let mut stdout = stdout().lock(); - stdout.execute(EnterAlternateScreen)?; - enable_raw_mode()?; - let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; - terminal.clear()?; - - watch(exercises)?; - - drop(terminal); - stdout.execute(LeaveAlternateScreen)?; - disable_raw_mode()?; - - // TODO - println!("We hope you're enjoying learning about Rust!"); - println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again"); - - Ok(()) -} diff --git a/src/watch.rs b/src/watch.rs new file mode 100644 index 0000000000..92da20dd46 --- /dev/null +++ b/src/watch.rs @@ -0,0 +1,240 @@ +use anyhow::Result; +use crossterm::{ + style::{Attribute, ContentStyle, Stylize}, + terminal::{Clear, ClearType}, + ExecutableCommand, +}; +use notify_debouncer_mini::{ + new_debouncer, notify::RecursiveMode, DebounceEventResult, DebouncedEventKind, +}; +use std::{ + fmt::Write as _, + io::{self, BufRead, StdoutLock, Write}, + path::Path, + sync::mpsc::{channel, sync_channel, Receiver}, + thread, + time::Duration, +}; + +use crate::{ + exercise::{self, Exercise}, + state::State, +}; + +enum Event { + Hint, + Clear, + Quit, +} + +struct WatchState<'a> { + writer: StdoutLock<'a>, + rx: Receiver, + exercises: &'a [Exercise], + exercise: &'a Exercise, + current_exercise_ind: usize, + stdout: Option>, + stderr: Option>, + message: Option, + prompt: Vec, +} + +impl<'a> WatchState<'a> { + fn run_exercise(&mut self) -> Result { + let output = self.exercise.run()?; + + if !output.status.success() { + self.stdout = Some(output.stdout); + self.stderr = Some(output.stderr); + return Ok(false); + } + + if let exercise::State::Pending(context) = self.exercise.state()? { + let mut message = format!( + " +You can keep working on this exercise or jump into the next one by removing the {} comment: + +", + "`I AM NOT DONE`".bold(), + ); + + for context_line in context { + let formatted_line = if context_line.important { + context_line.line.bold() + } else { + context_line.line.stylize() + }; + + writeln!( + message, + "{:>2} {} {}", + ContentStyle { + foreground_color: Some(crossterm::style::Color::Blue), + background_color: None, + underline_color: None, + attributes: Attribute::Bold.into() + } + .apply(context_line.number), + "|".blue(), + formatted_line, + )?; + } + + self.stdout = Some(output.stdout); + self.message = Some(message); + return Ok(false); + } + + Ok(true) + } + + fn try_recv_event(&mut self) -> Result<()> { + let Ok(events) = self.rx.recv_timeout(Duration::from_millis(100)) else { + return Ok(()); + }; + + if let Some(current_exercise_ind) = events? + .iter() + .filter_map(|event| { + if event.kind != DebouncedEventKind::Any + || !event.path.extension().is_some_and(|ext| ext == "rs") + { + return None; + } + + self.exercises + .iter() + .position(|exercise| event.path.ends_with(&exercise.path)) + }) + .min() + { + self.current_exercise_ind = current_exercise_ind; + } else { + return Ok(()); + }; + + while self.current_exercise_ind < self.exercises.len() { + self.exercise = &self.exercises[self.current_exercise_ind]; + if !self.run_exercise()? { + break; + } + + self.current_exercise_ind += 1; + } + + Ok(()) + } + + fn prompt(&mut self) -> io::Result<()> { + self.writer.write_all(&self.prompt)?; + self.writer.flush() + } + + fn render(&mut self) -> Result<()> { + self.writer.execute(Clear(ClearType::All))?; + + if let Some(stdout) = &self.stdout { + self.writer.write_all(stdout)?; + } + + if let Some(stderr) = &self.stderr { + self.writer.write_all(stderr)?; + } + + if let Some(message) = &self.message { + self.writer.write_all(message.as_bytes())?; + } + + self.prompt()?; + + Ok(()) + } +} + +pub fn watch(state: &State, exercises: &[Exercise]) -> Result<()> { + let (tx, rx) = channel(); + let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; + debouncer + .watcher() + .watch(Path::new("exercises"), RecursiveMode::Recursive)?; + + let current_exercise_ind = state.progress.iter().position(|done| *done).unwrap_or(0); + + let exercise = &exercises[current_exercise_ind]; + + let writer = io::stdout().lock(); + + let mut watch_state = WatchState { + writer, + rx, + exercises, + exercise, + current_exercise_ind, + stdout: None, + stderr: None, + message: None, + prompt: format!( + "\n\n{}int/{}lear/{}uit? ", + "h".bold(), + "c".bold(), + "q".bold() + ) + .into_bytes(), + }; + + watch_state.run_exercise()?; + watch_state.render()?; + + let (tx, rx) = sync_channel(0); + thread::spawn(move || { + let mut stdin = io::stdin().lock(); + let mut stdin_buf = String::with_capacity(8); + + loop { + stdin.read_line(&mut stdin_buf).unwrap(); + + let event = match stdin_buf.trim() { + "h" | "hint" => Some(Event::Hint), + "c" | "clear" => Some(Event::Clear), + "q" | "quit" => Some(Event::Quit), + _ => None, + }; + + stdin_buf.clear(); + + if tx.send(event).is_err() { + break; + }; + } + }); + + loop { + watch_state.try_recv_event()?; + + if let Ok(event) = rx.try_recv() { + match event { + Some(Event::Hint) => { + watch_state + .writer + .write_all(watch_state.exercise.hint.as_bytes())?; + watch_state.prompt()?; + } + Some(Event::Clear) => { + watch_state.render()?; + } + Some(Event::Quit) => break, + None => { + watch_state.writer.write_all(b"Invalid command")?; + watch_state.prompt()?; + } + } + } + } + + watch_state.writer.write_all(b" +We hope you're enjoying learning Rust! +If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. +")?; + + Ok(()) +} From f6db88aca860b229e97712a612cee8ab4436b764 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 03:03:37 +0200 Subject: [PATCH 0622/1432] Started with list --- src/list.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 96 +++-------------------------------------------------- 2 files changed, 97 insertions(+), 92 deletions(-) create mode 100644 src/list.rs diff --git a/src/list.rs b/src/list.rs new file mode 100644 index 0000000000..f8713b074e --- /dev/null +++ b/src/list.rs @@ -0,0 +1,93 @@ +use std::{io, time::Duration}; + +use anyhow::Result; +use crossterm::{ + event::{self, KeyCode, KeyEventKind}, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, +}; +use ratatui::{ + backend::CrosstermBackend, + layout::Constraint, + style::{Modifier, Style, Stylize}, + text::Span, + widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, + Terminal, +}; + +use crate::{exercise::Exercise, state::State}; + +// 40 FPS. +const UPDATE_INTERVAL: Duration = Duration::from_millis(25); + +pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { + let mut stdout = io::stdout().lock(); + + stdout.execute(EnterAlternateScreen)?; + enable_raw_mode()?; + + let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; + terminal.clear()?; + + let header = Row::new(["State", "Name", "Path"]); + + let max_name_len = exercises + .iter() + .map(|exercise| exercise.name.len()) + .max() + .unwrap_or(4) as u16; + + let widths = [ + Constraint::Length(7), + Constraint::Length(max_name_len), + Constraint::Fill(1), + ]; + + let rows = exercises + .iter() + .zip(&state.progress) + .map(|(exercise, done)| { + let state = if *done { + "DONE".green() + } else { + "PENDING".yellow() + }; + Row::new([ + state, + Span::raw(&exercise.name), + Span::raw(exercise.path.to_string_lossy()), + ]) + }) + .collect::>(); + + let table = Table::new(rows, widths) + .header(header) + .column_spacing(2) + .highlight_spacing(HighlightSpacing::Always) + .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) + .highlight_symbol("πŸ¦€"); + + let mut table_state = TableState::default().with_selected(Some(0)); + + loop { + terminal.draw(|frame| { + let area = frame.size(); + + frame.render_stateful_widget(&table, area, &mut table_state); + })?; + + if event::poll(UPDATE_INTERVAL)? { + if let event::Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { + break; + } + } + } + } + + drop(terminal); + stdout.execute(LeaveAlternateScreen)?; + disable_raw_mode()?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index e8218efe85..34d1784ae3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ use crate::verify::verify; use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; use state::State; -use std::io::Write; use std::path::Path; use std::process::exit; use verify::VerifyState; @@ -15,6 +14,7 @@ mod consts; mod embedded; mod exercise; mod init; +mod list; mod run; mod state; mod verify; @@ -52,24 +52,7 @@ enum Subcommands { name: String, }, /// List the exercises available in Rustlings - List { - /// Show only the paths of the exercises - #[arg(short, long)] - paths: bool, - /// Show only the names of the exercises - #[arg(short, long)] - names: bool, - /// Provide a string to match exercise names. - /// Comma separated patterns are accepted - #[arg(short, long)] - filter: Option, - /// Display only exercises not yet solved - #[arg(short, long)] - unsolved: bool, - /// Display only exercises that have been solved - #[arg(short, long)] - solved: bool, - }, + List, } fn main() -> Result<()> { @@ -110,79 +93,8 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini } // `Init` is handled above. Some(Subcommands::Init) => (), - Some(Subcommands::List { - paths, - names, - filter, - unsolved, - solved, - }) => { - if !paths && !names { - println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); - } - let mut exercises_done: u16 = 0; - let lowercase_filter = filter - .as_ref() - .map(|s| s.to_lowercase()) - .unwrap_or_default(); - let filters = lowercase_filter - .split(',') - .filter_map(|f| { - let f = f.trim(); - if f.is_empty() { - None - } else { - Some(f) - } - }) - .collect::>(); - - for exercise in &exercises { - let fname = exercise.path.to_string_lossy(); - let filter_cond = filters - .iter() - .any(|f| exercise.name.contains(f) || fname.contains(f)); - let looks_done = exercise.looks_done()?; - let status = if looks_done { - exercises_done += 1; - "Done" - } else { - "Pending" - }; - let solve_cond = - (looks_done && solved) || (!looks_done && unsolved) || (!solved && !unsolved); - if solve_cond && (filter_cond || filter.is_none()) { - let line = if paths { - format!("{fname}\n") - } else if names { - format!("{}\n", exercise.name) - } else { - format!("{:<17}\t{fname:<46}\t{status:<7}\n", exercise.name) - }; - // Somehow using println! leads to the binary panicking - // when its output is piped. - // So, we're handling a Broken Pipe error and exiting with 0 anyway - let stdout = std::io::stdout(); - { - let mut handle = stdout.lock(); - handle.write_all(line.as_bytes()).unwrap_or_else(|e| { - match e.kind() { - std::io::ErrorKind::BrokenPipe => exit(0), - _ => exit(1), - }; - }); - } - } - } - - let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0; - println!( - "Progress: You completed {} / {} exercises ({:.1} %).", - exercises_done, - exercises.len(), - percentage_progress - ); - exit(0); + Some(Subcommands::List) => { + list::list(&state, &exercises)?; } Some(Subcommands::Run { name }) => { let exercise = find_exercise(&name, &exercises)?; From 729385362c06da0c90015bb2d4b6b341d2cd489b Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 03:03:59 +0200 Subject: [PATCH 0623/1432] Update deps --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33d3030a80..ee4694376a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,9 +711,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" From 372290a796eb27b28edaf2475ebbb4e6e09090b3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 03:38:18 +0200 Subject: [PATCH 0624/1432] Done navigation --- src/list.rs | 83 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/src/list.rs b/src/list.rs index f8713b074e..82c3e46533 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,34 +1,22 @@ -use std::{io, time::Duration}; - use anyhow::Result; use crossterm::{ - event::{self, KeyCode, KeyEventKind}, + event::{self, Event, KeyCode, KeyEventKind}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, }; use ratatui::{ backend::CrosstermBackend, layout::Constraint, - style::{Modifier, Style, Stylize}, + style::{Style, Stylize}, text::Span, - widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, + widgets::{HighlightSpacing, Row, Table, TableState}, Terminal, }; +use std::io; use crate::{exercise::Exercise, state::State}; -// 40 FPS. -const UPDATE_INTERVAL: Duration = Duration::from_millis(25); - -pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { - let mut stdout = io::stdout().lock(); - - stdout.execute(EnterAlternateScreen)?; - enable_raw_mode()?; - - let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; - terminal.clear()?; - +fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { let header = Row::new(["State", "Name", "Path"]); let max_name_len = exercises @@ -60,28 +48,69 @@ pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { }) .collect::>(); - let table = Table::new(rows, widths) + Table::new(rows, widths) .header(header) .column_spacing(2) .highlight_spacing(HighlightSpacing::Always) - .highlight_style(Style::new().add_modifier(Modifier::REVERSED)) - .highlight_symbol("πŸ¦€"); + .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50))) + .highlight_symbol("πŸ¦€") +} - let mut table_state = TableState::default().with_selected(Some(0)); +pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { + let mut stdout = io::stdout().lock(); - loop { + stdout.execute(EnterAlternateScreen)?; + enable_raw_mode()?; + + let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; + terminal.clear()?; + + let table = table(state, exercises); + + let last_ind = exercises.len() - 1; + let mut selected = 0; + let mut table_state = TableState::default().with_selected(Some(selected)); + + 'outer: loop { terminal.draw(|frame| { let area = frame.size(); frame.render_stateful_widget(&table, area, &mut table_state); })?; - if event::poll(UPDATE_INTERVAL)? { - if let event::Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { - break; - } + let key = loop { + match event::read()? { + Event::Key(key) => break key, + // Redraw + Event::Resize(_, _) => continue 'outer, + // Ignore + Event::FocusGained | Event::FocusLost | Event::Mouse(_) | Event::Paste(_) => (), + } + }; + + if key.kind != KeyEventKind::Press { + continue; + } + + match key.code { + KeyCode::Char('q') => break, + KeyCode::Down | KeyCode::Char('j') => { + selected = selected.saturating_add(1).min(last_ind); + table_state.select(Some(selected)); + } + KeyCode::Up | KeyCode::Char('k') => { + selected = selected.saturating_sub(1).max(0); + table_state.select(Some(selected)); + } + KeyCode::Home | KeyCode::Char('g') => { + selected = 0; + table_state.select(Some(selected)); + } + KeyCode::End | KeyCode::Char('G') => { + selected = last_ind; + table_state.select(Some(selected)); } + _ => (), } } From c4897139aeff2316d2b737a4e03b7491b696ce3b Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 03:41:23 +0200 Subject: [PATCH 0625/1432] Prevent unneeded redraws --- src/list.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/list.rs b/src/list.rs index 82c3e46533..b8ea27bbcd 100644 --- a/src/list.rs +++ b/src/list.rs @@ -80,7 +80,13 @@ pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { let key = loop { match event::read()? { - Event::Key(key) => break key, + Event::Key(key) => { + if key.kind != KeyEventKind::Press { + continue; + } + + break key; + } // Redraw Event::Resize(_, _) => continue 'outer, // Ignore @@ -88,10 +94,6 @@ pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { } }; - if key.kind != KeyEventKind::Press { - continue; - } - match key.code { KeyCode::Char('q') => break, KeyCode::Down | KeyCode::Char('j') => { From 7f5a18fa3478596c3c1dbdc7eb92da99b0945886 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 04:19:50 +0200 Subject: [PATCH 0626/1432] Show help message --- src/list.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/list.rs b/src/list.rs index b8ea27bbcd..7329d2b81b 100644 --- a/src/list.rs +++ b/src/list.rs @@ -6,10 +6,10 @@ use crossterm::{ }; use ratatui::{ backend::CrosstermBackend, - layout::Constraint, + layout::{Constraint, Rect}, style::{Style, Stylize}, - text::Span, - widgets::{HighlightSpacing, Row, Table, TableState}, + text::{Line, Span}, + widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, Terminal, }; use std::io; @@ -54,6 +54,7 @@ fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { .highlight_spacing(HighlightSpacing::Always) .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50))) .highlight_symbol("πŸ¦€") + .block(Block::default().borders(Borders::BOTTOM)) } pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { @@ -75,7 +76,25 @@ pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { terminal.draw(|frame| { let area = frame.size(); - frame.render_stateful_widget(&table, area, &mut table_state); + frame.render_stateful_widget( + &table, + Rect { + x: 0, + y: 0, + width: area.width, + height: area.height - 1, + }, + &mut table_state, + ); + frame.render_widget( + Span::raw("Navi: ↓/j ↑/k home/g end/G β”‚ Filter done/pending: d/p β”‚ Reset: r β”‚ Continue at: c β”‚ Quit: q"), + Rect { + x: 0, + y: area.height - 1, + width: area.width, + height: 1, + }, + ); })?; let key = loop { From e640b4a1ffec82cba6b34c0bd222f4ab65502daa Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 04:36:27 +0200 Subject: [PATCH 0627/1432] Add "Next" column --- src/list.rs | 20 +++++++++++++++----- src/state.rs | 4 +++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/list.rs b/src/list.rs index 7329d2b81b..ce809efe3b 100644 --- a/src/list.rs +++ b/src/list.rs @@ -8,7 +8,7 @@ use ratatui::{ backend::CrosstermBackend, layout::{Constraint, Rect}, style::{Style, Stylize}, - text::{Line, Span}, + text::Span, widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, Terminal, }; @@ -17,7 +17,7 @@ use std::io; use crate::{exercise::Exercise, state::State}; fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { - let header = Row::new(["State", "Name", "Path"]); + let header = Row::new(["Next", "State", "Name", "Path"]); let max_name_len = exercises .iter() @@ -26,6 +26,7 @@ fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { .unwrap_or(4) as u16; let widths = [ + Constraint::Length(4), Constraint::Length(7), Constraint::Length(max_name_len), Constraint::Fill(1), @@ -34,14 +35,23 @@ fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { let rows = exercises .iter() .zip(&state.progress) - .map(|(exercise, done)| { - let state = if *done { + .enumerate() + .map(|(ind, (exercise, done))| { + let exercise_state = if *done { "DONE".green() } else { "PENDING".yellow() }; + + let next = if ind == state.next_exercise_ind { + ">>>>".bold().red() + } else { + Span::default() + }; + Row::new([ - state, + next, + exercise_state, Span::raw(&exercise.name), Span::raw(exercise.path.to_string_lossy()), ]) diff --git a/src/state.rs b/src/state.rs index 60f6a3795f..f29dc1351a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,6 +6,7 @@ use crate::exercise::Exercise; #[derive(Serialize, Deserialize)] pub struct State { + pub next_exercise_ind: usize, pub progress: Vec, } @@ -15,7 +16,7 @@ impl State { let slf: Self = serde_json::de::from_slice(&file_content).ok()?; - if slf.progress.len() != exercises.len() { + if slf.progress.len() != exercises.len() || slf.next_exercise_ind >= exercises.len() { return None; } @@ -24,6 +25,7 @@ impl State { pub fn read_or_default(exercises: &[Exercise]) -> Self { Self::read(exercises).unwrap_or_else(|| Self { + next_exercise_ind: 0, progress: vec![false; exercises.len()], }) } From 4f69285375342951da36346f1a1b93f7903a362f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 04:39:03 +0200 Subject: [PATCH 0628/1432] Shorten the help footer --- src/list.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index ce809efe3b..ff031cb03b 100644 --- a/src/list.rs +++ b/src/list.rs @@ -96,8 +96,10 @@ pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { }, &mut table_state, ); + + // Help footer frame.render_widget( - Span::raw("Navi: ↓/j ↑/k home/g end/G β”‚ Filter done/pending: d/p β”‚ Reset: r β”‚ Continue at: c β”‚ Quit: q"), + Span::raw("↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit"), Rect { x: 0, y: area.height - 1, From b0a475062445705853b4f861ee9e3135065f0660 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 04:59:22 +0200 Subject: [PATCH 0629/1432] Implement "continue at" --- src/list.rs | 54 ++++++++++++++++++++++++++++++---------------------- src/main.rs | 4 ++-- src/state.rs | 31 ++++++++++++++++++++++++------ src/watch.rs | 2 +- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/list.rs b/src/list.rs index ff031cb03b..bb5ba1c0e5 100644 --- a/src/list.rs +++ b/src/list.rs @@ -16,25 +16,13 @@ use std::io; use crate::{exercise::Exercise, state::State}; -fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { - let header = Row::new(["Next", "State", "Name", "Path"]); - - let max_name_len = exercises +fn rows<'s, 'e>(state: &'s State, exercises: &'e [Exercise]) -> impl Iterator> + 's +where + 'e: 's, +{ + exercises .iter() - .map(|exercise| exercise.name.len()) - .max() - .unwrap_or(4) as u16; - - let widths = [ - Constraint::Length(4), - Constraint::Length(7), - Constraint::Length(max_name_len), - Constraint::Fill(1), - ]; - - let rows = exercises - .iter() - .zip(&state.progress) + .zip(state.progress()) .enumerate() .map(|(ind, (exercise, done))| { let exercise_state = if *done { @@ -43,7 +31,7 @@ fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { "PENDING".yellow() }; - let next = if ind == state.next_exercise_ind { + let next = if ind == state.next_exercise_ind() { ">>>>".bold().red() } else { Span::default() @@ -56,9 +44,25 @@ fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { Span::raw(exercise.path.to_string_lossy()), ]) }) - .collect::>(); +} - Table::new(rows, widths) +fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { + let header = Row::new(["Next", "State", "Name", "Path"]); + + let max_name_len = exercises + .iter() + .map(|exercise| exercise.name.len()) + .max() + .unwrap_or(4) as u16; + + let widths = [ + Constraint::Length(4), + Constraint::Length(7), + Constraint::Length(max_name_len), + Constraint::Fill(1), + ]; + + Table::new(rows(state, exercises), widths) .header(header) .column_spacing(2) .highlight_spacing(HighlightSpacing::Always) @@ -67,7 +71,7 @@ fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { .block(Block::default().borders(Borders::BOTTOM)) } -pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { +pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> { let mut stdout = io::stdout().lock(); stdout.execute(EnterAlternateScreen)?; @@ -76,7 +80,7 @@ pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; terminal.clear()?; - let table = table(state, exercises); + let mut table = table(state, exercises); let last_ind = exercises.len() - 1; let mut selected = 0; @@ -143,6 +147,10 @@ pub fn list(state: &State, exercises: &[Exercise]) -> Result<()> { selected = last_ind; table_state.select(Some(selected)); } + KeyCode::Char('c') => { + state.set_next_exercise_ind(selected)?; + table = table.rows(rows(state, exercises)); + } _ => (), } } diff --git a/src/main.rs b/src/main.rs index 34d1784ae3..e82fc8080b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,7 +85,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let state = State::read_or_default(&exercises); + let mut state = State::read_or_default(&exercises); match args.command { None | Some(Subcommands::Watch) => { @@ -94,7 +94,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::List) => { - list::list(&state, &exercises)?; + list::list(&mut state, &exercises)?; } Some(Subcommands::Run { name }) => { let exercise = find_exercise(&name, &exercises)?; diff --git a/src/state.rs b/src/state.rs index f29dc1351a..5a64487382 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; use std::fs; @@ -6,8 +6,8 @@ use crate::exercise::Exercise; #[derive(Serialize, Deserialize)] pub struct State { - pub next_exercise_ind: usize, - pub progress: Vec, + next_exercise_ind: usize, + progress: Vec, } impl State { @@ -30,11 +30,30 @@ impl State { }) } - pub fn write(&self) -> Result<()> { + fn write(&self) -> Result<()> { // TODO: Capacity - let mut buf = Vec::with_capacity(1 << 12); + let mut buf = Vec::with_capacity(1024); serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; - dbg!(buf.len()); + Ok(()) } + + #[inline] + pub fn next_exercise_ind(&self) -> usize { + self.next_exercise_ind + } + + pub fn set_next_exercise_ind(&mut self, ind: usize) -> Result<()> { + if ind >= self.progress.len() { + bail!("The next exercise index is higher than the number of exercises"); + } + + self.next_exercise_ind = ind; + self.write() + } + + #[inline] + pub fn progress(&self) -> &[bool] { + &self.progress + } } diff --git a/src/watch.rs b/src/watch.rs index 92da20dd46..cc9668d009 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -158,7 +158,7 @@ pub fn watch(state: &State, exercises: &[Exercise]) -> Result<()> { .watcher() .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - let current_exercise_ind = state.progress.iter().position(|done| *done).unwrap_or(0); + let current_exercise_ind = state.next_exercise_ind(); let exercise = &exercises[current_exercise_ind]; From 2db86833a9f3fae4dc5410aac828b3071dda1984 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 13:12:40 +0200 Subject: [PATCH 0630/1432] Fix lifetimes --- src/list.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/list.rs b/src/list.rs index bb5ba1c0e5..5153e01f32 100644 --- a/src/list.rs +++ b/src/list.rs @@ -16,27 +16,31 @@ use std::io; use crate::{exercise::Exercise, state::State}; -fn rows<'s, 'e>(state: &'s State, exercises: &'e [Exercise]) -> impl Iterator> + 's +fn rows<'s, 'e, 'i>( + state: &'s State, + exercises: &'e [Exercise], +) -> impl Iterator> + 'i where - 'e: 's, + 's: 'i, + 'e: 'i, { exercises .iter() .zip(state.progress()) .enumerate() .map(|(ind, (exercise, done))| { - let exercise_state = if *done { - "DONE".green() - } else { - "PENDING".yellow() - }; - let next = if ind == state.next_exercise_ind() { ">>>>".bold().red() } else { Span::default() }; + let exercise_state = if *done { + "DONE".green() + } else { + "PENDING".yellow() + }; + Row::new([ next, exercise_state, From d988054ad851cb6ce67c77e2607322142d188804 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 16:33:00 +0200 Subject: [PATCH 0631/1432] Add UiState --- src/list.rs | 242 +++++++++++++++++++++++++++++----------------------- 1 file changed, 137 insertions(+), 105 deletions(-) diff --git a/src/list.rs b/src/list.rs index 5153e01f32..dad2182235 100644 --- a/src/list.rs +++ b/src/list.rs @@ -10,112 +10,156 @@ use ratatui::{ style::{Style, Stylize}, text::Span, widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, - Terminal, + Frame, Terminal, }; use std::io; use crate::{exercise::Exercise, state::State}; -fn rows<'s, 'e, 'i>( - state: &'s State, - exercises: &'e [Exercise], -) -> impl Iterator> + 'i -where - 's: 'i, - 'e: 'i, -{ - exercises - .iter() - .zip(state.progress()) - .enumerate() - .map(|(ind, (exercise, done))| { - let next = if ind == state.next_exercise_ind() { - ">>>>".bold().red() - } else { - Span::default() - }; - - let exercise_state = if *done { - "DONE".green() - } else { - "PENDING".yellow() - }; - - Row::new([ - next, - exercise_state, - Span::raw(&exercise.name), - Span::raw(exercise.path.to_string_lossy()), - ]) - }) +struct UiState<'a> { + pub table: Table<'a>, + selected: usize, + table_state: TableState, + last_ind: usize, } -fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> { - let header = Row::new(["Next", "State", "Name", "Path"]); - - let max_name_len = exercises - .iter() - .map(|exercise| exercise.name.len()) - .max() - .unwrap_or(4) as u16; - - let widths = [ - Constraint::Length(4), - Constraint::Length(7), - Constraint::Length(max_name_len), - Constraint::Fill(1), - ]; - - Table::new(rows(state, exercises), widths) - .header(header) - .column_spacing(2) - .highlight_spacing(HighlightSpacing::Always) - .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50))) - .highlight_symbol("πŸ¦€") - .block(Block::default().borders(Borders::BOTTOM)) +impl<'a> UiState<'a> { + pub fn rows<'s, 'i>( + state: &'s State, + exercises: &'a [Exercise], + ) -> impl Iterator> + 'i + where + 's: 'i, + 'a: 'i, + { + exercises + .iter() + .zip(state.progress()) + .enumerate() + .map(|(ind, (exercise, done))| { + let next = if ind == state.next_exercise_ind() { + ">>>>".bold().red() + } else { + Span::default() + }; + + let exercise_state = if *done { + "DONE".green() + } else { + "PENDING".yellow() + }; + + Row::new([ + next, + exercise_state, + Span::raw(&exercise.name), + Span::raw(exercise.path.to_string_lossy()), + ]) + }) + } + + pub fn new(state: &State, exercises: &'a [Exercise]) -> Self { + let header = Row::new(["Next", "State", "Name", "Path"]); + + let max_name_len = exercises + .iter() + .map(|exercise| exercise.name.len()) + .max() + .unwrap_or(4) as u16; + + let widths = [ + Constraint::Length(4), + Constraint::Length(7), + Constraint::Length(max_name_len), + Constraint::Fill(1), + ]; + + let rows = Self::rows(state, exercises); + + let table = Table::new(rows, widths) + .header(header) + .column_spacing(2) + .highlight_spacing(HighlightSpacing::Always) + .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50))) + .highlight_symbol("πŸ¦€") + .block(Block::default().borders(Borders::BOTTOM)); + + let selected = 0; + let table_state = TableState::default().with_selected(Some(selected)); + let last_ind = exercises.len() - 1; + + Self { + table, + selected, + table_state, + last_ind, + } + } + + fn select(&mut self, ind: usize) { + self.selected = ind; + self.table_state.select(Some(ind)); + } + + pub fn select_next(&mut self) { + self.select(self.selected.saturating_add(1).min(self.last_ind)); + } + + pub fn select_previous(&mut self) { + self.select(self.selected.saturating_sub(1)); + } + + #[inline] + pub fn select_first(&mut self) { + self.select(0); + } + + #[inline] + pub fn select_last(&mut self) { + self.select(self.last_ind); + } + + pub fn draw(&mut self, frame: &mut Frame) { + let area = frame.size(); + + frame.render_stateful_widget( + &self.table, + Rect { + x: 0, + y: 0, + width: area.width, + height: area.height - 1, + }, + &mut self.table_state, + ); + + // Help footer + let footer = + "↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit"; + frame.render_widget( + Span::raw(footer), + Rect { + x: 0, + y: area.height - 1, + width: area.width, + height: 1, + }, + ); + } } pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> { let mut stdout = io::stdout().lock(); - stdout.execute(EnterAlternateScreen)?; enable_raw_mode()?; let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; terminal.clear()?; - let mut table = table(state, exercises); - - let last_ind = exercises.len() - 1; - let mut selected = 0; - let mut table_state = TableState::default().with_selected(Some(selected)); + let mut ui_state = UiState::new(state, exercises); 'outer: loop { - terminal.draw(|frame| { - let area = frame.size(); - - frame.render_stateful_widget( - &table, - Rect { - x: 0, - y: 0, - width: area.width, - height: area.height - 1, - }, - &mut table_state, - ); - - // Help footer - frame.render_widget( - Span::raw("↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit"), - Rect { - x: 0, - y: area.height - 1, - width: area.width, - height: 1, - }, - ); - })?; + terminal.draw(|frame| ui_state.draw(frame))?; let key = loop { match event::read()? { @@ -135,25 +179,13 @@ pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> { match key.code { KeyCode::Char('q') => break, - KeyCode::Down | KeyCode::Char('j') => { - selected = selected.saturating_add(1).min(last_ind); - table_state.select(Some(selected)); - } - KeyCode::Up | KeyCode::Char('k') => { - selected = selected.saturating_sub(1).max(0); - table_state.select(Some(selected)); - } - KeyCode::Home | KeyCode::Char('g') => { - selected = 0; - table_state.select(Some(selected)); - } - KeyCode::End | KeyCode::Char('G') => { - selected = last_ind; - table_state.select(Some(selected)); - } + KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(), + KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(), + KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), + KeyCode::End | KeyCode::Char('G') => ui_state.select_last(), KeyCode::Char('c') => { - state.set_next_exercise_ind(selected)?; - table = table.rows(rows(state, exercises)); + state.set_next_exercise_ind(ui_state.selected)?; + ui_state.table = ui_state.table.rows(UiState::rows(state, exercises)); } _ => (), } From 8c31d38fa17970d0d2dc696922eb8cb329a6fdb9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 17:57:20 +0200 Subject: [PATCH 0632/1432] Better variable name --- src/list.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/list.rs b/src/list.rs index dad2182235..cff0a3d82a 100644 --- a/src/list.rs +++ b/src/list.rs @@ -133,11 +133,10 @@ impl<'a> UiState<'a> { &mut self.table_state, ); - // Help footer - let footer = + let help_footer = "↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit"; frame.render_widget( - Span::raw(footer), + Span::raw(help_footer), Rect { x: 0, y: area.height - 1, From 3bd26c7a24a97f9b4b87c453fbdbb06fe9971920 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 19:01:08 +0200 Subject: [PATCH 0633/1432] State -> StateFile --- src/list.rs | 20 ++++++++++---------- src/main.rs | 17 +++++++++-------- src/{state.rs => state_file.rs} | 4 ++-- src/watch.rs | 6 +++--- 4 files changed, 24 insertions(+), 23 deletions(-) rename src/{state.rs => state_file.rs} (97%) diff --git a/src/list.rs b/src/list.rs index cff0a3d82a..c59b8d8478 100644 --- a/src/list.rs +++ b/src/list.rs @@ -14,7 +14,7 @@ use ratatui::{ }; use std::io; -use crate::{exercise::Exercise, state::State}; +use crate::{exercise::Exercise, state_file::StateFile}; struct UiState<'a> { pub table: Table<'a>, @@ -25,7 +25,7 @@ struct UiState<'a> { impl<'a> UiState<'a> { pub fn rows<'s, 'i>( - state: &'s State, + state_file: &'s StateFile, exercises: &'a [Exercise], ) -> impl Iterator> + 'i where @@ -34,10 +34,10 @@ impl<'a> UiState<'a> { { exercises .iter() - .zip(state.progress()) + .zip(state_file.progress()) .enumerate() .map(|(ind, (exercise, done))| { - let next = if ind == state.next_exercise_ind() { + let next = if ind == state_file.next_exercise_ind() { ">>>>".bold().red() } else { Span::default() @@ -58,7 +58,7 @@ impl<'a> UiState<'a> { }) } - pub fn new(state: &State, exercises: &'a [Exercise]) -> Self { + pub fn new(state_file: &StateFile, exercises: &'a [Exercise]) -> Self { let header = Row::new(["Next", "State", "Name", "Path"]); let max_name_len = exercises @@ -74,7 +74,7 @@ impl<'a> UiState<'a> { Constraint::Fill(1), ]; - let rows = Self::rows(state, exercises); + let rows = Self::rows(state_file, exercises); let table = Table::new(rows, widths) .header(header) @@ -147,7 +147,7 @@ impl<'a> UiState<'a> { } } -pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> { +pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { let mut stdout = io::stdout().lock(); stdout.execute(EnterAlternateScreen)?; enable_raw_mode()?; @@ -155,7 +155,7 @@ pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> { let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; terminal.clear()?; - let mut ui_state = UiState::new(state, exercises); + let mut ui_state = UiState::new(state_file, exercises); 'outer: loop { terminal.draw(|frame| ui_state.draw(frame))?; @@ -183,8 +183,8 @@ pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> { KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), KeyCode::End | KeyCode::Char('G') => ui_state.select_last(), KeyCode::Char('c') => { - state.set_next_exercise_ind(ui_state.selected)?; - ui_state.table = ui_state.table.rows(UiState::rows(state, exercises)); + state_file.set_next_exercise_ind(ui_state.selected)?; + ui_state.table = ui_state.table.rows(UiState::rows(state_file, exercises)); } _ => (), } diff --git a/src/main.rs b/src/main.rs index e82fc8080b..3d691b0881 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,6 @@ -use crate::consts::WELCOME; -use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; -use crate::exercise::{Exercise, ExerciseList}; -use crate::run::run; -use crate::verify::verify; use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; -use state::State; +use state_file::StateFile; use std::path::Path; use std::process::exit; use verify::VerifyState; @@ -16,10 +11,16 @@ mod exercise; mod init; mod list; mod run; -mod state; +mod state_file; mod verify; mod watch; +use crate::consts::WELCOME; +use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; +use crate::exercise::{Exercise, ExerciseList}; +use crate::run::run; +use crate::verify::verify; + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -85,7 +86,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let mut state = State::read_or_default(&exercises); + let mut state = StateFile::read_or_default(&exercises); match args.command { None | Some(Subcommands::Watch) => { diff --git a/src/state.rs b/src/state_file.rs similarity index 97% rename from src/state.rs rename to src/state_file.rs index 5a64487382..ca7ed342f1 100644 --- a/src/state.rs +++ b/src/state_file.rs @@ -5,12 +5,12 @@ use std::fs; use crate::exercise::Exercise; #[derive(Serialize, Deserialize)] -pub struct State { +pub struct StateFile { next_exercise_ind: usize, progress: Vec, } -impl State { +impl StateFile { fn read(exercises: &[Exercise]) -> Option { let file_content = fs::read(".rustlings.json").ok()?; diff --git a/src/watch.rs b/src/watch.rs index cc9668d009..1503fdfe29 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -18,7 +18,7 @@ use std::{ use crate::{ exercise::{self, Exercise}, - state::State, + state_file::StateFile, }; enum Event { @@ -151,14 +151,14 @@ You can keep working on this exercise or jump into the next one by removing the } } -pub fn watch(state: &State, exercises: &[Exercise]) -> Result<()> { +pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { let (tx, rx) = channel(); let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; debouncer .watcher() .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - let current_exercise_ind = state.next_exercise_ind(); + let current_exercise_ind = state_file.next_exercise_ind(); let exercise = &exercises[current_exercise_ind]; From 0a674a158da0d519f03a88bfabf31d98c0e064c6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 19:05:29 +0200 Subject: [PATCH 0634/1432] Separate UiState --- src/list.rs | 144 ++------------------------------------------- src/list/state.rs | 145 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 139 deletions(-) create mode 100644 src/list/state.rs diff --git a/src/list.rs b/src/list.rs index c59b8d8478..4d26702dd8 100644 --- a/src/list.rs +++ b/src/list.rs @@ -4,148 +4,14 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, }; -use ratatui::{ - backend::CrosstermBackend, - layout::{Constraint, Rect}, - style::{Style, Stylize}, - text::Span, - widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, - Frame, Terminal, -}; +use ratatui::{backend::CrosstermBackend, Terminal}; use std::io; -use crate::{exercise::Exercise, state_file::StateFile}; - -struct UiState<'a> { - pub table: Table<'a>, - selected: usize, - table_state: TableState, - last_ind: usize, -} - -impl<'a> UiState<'a> { - pub fn rows<'s, 'i>( - state_file: &'s StateFile, - exercises: &'a [Exercise], - ) -> impl Iterator> + 'i - where - 's: 'i, - 'a: 'i, - { - exercises - .iter() - .zip(state_file.progress()) - .enumerate() - .map(|(ind, (exercise, done))| { - let next = if ind == state_file.next_exercise_ind() { - ">>>>".bold().red() - } else { - Span::default() - }; - - let exercise_state = if *done { - "DONE".green() - } else { - "PENDING".yellow() - }; - - Row::new([ - next, - exercise_state, - Span::raw(&exercise.name), - Span::raw(exercise.path.to_string_lossy()), - ]) - }) - } - - pub fn new(state_file: &StateFile, exercises: &'a [Exercise]) -> Self { - let header = Row::new(["Next", "State", "Name", "Path"]); - - let max_name_len = exercises - .iter() - .map(|exercise| exercise.name.len()) - .max() - .unwrap_or(4) as u16; - - let widths = [ - Constraint::Length(4), - Constraint::Length(7), - Constraint::Length(max_name_len), - Constraint::Fill(1), - ]; - - let rows = Self::rows(state_file, exercises); +mod state; - let table = Table::new(rows, widths) - .header(header) - .column_spacing(2) - .highlight_spacing(HighlightSpacing::Always) - .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50))) - .highlight_symbol("πŸ¦€") - .block(Block::default().borders(Borders::BOTTOM)); - - let selected = 0; - let table_state = TableState::default().with_selected(Some(selected)); - let last_ind = exercises.len() - 1; - - Self { - table, - selected, - table_state, - last_ind, - } - } - - fn select(&mut self, ind: usize) { - self.selected = ind; - self.table_state.select(Some(ind)); - } - - pub fn select_next(&mut self) { - self.select(self.selected.saturating_add(1).min(self.last_ind)); - } - - pub fn select_previous(&mut self) { - self.select(self.selected.saturating_sub(1)); - } - - #[inline] - pub fn select_first(&mut self) { - self.select(0); - } - - #[inline] - pub fn select_last(&mut self) { - self.select(self.last_ind); - } - - pub fn draw(&mut self, frame: &mut Frame) { - let area = frame.size(); - - frame.render_stateful_widget( - &self.table, - Rect { - x: 0, - y: 0, - width: area.width, - height: area.height - 1, - }, - &mut self.table_state, - ); +use crate::{exercise::Exercise, state_file::StateFile}; - let help_footer = - "↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit"; - frame.render_widget( - Span::raw(help_footer), - Rect { - x: 0, - y: area.height - 1, - width: area.width, - height: 1, - }, - ); - } -} +use self::state::UiState; pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { let mut stdout = io::stdout().lock(); @@ -183,7 +49,7 @@ pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), KeyCode::End | KeyCode::Char('G') => ui_state.select_last(), KeyCode::Char('c') => { - state_file.set_next_exercise_ind(ui_state.selected)?; + state_file.set_next_exercise_ind(ui_state.selected())?; ui_state.table = ui_state.table.rows(UiState::rows(state_file, exercises)); } _ => (), diff --git a/src/list/state.rs b/src/list/state.rs new file mode 100644 index 0000000000..3d2f0a628d --- /dev/null +++ b/src/list/state.rs @@ -0,0 +1,145 @@ +use ratatui::{ + layout::{Constraint, Rect}, + style::{Style, Stylize}, + text::Span, + widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, + Frame, +}; + +use crate::{exercise::Exercise, state_file::StateFile}; + +pub struct UiState<'a> { + pub table: Table<'a>, + selected: usize, + table_state: TableState, + last_ind: usize, +} + +impl<'a> UiState<'a> { + pub fn rows<'s, 'i>( + state_file: &'s StateFile, + exercises: &'a [Exercise], + ) -> impl Iterator> + 'i + where + 's: 'i, + 'a: 'i, + { + exercises + .iter() + .zip(state_file.progress()) + .enumerate() + .map(|(ind, (exercise, done))| { + let next = if ind == state_file.next_exercise_ind() { + ">>>>".bold().red() + } else { + Span::default() + }; + + let exercise_state = if *done { + "DONE".green() + } else { + "PENDING".yellow() + }; + + Row::new([ + next, + exercise_state, + Span::raw(&exercise.name), + Span::raw(exercise.path.to_string_lossy()), + ]) + }) + } + + pub fn new(state_file: &StateFile, exercises: &'a [Exercise]) -> Self { + let header = Row::new(["Next", "State", "Name", "Path"]); + + let max_name_len = exercises + .iter() + .map(|exercise| exercise.name.len()) + .max() + .unwrap_or(4) as u16; + + let widths = [ + Constraint::Length(4), + Constraint::Length(7), + Constraint::Length(max_name_len), + Constraint::Fill(1), + ]; + + let rows = Self::rows(state_file, exercises); + + let table = Table::new(rows, widths) + .header(header) + .column_spacing(2) + .highlight_spacing(HighlightSpacing::Always) + .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50))) + .highlight_symbol("πŸ¦€") + .block(Block::default().borders(Borders::BOTTOM)); + + let selected = 0; + let table_state = TableState::default().with_selected(Some(selected)); + let last_ind = exercises.len() - 1; + + Self { + table, + selected, + table_state, + last_ind, + } + } + + #[inline] + pub fn selected(&self) -> usize { + self.selected + } + + fn select(&mut self, ind: usize) { + self.selected = ind; + self.table_state.select(Some(ind)); + } + + pub fn select_next(&mut self) { + self.select(self.selected.saturating_add(1).min(self.last_ind)); + } + + pub fn select_previous(&mut self) { + self.select(self.selected.saturating_sub(1)); + } + + #[inline] + pub fn select_first(&mut self) { + self.select(0); + } + + #[inline] + pub fn select_last(&mut self) { + self.select(self.last_ind); + } + + pub fn draw(&mut self, frame: &mut Frame) { + let area = frame.size(); + + frame.render_stateful_widget( + &self.table, + Rect { + x: 0, + y: 0, + width: area.width, + height: area.height - 1, + }, + &mut self.table_state, + ); + + let help_footer = + "↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit"; + frame.render_widget( + Span::raw(help_footer), + Rect { + x: 0, + y: area.height - 1, + width: area.width, + height: 1, + }, + ); + } +} From 9a4ee47c527251fc3efacacc31bd0e73ef527969 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 19:29:16 +0200 Subject: [PATCH 0635/1432] Separate WatchState --- src/watch.rs | 181 +++---------------------------------------- src/watch/state.rs | 186 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 169 deletions(-) create mode 100644 src/watch/state.rs diff --git a/src/watch.rs b/src/watch.rs index 1503fdfe29..967f98c186 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,25 +1,18 @@ use anyhow::Result; -use crossterm::{ - style::{Attribute, ContentStyle, Stylize}, - terminal::{Clear, ClearType}, - ExecutableCommand, -}; -use notify_debouncer_mini::{ - new_debouncer, notify::RecursiveMode, DebounceEventResult, DebouncedEventKind, -}; +use notify_debouncer_mini::{new_debouncer, notify::RecursiveMode}; use std::{ - fmt::Write as _, - io::{self, BufRead, StdoutLock, Write}, + io::{self, BufRead, Write}, path::Path, - sync::mpsc::{channel, sync_channel, Receiver}, + sync::mpsc::{channel, sync_channel}, thread, time::Duration, }; -use crate::{ - exercise::{self, Exercise}, - state_file::StateFile, -}; +mod state; + +use crate::{exercise::Exercise, state_file::StateFile}; + +use self::state::WatchState; enum Event { Hint, @@ -27,130 +20,6 @@ enum Event { Quit, } -struct WatchState<'a> { - writer: StdoutLock<'a>, - rx: Receiver, - exercises: &'a [Exercise], - exercise: &'a Exercise, - current_exercise_ind: usize, - stdout: Option>, - stderr: Option>, - message: Option, - prompt: Vec, -} - -impl<'a> WatchState<'a> { - fn run_exercise(&mut self) -> Result { - let output = self.exercise.run()?; - - if !output.status.success() { - self.stdout = Some(output.stdout); - self.stderr = Some(output.stderr); - return Ok(false); - } - - if let exercise::State::Pending(context) = self.exercise.state()? { - let mut message = format!( - " -You can keep working on this exercise or jump into the next one by removing the {} comment: - -", - "`I AM NOT DONE`".bold(), - ); - - for context_line in context { - let formatted_line = if context_line.important { - context_line.line.bold() - } else { - context_line.line.stylize() - }; - - writeln!( - message, - "{:>2} {} {}", - ContentStyle { - foreground_color: Some(crossterm::style::Color::Blue), - background_color: None, - underline_color: None, - attributes: Attribute::Bold.into() - } - .apply(context_line.number), - "|".blue(), - formatted_line, - )?; - } - - self.stdout = Some(output.stdout); - self.message = Some(message); - return Ok(false); - } - - Ok(true) - } - - fn try_recv_event(&mut self) -> Result<()> { - let Ok(events) = self.rx.recv_timeout(Duration::from_millis(100)) else { - return Ok(()); - }; - - if let Some(current_exercise_ind) = events? - .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { - return None; - } - - self.exercises - .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) - }) - .min() - { - self.current_exercise_ind = current_exercise_ind; - } else { - return Ok(()); - }; - - while self.current_exercise_ind < self.exercises.len() { - self.exercise = &self.exercises[self.current_exercise_ind]; - if !self.run_exercise()? { - break; - } - - self.current_exercise_ind += 1; - } - - Ok(()) - } - - fn prompt(&mut self) -> io::Result<()> { - self.writer.write_all(&self.prompt)?; - self.writer.flush() - } - - fn render(&mut self) -> Result<()> { - self.writer.execute(Clear(ClearType::All))?; - - if let Some(stdout) = &self.stdout { - self.writer.write_all(stdout)?; - } - - if let Some(stderr) = &self.stderr { - self.writer.write_all(stderr)?; - } - - if let Some(message) = &self.message { - self.writer.write_all(message.as_bytes())?; - } - - self.prompt()?; - - Ok(()) - } -} - pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { let (tx, rx) = channel(); let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; @@ -158,29 +27,7 @@ pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { .watcher() .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - let current_exercise_ind = state_file.next_exercise_ind(); - - let exercise = &exercises[current_exercise_ind]; - - let writer = io::stdout().lock(); - - let mut watch_state = WatchState { - writer, - rx, - exercises, - exercise, - current_exercise_ind, - stdout: None, - stderr: None, - message: None, - prompt: format!( - "\n\n{}int/{}lear/{}uit? ", - "h".bold(), - "c".bold(), - "q".bold() - ) - .into_bytes(), - }; + let mut watch_state = WatchState::new(state_file, exercises, rx); watch_state.run_exercise()?; watch_state.render()?; @@ -214,24 +61,20 @@ pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { if let Ok(event) = rx.try_recv() { match event { Some(Event::Hint) => { - watch_state - .writer - .write_all(watch_state.exercise.hint.as_bytes())?; - watch_state.prompt()?; + watch_state.show_hint()?; } Some(Event::Clear) => { watch_state.render()?; } Some(Event::Quit) => break, None => { - watch_state.writer.write_all(b"Invalid command")?; - watch_state.prompt()?; + watch_state.handle_invalid_cmd()?; } } } } - watch_state.writer.write_all(b" + watch_state.into_writer().write_all(b" We hope you're enjoying learning Rust! If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. ")?; diff --git a/src/watch/state.rs b/src/watch/state.rs new file mode 100644 index 0000000000..40f48effea --- /dev/null +++ b/src/watch/state.rs @@ -0,0 +1,186 @@ +use anyhow::Result; +use crossterm::{ + style::{Attribute, ContentStyle, Stylize}, + terminal::{Clear, ClearType}, + ExecutableCommand, +}; +use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; +use std::{ + fmt::Write as _, + io::{self, StdoutLock, Write as _}, + sync::mpsc::Receiver, + time::Duration, +}; + +use crate::{ + exercise::{Exercise, State}, + state_file::StateFile, +}; + +pub struct WatchState<'a> { + writer: StdoutLock<'a>, + rx: Receiver, + exercises: &'a [Exercise], + exercise: &'a Exercise, + current_exercise_ind: usize, + stdout: Option>, + stderr: Option>, + message: Option, + prompt: Vec, +} + +impl<'a> WatchState<'a> { + pub fn new( + state_file: &StateFile, + exercises: &'a [Exercise], + rx: Receiver, + ) -> Self { + let current_exercise_ind = state_file.next_exercise_ind(); + let exercise = &exercises[current_exercise_ind]; + + let writer = io::stdout().lock(); + + let prompt = format!( + "\n\n{}int/{}lear/{}uit? ", + "h".bold(), + "c".bold(), + "q".bold() + ) + .into_bytes(); + + Self { + writer, + rx, + exercises, + exercise, + current_exercise_ind, + stdout: None, + stderr: None, + message: None, + prompt, + } + } + + #[inline] + pub fn into_writer(self) -> StdoutLock<'a> { + self.writer + } + + pub fn run_exercise(&mut self) -> Result { + let output = self.exercise.run()?; + + if !output.status.success() { + self.stdout = Some(output.stdout); + self.stderr = Some(output.stderr); + return Ok(false); + } + + if let State::Pending(context) = self.exercise.state()? { + let mut message = format!( + " +You can keep working on this exercise or jump into the next one by removing the {} comment: + +", + "`I AM NOT DONE`".bold(), + ); + + for context_line in context { + let formatted_line = if context_line.important { + context_line.line.bold() + } else { + context_line.line.stylize() + }; + + writeln!( + message, + "{:>2} {} {}", + ContentStyle { + foreground_color: Some(crossterm::style::Color::Blue), + background_color: None, + underline_color: None, + attributes: Attribute::Bold.into() + } + .apply(context_line.number), + "|".blue(), + formatted_line, + )?; + } + + self.stdout = Some(output.stdout); + self.message = Some(message); + return Ok(false); + } + + Ok(true) + } + + pub fn try_recv_event(&mut self) -> Result<()> { + let Ok(events) = self.rx.recv_timeout(Duration::from_millis(100)) else { + return Ok(()); + }; + + if let Some(current_exercise_ind) = events? + .iter() + .filter_map(|event| { + if event.kind != DebouncedEventKind::Any + || !event.path.extension().is_some_and(|ext| ext == "rs") + { + return None; + } + + self.exercises + .iter() + .position(|exercise| event.path.ends_with(&exercise.path)) + }) + .min() + { + self.current_exercise_ind = current_exercise_ind; + } else { + return Ok(()); + }; + + while self.current_exercise_ind < self.exercises.len() { + self.exercise = &self.exercises[self.current_exercise_ind]; + if !self.run_exercise()? { + break; + } + + self.current_exercise_ind += 1; + } + + Ok(()) + } + + pub fn show_prompt(&mut self) -> io::Result<()> { + self.writer.write_all(&self.prompt)?; + self.writer.flush() + } + + pub fn render(&mut self) -> io::Result<()> { + self.writer.execute(Clear(ClearType::All))?; + + if let Some(stdout) = &self.stdout { + self.writer.write_all(stdout)?; + } + + if let Some(stderr) = &self.stderr { + self.writer.write_all(stderr)?; + } + + if let Some(message) = &self.message { + self.writer.write_all(message.as_bytes())?; + } + + self.show_prompt() + } + + pub fn show_hint(&mut self) -> io::Result<()> { + self.writer.write_all(self.exercise.hint.as_bytes())?; + self.show_prompt() + } + + pub fn handle_invalid_cmd(&mut self) -> io::Result<()> { + self.writer.write_all(b"Invalid command")?; + self.show_prompt() + } +} From db43efe3ec9d0bba5ee997923d68d2356b08a257 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 22:40:50 +0200 Subject: [PATCH 0636/1432] Update .gitignore --- .gitignore | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 0ea1fb6d02..2d4a04dc67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,27 @@ +# Cargo target/ /tests/fixture/*/Cargo.lock /dev/Cargo.lock -*.swp -**/*.rs.bk +# State file +.rustlings-state.json + +# oranda +public/ +.netlify + +# OS .DS_Store -*.pdb +.direnv/ + +# Editor +*.swp .idea +*.iml + +# VS Code extension recommendations .vscode/* !.vscode/extensions.json -*.iml -*.o -public/ -.direnv/ -.ignore -# Local Netlify folder -.netlify +# Ignore file for editors like Helix +.ignore From 99c9ab467b3e57f9dca080a6fe9c1dbd991a3fdb Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 22:43:59 +0200 Subject: [PATCH 0637/1432] Implement resetting --- src/exercise.rs | 8 ++++++- src/list.rs | 6 +++++ src/main.rs | 57 +++++++++++++++++++++++------------------------ src/state_file.rs | 15 ++++++++++--- 4 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index d01d427a1b..508f4776df 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -10,7 +10,7 @@ use winnow::ascii::{space0, Caseless}; use winnow::combinator::opt; use winnow::Parser; -use crate::embedded::EMBEDDED_FILES; +use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; // The number of context lines above and below a highlighted line. const CONTEXT: usize = 2; @@ -220,6 +220,12 @@ impl Exercise { pub fn looks_done(&self) -> Result { self.state().map(|state| state == State::Done) } + + pub fn reset(&self) -> Result<()> { + EMBEDDED_FILES + .write_exercise_to_disk(&self.path, WriteStrategy::Overwrite) + .with_context(|| format!("Failed to reset the exercise {self}")) + } } impl Display for Exercise { diff --git a/src/list.rs b/src/list.rs index 4d26702dd8..e2af21d3dc 100644 --- a/src/list.rs +++ b/src/list.rs @@ -48,6 +48,12 @@ pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), KeyCode::End | KeyCode::Char('G') => ui_state.select_last(), + KeyCode::Char('r') => { + let selected = ui_state.selected(); + exercises[selected].reset()?; + state_file.reset(selected)?; + ui_state.table = ui_state.table.rows(UiState::rows(state_file, exercises)); + } KeyCode::Char('c') => { state_file.set_next_exercise_ind(ui_state.selected())?; ui_state.table = ui_state.table.rows(UiState::rows(state_file, exercises)); diff --git a/src/main.rs b/src/main.rs index 3d691b0881..81f6617535 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ mod verify; mod watch; use crate::consts::WELCOME; -use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, ExerciseList}; use crate::run::run; use crate::verify::verify; @@ -56,6 +55,26 @@ enum Subcommands { List, } +fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<(usize, &'a Exercise)> { + if name == "next" { + for (ind, exercise) in exercises.iter().enumerate() { + if !exercise.looks_done()? { + return Ok((ind, exercise)); + } + } + + println!("πŸŽ‰ Congratulations! You have done all the exercises!"); + println!("πŸ”š There are no more exercises to do next!"); + exit(0); + } + + exercises + .iter() + .enumerate() + .find(|(_, exercise)| exercise.name == name) + .with_context(|| format!("No exercise found for '{name}'!")) +} + fn main() -> Result<()> { let args = Args::parse(); @@ -86,30 +105,29 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let mut state = StateFile::read_or_default(&exercises); + let mut state_file = StateFile::read_or_default(&exercises); match args.command { None | Some(Subcommands::Watch) => { - watch::watch(&state, &exercises)?; + watch::watch(&state_file, &exercises)?; } // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::List) => { - list::list(&mut state, &exercises)?; + list::list(&mut state_file, &exercises)?; } Some(Subcommands::Run { name }) => { - let exercise = find_exercise(&name, &exercises)?; + let (_, exercise) = find_exercise(&name, &exercises)?; run(exercise).unwrap_or_else(|_| exit(1)); } Some(Subcommands::Reset { name }) => { - let exercise = find_exercise(&name, &exercises)?; - EMBEDDED_FILES - .write_exercise_to_disk(&exercise.path, WriteStrategy::Overwrite) - .with_context(|| format!("Failed to reset the exercise {exercise}"))?; + let (ind, exercise) = find_exercise(&name, &exercises)?; + exercise.reset()?; + state_file.reset(ind)?; println!("The file {} has been reset!", exercise.path.display()); } Some(Subcommands::Hint { name }) => { - let exercise = find_exercise(&name, &exercises)?; + let (_, exercise) = find_exercise(&name, &exercises)?; println!("{}", exercise.hint); } Some(Subcommands::Verify) => match verify(&exercises, 0)? { @@ -120,22 +138,3 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini Ok(()) } - -fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<&'a Exercise> { - if name == "next" { - for exercise in exercises { - if !exercise.looks_done()? { - return Ok(exercise); - } - } - - println!("πŸŽ‰ Congratulations! You have done all the exercises!"); - println!("πŸ”š There are no more exercises to do next!"); - exit(0); - } - - exercises - .iter() - .find(|e| e.name == name) - .with_context(|| format!("No exercise found for '{name}'!")) -} diff --git a/src/state_file.rs b/src/state_file.rs index ca7ed342f1..693c78dcbc 100644 --- a/src/state_file.rs +++ b/src/state_file.rs @@ -10,9 +10,11 @@ pub struct StateFile { progress: Vec, } +const BAD_INDEX_ERR: &str = "The next exercise index is higher than the number of exercises"; + impl StateFile { fn read(exercises: &[Exercise]) -> Option { - let file_content = fs::read(".rustlings.json").ok()?; + let file_content = fs::read(".rustlings-state.json").ok()?; let slf: Self = serde_json::de::from_slice(&file_content).ok()?; @@ -34,6 +36,8 @@ impl StateFile { // TODO: Capacity let mut buf = Vec::with_capacity(1024); serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; + fs::write(".rustlings-state.json", buf) + .context("Failed to write the state file `.rustlings-state.json`")?; Ok(()) } @@ -45,9 +49,8 @@ impl StateFile { pub fn set_next_exercise_ind(&mut self, ind: usize) -> Result<()> { if ind >= self.progress.len() { - bail!("The next exercise index is higher than the number of exercises"); + bail!(BAD_INDEX_ERR); } - self.next_exercise_ind = ind; self.write() } @@ -56,4 +59,10 @@ impl StateFile { pub fn progress(&self) -> &[bool] { &self.progress } + + pub fn reset(&mut self, ind: usize) -> Result<()> { + let done = self.progress.get_mut(ind).context(BAD_INDEX_ERR)?; + *done = false; + self.write() + } } From 93f8d1610d293e57fd5002a9755c1f91a31ba891 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 23:37:40 +0200 Subject: [PATCH 0638/1432] Some renamings --- src/exercise.rs | 4 ++-- src/init.rs | 2 +- src/main.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 508f4776df..ae47d5e6a5 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -41,11 +41,11 @@ pub enum Mode { } #[derive(Deserialize)] -pub struct ExerciseList { +pub struct InfoFile { pub exercises: Vec, } -impl ExerciseList { +impl InfoFile { pub fn parse() -> Result { // Read a local `info.toml` if it exists. // Mainly to let the tests work for now. diff --git a/src/init.rs b/src/init.rs index 6af32351b8..df2d19d8b9 100644 --- a/src/init.rs +++ b/src/init.rs @@ -56,7 +56,7 @@ fn create_vscode_dir() -> Result<()> { Ok(()) } -pub fn init_rustlings(exercises: &[Exercise]) -> Result<()> { +pub fn init(exercises: &[Exercise]) -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { bail!( "A directory with the name `exercises` and a file with the name `Cargo.toml` already exist diff --git a/src/main.rs b/src/main.rs index 81f6617535..3f10a8bdaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ mod verify; mod watch; use crate::consts::WELCOME; -use crate::exercise::{Exercise, ExerciseList}; +use crate::exercise::{Exercise, InfoFile}; use crate::run::run; use crate::verify::verify; @@ -84,10 +84,10 @@ Did you already install Rust? Try running `cargo --version` to diagnose the problem.", )?; - let exercises = ExerciseList::parse()?.exercises; + let exercises = InfoFile::parse()?.exercises; if matches!(args.command, Some(Subcommands::Init)) { - init::init_rustlings(&exercises).context("Initialization failed")?; + init::init(&exercises).context("Initialization failed")?; println!( "\nDone initialization!\n Run `cd rustlings` to go into the generated directory. From db25cc91576a05b02edd3754df85eb5668cec83f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 23:54:32 +0200 Subject: [PATCH 0639/1432] Ignore .rustlings-state.json --- src/init.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index df2d19d8b9..bc561eaffc 100644 --- a/src/init.rs +++ b/src/init.rs @@ -36,7 +36,8 @@ publish = false } fn create_gitignore() -> io::Result<()> { - let gitignore = b"/target"; + let gitignore = b"/target +/.rustlings-state.json"; OpenOptions::new() .create_new(true) .write(true) From 394ca402a8883581dc040546b4ca18b07d76a7f2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 23:57:54 +0200 Subject: [PATCH 0640/1432] Remove the info_toml_content field --- rustlings-macros/src/lib.rs | 1 - src/embedded.rs | 1 - src/exercise.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 598b5c357a..d8da666e1a 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -75,7 +75,6 @@ pub fn include_files(_: TokenStream) -> TokenStream { quote! { EmbeddedFiles { - info_toml_content: ::std::include_str!("../info.toml"), exercises_dir: ExercisesDir { readme: EmbeddedFile { path: "exercises/README.md", diff --git a/src/embedded.rs b/src/embedded.rs index 56b4b61833..1e2d677037 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -65,7 +65,6 @@ struct ExercisesDir { } pub struct EmbeddedFiles { - pub info_toml_content: &'static str, exercises_dir: ExercisesDir, } diff --git a/src/exercise.rs b/src/exercise.rs index ae47d5e6a5..c9fb3312bf 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -52,7 +52,7 @@ impl InfoFile { if let Ok(file_content) = fs::read_to_string("info.toml") { toml_edit::de::from_str(&file_content) } else { - toml_edit::de::from_str(EMBEDDED_FILES.info_toml_content) + toml_edit::de::from_str(include_str!("../info.toml")) } .context("Failed to parse `info.toml`") } From 3a4f2bebb487f3fef9ce222674eede86722824b3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 00:35:51 +0200 Subject: [PATCH 0641/1432] Remove test because of defaulting to watch mode --- tests/integration_tests.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index ccdd910ea5..2219fea499 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,14 +1,7 @@ use assert_cmd::prelude::*; use glob::glob; use predicates::boolean::PredicateBooleanExt; -use std::fs::File; -use std::io::Read; -use std::process::Command; - -#[test] -fn runs_without_arguments() { - Command::cargo_bin("rustlings").unwrap().assert().success(); -} +use std::{fs::File, io::Read, process::Command}; #[test] fn fails_when_in_wrong_dir() { From c2501ae733f27cf3d9f14cf1b14e437c8675d80c Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 00:36:10 +0200 Subject: [PATCH 0642/1432] Remove list tests because of the TUI --- tests/integration_tests.rs | 54 -------------------------------------- 1 file changed, 54 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 2219fea499..f8f4383fb1 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -194,57 +194,3 @@ fn run_single_test_success_with_output() { .code(0) .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS")); } - -#[test] -fn run_rustlings_list() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["list"]) - .current_dir("tests/fixture/success") - .assert() - .success(); -} - -#[test] -fn run_rustlings_list_no_pending() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["list"]) - .current_dir("tests/fixture/success") - .assert() - .success() - .stdout(predicates::str::contains("Pending").not()); -} - -#[test] -fn run_rustlings_list_both_done_and_pending() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["list"]) - .current_dir("tests/fixture/state") - .assert() - .success() - .stdout(predicates::str::contains("Done").and(predicates::str::contains("Pending"))); -} - -#[test] -fn run_rustlings_list_without_pending() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["list", "--solved"]) - .current_dir("tests/fixture/state") - .assert() - .success() - .stdout(predicates::str::contains("Pending").not()); -} - -#[test] -fn run_rustlings_list_without_done() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["list", "--unsolved"]) - .current_dir("tests/fixture/state") - .assert() - .success() - .stdout(predicates::str::contains("Done").not()); -} From 25e855a009c47d30bfa4da93a93d8390df20fe45 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 00:36:26 +0200 Subject: [PATCH 0643/1432] Merge imports --- src/exercise.rs | 23 ++++++++++++++--------- src/main.rs | 16 ++++++++-------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index c9fb3312bf..232d7f951d 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,14 +1,19 @@ use anyhow::{Context, Result}; use serde::Deserialize; -use std::fmt::{self, Debug, Display, Formatter}; -use std::fs::{self, File}; -use std::io::{self, BufRead, BufReader}; -use std::path::PathBuf; -use std::process::{Command, Output}; -use std::{array, mem}; -use winnow::ascii::{space0, Caseless}; -use winnow::combinator::opt; -use winnow::Parser; +use std::{ + array, + fmt::{self, Debug, Display, Formatter}, + fs::{self, File}, + io::{self, BufRead, BufReader}, + mem, + path::PathBuf, + process::{Command, Output}, +}; +use winnow::{ + ascii::{space0, Caseless}, + combinator::opt, + Parser, +}; use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; diff --git a/src/main.rs b/src/main.rs index 3f10a8bdaf..cba525a14c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,6 @@ use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; -use state_file::StateFile; -use std::path::Path; -use std::process::exit; -use verify::VerifyState; +use std::{path::Path, process::exit}; mod consts; mod embedded; @@ -15,10 +12,13 @@ mod state_file; mod verify; mod watch; -use crate::consts::WELCOME; -use crate::exercise::{Exercise, InfoFile}; -use crate::run::run; -use crate::verify::verify; +use self::{ + consts::WELCOME, + exercise::{Exercise, InfoFile}, + run::run, + state_file::StateFile, + verify::{verify, VerifyState}, +}; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] From bd5503a0d363384fb551f3e303d0376a08d50831 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 01:33:11 +0200 Subject: [PATCH 0644/1432] Show message on reset --- src/list.rs | 11 +++++++++-- src/list/state.rs | 18 +++++++++++------- src/main.rs | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/list.rs b/src/list.rs index e2af21d3dc..3d91b8ae38 100644 --- a/src/list.rs +++ b/src/list.rs @@ -5,7 +5,7 @@ use crossterm::{ ExecutableCommand, }; use ratatui::{backend::CrosstermBackend, Terminal}; -use std::io; +use std::{fmt::Write, io}; mod state; @@ -42,6 +42,8 @@ pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { } }; + ui_state.message.clear(); + match key.code { KeyCode::Char('q') => break, KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(), @@ -50,9 +52,14 @@ pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { KeyCode::End | KeyCode::Char('G') => ui_state.select_last(), KeyCode::Char('r') => { let selected = ui_state.selected(); - exercises[selected].reset()?; + let exercise = &exercises[selected]; + exercise.reset()?; state_file.reset(selected)?; + ui_state.table = ui_state.table.rows(UiState::rows(state_file, exercises)); + ui_state + .message + .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; } KeyCode::Char('c') => { state_file.set_next_exercise_ind(ui_state.selected())?; diff --git a/src/list/state.rs b/src/list/state.rs index 3d2f0a628d..534b5359b5 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -10,6 +10,7 @@ use crate::{exercise::Exercise, state_file::StateFile}; pub struct UiState<'a> { pub table: Table<'a>, + pub message: String, selected: usize, table_state: TableState, last_ind: usize, @@ -77,14 +78,13 @@ impl<'a> UiState<'a> { .block(Block::default().borders(Borders::BOTTOM)); let selected = 0; - let table_state = TableState::default().with_selected(Some(selected)); - let last_ind = exercises.len() - 1; Self { table, selected, - table_state, - last_ind, + table_state: TableState::default().with_selected(Some(selected)), + last_ind: exercises.len() - 1, + message: String::with_capacity(128), } } @@ -130,10 +130,14 @@ impl<'a> UiState<'a> { &mut self.table_state, ); - let help_footer = - "↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit"; + let message = if self.message.is_empty() { + // Help footer. + "↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit" + } else { + &self.message + }; frame.render_widget( - Span::raw(help_footer), + Span::raw(message), Rect { x: 0, y: area.height - 1, diff --git a/src/main.rs b/src/main.rs index cba525a14c..f6c4c2002f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,7 +124,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini let (ind, exercise) = find_exercise(&name, &exercises)?; exercise.reset()?; state_file.reset(ind)?; - println!("The file {} has been reset!", exercise.path.display()); + println!("The exercise {exercise} has been reset!"); } Some(Subcommands::Hint { name }) => { let (_, exercise) = find_exercise(&name, &exercises)?; From 0bf3f7e01f219372bea56e2c3e9144a1b76bd3af Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 01:34:41 +0200 Subject: [PATCH 0645/1432] Lowercase "filter" in help footer --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index 534b5359b5..35a906a307 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -132,7 +132,7 @@ impl<'a> UiState<'a> { let message = if self.message.is_empty() { // Help footer. - "↓/j ↑/k home/g end/G β”‚ Filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit" + "↓/j ↑/k home/g end/G β”‚ filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit" } else { &self.message }; From 05729b27a06d50d4d3516c1b62a2c7450e4ac12a Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 01:49:38 +0200 Subject: [PATCH 0646/1432] Set a list offset --- src/list/state.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 35a906a307..d2ade97e27 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -77,12 +77,15 @@ impl<'a> UiState<'a> { .highlight_symbol("πŸ¦€") .block(Block::default().borders(Borders::BOTTOM)); - let selected = 0; + let selected = state_file.next_exercise_ind(); + let table_state = TableState::default() + .with_offset(selected.saturating_sub(3)) + .with_selected(Some(selected)); Self { table, selected, - table_state: TableState::default().with_selected(Some(selected)), + table_state, last_ind: exercises.len() - 1, message: String::with_capacity(128), } From 7c4d33654fb37200905c06c198f427545fedd461 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 02:41:48 +0200 Subject: [PATCH 0647/1432] Implement done/pending filters --- src/list.rs | 30 +++++++++++++++++++++++++++--- src/list/state.rs | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/list.rs b/src/list.rs index 3d91b8ae38..d7fa05f17a 100644 --- a/src/list.rs +++ b/src/list.rs @@ -11,7 +11,7 @@ mod state; use crate::{exercise::Exercise, state_file::StateFile}; -use self::state::UiState; +use self::state::{Filter, UiState}; pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { let mut stdout = io::stdout().lock(); @@ -50,20 +50,44 @@ pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), KeyCode::End | KeyCode::Char('G') => ui_state.select_last(), + KeyCode::Char('d') => { + let message = if ui_state.filter == Filter::Done { + ui_state.filter = Filter::None; + "Disabled filter DONE" + } else { + ui_state.filter = Filter::Done; + "Enabled filter DONE β”‚ Press d again to disable the filter" + }; + + ui_state = ui_state.with_updated_rows(state_file); + ui_state.message.push_str(message); + } + KeyCode::Char('p') => { + let message = if ui_state.filter == Filter::Pending { + ui_state.filter = Filter::None; + "Disabled filter PENDING" + } else { + ui_state.filter = Filter::Pending; + "Enabled filter PENDING β”‚ Press p again to disable the filter" + }; + + ui_state = ui_state.with_updated_rows(state_file); + ui_state.message.push_str(message); + } KeyCode::Char('r') => { let selected = ui_state.selected(); let exercise = &exercises[selected]; exercise.reset()?; state_file.reset(selected)?; - ui_state.table = ui_state.table.rows(UiState::rows(state_file, exercises)); + ui_state = ui_state.with_updated_rows(state_file); ui_state .message .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; } KeyCode::Char('c') => { state_file.set_next_exercise_ind(ui_state.selected())?; - ui_state.table = ui_state.table.rows(UiState::rows(state_file, exercises)); + ui_state = ui_state.with_updated_rows(state_file); } _ => (), } diff --git a/src/list/state.rs b/src/list/state.rs index d2ade97e27..30567d1c6c 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -8,18 +8,28 @@ use ratatui::{ use crate::{exercise::Exercise, state_file::StateFile}; +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum Filter { + Done, + Pending, + None, +} + pub struct UiState<'a> { pub table: Table<'a>, pub message: String, + pub filter: Filter, + exercises: &'a [Exercise], selected: usize, table_state: TableState, last_ind: usize, } impl<'a> UiState<'a> { - pub fn rows<'s, 'i>( + fn rows<'s, 'i>( state_file: &'s StateFile, exercises: &'a [Exercise], + filter: Filter, ) -> impl Iterator> + 'i where 's: 'i, @@ -27,30 +37,41 @@ impl<'a> UiState<'a> { { exercises .iter() - .zip(state_file.progress()) + .zip(state_file.progress().iter().copied()) .enumerate() - .map(|(ind, (exercise, done))| { + .filter_map(move |(ind, (exercise, done))| { + match (filter, done) { + (Filter::Done, false) | (Filter::Pending, true) => return None, + _ => (), + } + let next = if ind == state_file.next_exercise_ind() { ">>>>".bold().red() } else { Span::default() }; - let exercise_state = if *done { + let exercise_state = if done { "DONE".green() } else { "PENDING".yellow() }; - Row::new([ + Some(Row::new([ next, exercise_state, Span::raw(&exercise.name), Span::raw(exercise.path.to_string_lossy()), - ]) + ])) }) } + pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { + let rows = Self::rows(state_file, self.exercises, self.filter); + self.table = self.table.rows(rows); + self + } + pub fn new(state_file: &StateFile, exercises: &'a [Exercise]) -> Self { let header = Row::new(["Next", "State", "Name", "Path"]); @@ -67,7 +88,8 @@ impl<'a> UiState<'a> { Constraint::Fill(1), ]; - let rows = Self::rows(state_file, exercises); + let filter = Filter::None; + let rows = Self::rows(state_file, exercises, filter); let table = Table::new(rows, widths) .header(header) @@ -84,10 +106,12 @@ impl<'a> UiState<'a> { Self { table, + message: String::with_capacity(128), + filter, + exercises, selected, table_state, last_ind: exercises.len() - 1, - message: String::with_capacity(128), } } From b5fc06bd56c6bf6a9b3d4e3dbcd4346c8256731c Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 02:46:35 +0200 Subject: [PATCH 0648/1432] Show more exercises before the selected one --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index 30567d1c6c..48c90d2825 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -101,7 +101,7 @@ impl<'a> UiState<'a> { let selected = state_file.next_exercise_ind(); let table_state = TableState::default() - .with_offset(selected.saturating_sub(3)) + .with_offset(selected.saturating_sub(10)) .with_selected(Some(selected)); Self { From 1db5de965305c0eb3f31e78217e8a52c61e15dd4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 03:08:05 +0200 Subject: [PATCH 0649/1432] Fix selection after applying filters --- src/list/state.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 48c90d2825..902e7a6a35 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -26,14 +26,16 @@ pub struct UiState<'a> { } impl<'a> UiState<'a> { - fn rows<'s, 'i>( + fn rows<'s, 'c, 'i>( state_file: &'s StateFile, exercises: &'a [Exercise], + rows_counter: &'c mut usize, filter: Filter, ) -> impl Iterator> + 'i where 's: 'i, 'a: 'i, + 'c: 'i, { exercises .iter() @@ -45,6 +47,8 @@ impl<'a> UiState<'a> { _ => (), } + *rows_counter += 1; + let next = if ind == state_file.next_exercise_ind() { ">>>>".bold().red() } else { @@ -67,8 +71,13 @@ impl<'a> UiState<'a> { } pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { - let rows = Self::rows(state_file, self.exercises, self.filter); + let mut rows_counter = 0; + let rows = Self::rows(state_file, self.exercises, &mut rows_counter, self.filter); self.table = self.table.rows(rows); + + self.last_ind = rows_counter.saturating_sub(1); + self.select(self.selected.min(self.last_ind)); + self } @@ -89,7 +98,8 @@ impl<'a> UiState<'a> { ]; let filter = Filter::None; - let rows = Self::rows(state_file, exercises, filter); + let mut rows_counter = 0; + let rows = Self::rows(state_file, exercises, &mut rows_counter, filter); let table = Table::new(rows, widths) .header(header) @@ -111,7 +121,7 @@ impl<'a> UiState<'a> { exercises, selected, table_state, - last_ind: exercises.len() - 1, + last_ind: rows_counter.saturating_sub(1), } } From 7c46e7ac697507ff1826bf5bf691a93898d4368d Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 03:16:38 +0200 Subject: [PATCH 0650/1432] Simplify building rows. No more lifetimes championship :( --- src/list/state.rs | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 902e7a6a35..b3dbafed5f 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -26,28 +26,20 @@ pub struct UiState<'a> { } impl<'a> UiState<'a> { - fn rows<'s, 'c, 'i>( - state_file: &'s StateFile, - exercises: &'a [Exercise], - rows_counter: &'c mut usize, - filter: Filter, - ) -> impl Iterator> + 'i - where - 's: 'i, - 'a: 'i, - 'c: 'i, - { - exercises + pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { + let mut rows_counter: usize = 0; + let rows = self + .exercises .iter() .zip(state_file.progress().iter().copied()) .enumerate() - .filter_map(move |(ind, (exercise, done))| { - match (filter, done) { + .filter_map(|(ind, (exercise, done))| { + match (self.filter, done) { (Filter::Done, false) | (Filter::Pending, true) => return None, _ => (), } - *rows_counter += 1; + rows_counter += 1; let next = if ind == state_file.next_exercise_ind() { ">>>>".bold().red() @@ -67,12 +59,8 @@ impl<'a> UiState<'a> { Span::raw(&exercise.name), Span::raw(exercise.path.to_string_lossy()), ])) - }) - } + }); - pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { - let mut rows_counter = 0; - let rows = Self::rows(state_file, self.exercises, &mut rows_counter, self.filter); self.table = self.table.rows(rows); self.last_ind = rows_counter.saturating_sub(1); @@ -97,11 +85,8 @@ impl<'a> UiState<'a> { Constraint::Fill(1), ]; - let filter = Filter::None; - let mut rows_counter = 0; - let rows = Self::rows(state_file, exercises, &mut rows_counter, filter); - - let table = Table::new(rows, widths) + let table = Table::default() + .widths(widths) .header(header) .column_spacing(2) .highlight_spacing(HighlightSpacing::Always) @@ -114,15 +99,17 @@ impl<'a> UiState<'a> { .with_offset(selected.saturating_sub(10)) .with_selected(Some(selected)); - Self { + let slf = Self { table, message: String::with_capacity(128), - filter, + filter: Filter::None, exercises, selected, table_state, - last_ind: rows_counter.saturating_sub(1), - } + last_ind: 0, + }; + + slf.with_updated_rows(state_file) } #[inline] From d0fcd8ae8aac43e0c0ac933bd810f11fa79d962e Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Apr 2024 03:21:13 +0200 Subject: [PATCH 0651/1432] Use a color for the message --- src/list/state.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index b3dbafed5f..dc9ff5fe46 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -156,12 +156,14 @@ impl<'a> UiState<'a> { let message = if self.message.is_empty() { // Help footer. - "↓/j ↑/k home/g end/G β”‚ filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit" + Span::raw( + "↓/j ↑/k home/g end/G β”‚ filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit", + ) } else { - &self.message + self.message.as_str().blue() }; frame.render_widget( - Span::raw(message), + message, Rect { x: 0, y: area.height - 1, From f7145343937acfd9039ee7f4f562731a44bdf33a Mon Sep 17 00:00:00 2001 From: YunShu Date: Mon, 8 Apr 2024 22:07:26 +0800 Subject: [PATCH 0652/1432] docs: add more info in threads info.toml: ```toml [[exercises]] name = "threads3" path = "exercises/threads/threads3.rs" mode = "test" hint = """ An alternate way to handle concurrency between threads is to use a mpsc (multiple producer, single consumer) channel to communicate. With both a sending end and a receiving end, it's possible to send values in one thread and receive them in another. Multiple producers are possible by using clone() to create a duplicate of the original sending end. See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. """ ``` threads3'hint contains this link, so it should be placed in Further Information --- exercises/20_threads/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/20_threads/README.md b/exercises/20_threads/README.md index dbe66643c3..0b32fb1def 100644 --- a/exercises/20_threads/README.md +++ b/exercises/20_threads/README.md @@ -7,3 +7,4 @@ Within your program, you can also have independent parts that run simultaneously - [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html) - [Using Threads to Run Code Simultaneously](https://doc.rust-lang.org/book/ch16-01-threads.html) +- [Using Message Passing to Transfer Data Between Threads](https://doc.rust-lang.org/book/ch16-02-message-passing.html) From 501861e43589ab8a47b0c072098ecf36779e8db2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 20:49:02 +0000 Subject: [PATCH 0653/1432] docs: update AUTHORS.md [skip ci] --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index d82a5caba7..341ba42e8f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -385,6 +385,7 @@ authors. NicolasRoelandt
NicolasRoelandt

πŸ“– Josh Bouganim
Josh Bouganim

πŸ’» Dan
Dan

πŸ’» + YunShu
YunShu

πŸ–‹ From 95a3fe17fa61144101ab7a9cae3acd75e58948b0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 20:49:03 +0000 Subject: [PATCH 0654/1432] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 36952f7e7e..3f3f3ec945 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2730,6 +2730,15 @@ "contributions": [ "code" ] + }, + { + "login": "Selflocking", + "name": "YunShu", + "avatar_url": "https://avatars.githubusercontent.com/u/53544726?v=4", + "profile": "https://yunshu.site", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, From ee7d9762832241b34dc5533bad4ed151e21acab1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 17:15:12 +0200 Subject: [PATCH 0655/1432] Use a green color on successful run --- src/run.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/run.rs b/src/run.rs index 38f4e0e22c..2fd6f40719 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,4 +1,5 @@ use anyhow::{bail, Result}; +use crossterm::style::Stylize; use std::io::{stdout, Write}; use crate::exercise::Exercise; @@ -21,8 +22,7 @@ pub fn run(exercise: &Exercise) -> Result<()> { bail!("Ran {exercise} with errors"); } - // TODO: Color - println!("Successfully ran {exercise}"); + println!("{}", "βœ“ Successfully ran {exercise}".green()); Ok(()) } From 850c1d0234b2c1ae09a8f1c8f669e23a324fd644 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 19:37:39 +0200 Subject: [PATCH 0656/1432] Add progress bar to list --- src/list.rs | 2 +- src/list/state.rs | 56 +++++++++++++++++++++++++++++++++------------ src/main.rs | 1 + src/progress_bar.rs | 41 +++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 src/progress_bar.rs diff --git a/src/list.rs b/src/list.rs index d7fa05f17a..db83ea4f58 100644 --- a/src/list.rs +++ b/src/list.rs @@ -24,7 +24,7 @@ pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { let mut ui_state = UiState::new(state_file, exercises); 'outer: loop { - terminal.draw(|frame| ui_state.draw(frame))?; + terminal.draw(|frame| ui_state.draw(frame).unwrap())?; let key = loop { match event::read()? { diff --git a/src/list/state.rs b/src/list/state.rs index dc9ff5fe46..7bfc163ae1 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -1,12 +1,13 @@ +use anyhow::Result; use ratatui::{ layout::{Constraint, Rect}, style::{Style, Stylize}, text::Span, - widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState}, + widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, Frame, }; -use crate::{exercise::Exercise, state_file::StateFile}; +use crate::{exercise::Exercise, progress_bar::progress_bar, state_file::StateFile}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -20,6 +21,7 @@ pub struct UiState<'a> { pub message: String, pub filter: Filter, exercises: &'a [Exercise], + progress: u16, selected: usize, table_state: TableState, last_ind: usize, @@ -28,16 +30,28 @@ pub struct UiState<'a> { impl<'a> UiState<'a> { pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { let mut rows_counter: usize = 0; + let mut progress: u16 = 0; let rows = self .exercises .iter() .zip(state_file.progress().iter().copied()) .enumerate() .filter_map(|(ind, (exercise, done))| { - match (self.filter, done) { - (Filter::Done, false) | (Filter::Pending, true) => return None, - _ => (), - } + let exercise_state = if done { + progress += 1; + + if self.filter == Filter::Pending { + return None; + } + + "DONE".green() + } else { + if self.filter == Filter::Done { + return None; + } + + "PENDING".yellow() + }; rows_counter += 1; @@ -47,12 +61,6 @@ impl<'a> UiState<'a> { Span::default() }; - let exercise_state = if done { - "DONE".green() - } else { - "PENDING".yellow() - }; - Some(Row::new([ next, exercise_state, @@ -66,6 +74,8 @@ impl<'a> UiState<'a> { self.last_ind = rows_counter.saturating_sub(1); self.select(self.selected.min(self.last_ind)); + self.progress = progress; + self } @@ -104,6 +114,7 @@ impl<'a> UiState<'a> { message: String::with_capacity(128), filter: Filter::None, exercises, + progress: 0, selected, table_state, last_ind: 0, @@ -140,7 +151,7 @@ impl<'a> UiState<'a> { self.select(self.last_ind); } - pub fn draw(&mut self, frame: &mut Frame) { + pub fn draw(&mut self, frame: &mut Frame) -> Result<()> { let area = frame.size(); frame.render_stateful_widget( @@ -149,11 +160,26 @@ impl<'a> UiState<'a> { x: 0, y: 0, width: area.width, - height: area.height - 1, + height: area.height - 3, }, &mut self.table_state, ); + frame.render_widget( + Paragraph::new(Span::raw(progress_bar( + self.progress, + self.exercises.len() as u16, + area.width, + )?)) + .block(Block::default().borders(Borders::BOTTOM)), + Rect { + x: 0, + y: area.height - 3, + width: area.width, + height: 2, + }, + ); + let message = if self.message.is_empty() { // Help footer. Span::raw( @@ -171,5 +197,7 @@ impl<'a> UiState<'a> { height: 1, }, ); + + Ok(()) } } diff --git a/src/main.rs b/src/main.rs index f6c4c2002f..356b77ca04 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod embedded; mod exercise; mod init; mod list; +mod progress_bar; mod run; mod state_file; mod verify; diff --git a/src/progress_bar.rs b/src/progress_bar.rs new file mode 100644 index 0000000000..b4abbfc955 --- /dev/null +++ b/src/progress_bar.rs @@ -0,0 +1,41 @@ +use anyhow::{bail, Result}; +use std::fmt::Write; + +pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result { + if progress > total { + bail!("The progress of the progress bar is higher than the maximum"); + } + + // "Progress: [".len() == 11 + // "] xxx/xxx".len() == 9 + // 11 + 9 = 20 + let wrapper_width = 20; + + // If the line width is too low for a progress bar, just show the ratio. + if line_width < wrapper_width + 4 { + return Ok(format!("Progress: {progress}/{total}")); + } + + let mut line = String::with_capacity(usize::from(line_width)); + line.push_str("Progress: ["); + + let remaining_width = line_width.saturating_sub(wrapper_width); + let filled = (remaining_width * progress) / total; + + for _ in 0..filled { + line.push('='); + } + + if filled < remaining_width { + line.push('>'); + } + + for _ in 0..(remaining_width - filled).saturating_sub(1) { + line.push(' '); + } + + line.write_fmt(format_args!("] {progress:>3}/{total:<3}")) + .unwrap(); + + Ok(line) +} From f0ce2c1afa21fdaa34aed8f21c1ef4d3c47cebdd Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 21:07:53 +0200 Subject: [PATCH 0657/1432] Improve event handling in the watch mode --- src/main.rs | 5 +- src/watch.rs | 150 ++++++++++++++++++++++++++++++++------------- src/watch/state.rs | 73 ++++++++-------------- 3 files changed, 133 insertions(+), 95 deletions(-) diff --git a/src/main.rs b/src/main.rs index 356b77ca04..6af66bd71b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,7 +85,8 @@ Did you already install Rust? Try running `cargo --version` to diagnose the problem.", )?; - let exercises = InfoFile::parse()?.exercises; + // Leaking is not a problem since the exercises are used until the end of the program. + let exercises = InfoFile::parse()?.exercises.leak(); if matches!(args.command, Some(Subcommands::Init)) { init::init(&exercises).context("Initialization failed")?; @@ -110,7 +111,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini match args.command { None | Some(Subcommands::Watch) => { - watch::watch(&state_file, &exercises)?; + watch::watch(&state_file, exercises)?; } // `Init` is handled above. Some(Subcommands::Init) => (), diff --git a/src/watch.rs b/src/watch.rs index 967f98c186..abf40020e2 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,9 +1,11 @@ -use anyhow::Result; -use notify_debouncer_mini::{new_debouncer, notify::RecursiveMode}; +use anyhow::{bail, Context, Result}; +use notify_debouncer_mini::{ + new_debouncer, notify::RecursiveMode, DebounceEventResult, DebouncedEventKind, +}; use std::{ io::{self, BufRead, Write}, path::Path, - sync::mpsc::{channel, sync_channel}, + sync::mpsc::{channel, Sender}, thread, time::Duration, }; @@ -14,70 +16,130 @@ use crate::{exercise::Exercise, state_file::StateFile}; use self::state::WatchState; -enum Event { +enum InputEvent { Hint, Clear, Quit, + Unrecognized, } -pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { - let (tx, rx) = channel(); - let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; - debouncer - .watcher() - .watch(Path::new("exercises"), RecursiveMode::Recursive)?; +enum WatchEvent { + Input(InputEvent), + FileChange { exercise_ind: usize }, + TerminalResize, +} - let mut watch_state = WatchState::new(state_file, exercises, rx); +struct DebouceEventHandler { + tx: Sender, + exercises: &'static [Exercise], +} - watch_state.run_exercise()?; - watch_state.render()?; +impl notify_debouncer_mini::DebounceEventHandler for DebouceEventHandler { + fn handle_event(&mut self, event: DebounceEventResult) { + let Ok(event) = event else { + // TODO + return; + }; + + let Some(exercise_ind) = event + .iter() + .filter_map(|event| { + if event.kind != DebouncedEventKind::Any + || !event.path.extension().is_some_and(|ext| ext == "rs") + { + return None; + } - let (tx, rx) = sync_channel(0); - thread::spawn(move || { - let mut stdin = io::stdin().lock(); - let mut stdin_buf = String::with_capacity(8); + self.exercises + .iter() + .position(|exercise| event.path.ends_with(&exercise.path)) + }) + .min() + else { + return; + }; - loop { - stdin.read_line(&mut stdin_buf).unwrap(); + self.tx.send(WatchEvent::FileChange { exercise_ind }); + } +} - let event = match stdin_buf.trim() { - "h" | "hint" => Some(Event::Hint), - "c" | "clear" => Some(Event::Clear), - "q" | "quit" => Some(Event::Quit), - _ => None, - }; +fn input_handler(tx: Sender) -> Result<()> { + let mut stdin = io::stdin().lock(); + let mut stdin_buf = String::with_capacity(8); + + loop { + stdin + .read_line(&mut stdin_buf) + .context("Failed to read the user's input from stdin")?; - stdin_buf.clear(); + let event = match stdin_buf.trim() { + "h" | "hint" => InputEvent::Hint, + "c" | "clear" => InputEvent::Clear, + "q" | "quit" => InputEvent::Quit, + _ => InputEvent::Unrecognized, + }; - if tx.send(event).is_err() { - break; - }; + stdin_buf.clear(); + + if tx.send(WatchEvent::Input(event)).is_err() { + return Ok(()); } - }); + } +} - loop { - watch_state.try_recv_event()?; +pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<()> { + let (tx, rx) = channel(); + let mut debouncer = new_debouncer( + Duration::from_secs(1), + DebouceEventHandler { + tx: tx.clone(), + exercises, + }, + )?; + debouncer + .watcher() + .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - if let Ok(event) = rx.try_recv() { - match event { - Some(Event::Hint) => { - watch_state.show_hint()?; - } - Some(Event::Clear) => { - watch_state.render()?; - } - Some(Event::Quit) => break, - None => { - watch_state.handle_invalid_cmd()?; - } + let mut watch_state = WatchState::new(state_file, exercises); + + // TODO: bool + watch_state.run_exercise()?; + watch_state.render()?; + + let input_thread = thread::spawn(move || input_handler(tx)); + + while let Ok(event) = rx.recv() { + match event { + WatchEvent::Input(InputEvent::Hint) => { + watch_state.show_hint()?; + } + WatchEvent::Input(InputEvent::Clear) | WatchEvent::TerminalResize => { + watch_state.render()?; + } + WatchEvent::Input(InputEvent::Quit) => break, + WatchEvent::Input(InputEvent::Unrecognized) => { + watch_state.handle_invalid_cmd()?; + } + WatchEvent::FileChange { exercise_ind } => { + // TODO: bool + watch_state.run_exercise_with_ind(exercise_ind)?; + watch_state.render()?; } } } + // Drop the receiver for the sender threads to exit. + drop(rx); + watch_state.into_writer().write_all(b" We hope you're enjoying learning Rust! If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. ")?; + match input_thread.join() { + Ok(res) => res?, + Err(_) => bail!("The input thread panicked"), + } + Ok(()) } diff --git a/src/watch/state.rs b/src/watch/state.rs index 40f48effea..f614ae0dde 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,26 +1,23 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use crossterm::{ style::{Attribute, ContentStyle, Stylize}, - terminal::{Clear, ClearType}, + terminal::{size, Clear, ClearType}, ExecutableCommand, }; -use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; use std::{ fmt::Write as _, io::{self, StdoutLock, Write as _}, - sync::mpsc::Receiver, - time::Duration, }; use crate::{ exercise::{Exercise, State}, + progress_bar::progress_bar, state_file::StateFile, }; pub struct WatchState<'a> { writer: StdoutLock<'a>, - rx: Receiver, - exercises: &'a [Exercise], + exercises: &'static [Exercise], exercise: &'a Exercise, current_exercise_ind: usize, stdout: Option>, @@ -30,11 +27,7 @@ pub struct WatchState<'a> { } impl<'a> WatchState<'a> { - pub fn new( - state_file: &StateFile, - exercises: &'a [Exercise], - rx: Receiver, - ) -> Self { + pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self { let current_exercise_ind = state_file.next_exercise_ind(); let exercise = &exercises[current_exercise_ind]; @@ -50,7 +43,6 @@ impl<'a> WatchState<'a> { Self { writer, - rx, exercises, exercise, current_exercise_ind, @@ -114,41 +106,14 @@ You can keep working on this exercise or jump into the next one by removing the Ok(true) } - pub fn try_recv_event(&mut self) -> Result<()> { - let Ok(events) = self.rx.recv_timeout(Duration::from_millis(100)) else { - return Ok(()); - }; - - if let Some(current_exercise_ind) = events? - .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { - return None; - } - - self.exercises - .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) - }) - .min() - { - self.current_exercise_ind = current_exercise_ind; - } else { - return Ok(()); - }; - - while self.current_exercise_ind < self.exercises.len() { - self.exercise = &self.exercises[self.current_exercise_ind]; - if !self.run_exercise()? { - break; - } - - self.current_exercise_ind += 1; - } + pub fn run_exercise_with_ind(&mut self, exercise_ind: usize) -> Result { + self.exercise = self + .exercises + .get(exercise_ind) + .context("Invalid exercise index")?; + self.current_exercise_ind = exercise_ind; - Ok(()) + self.run_exercise() } pub fn show_prompt(&mut self) -> io::Result<()> { @@ -156,7 +121,7 @@ You can keep working on this exercise or jump into the next one by removing the self.writer.flush() } - pub fn render(&mut self) -> io::Result<()> { + pub fn render(&mut self) -> Result<()> { self.writer.execute(Clear(ClearType::All))?; if let Some(stdout) = &self.stdout { @@ -171,7 +136,17 @@ You can keep working on this exercise or jump into the next one by removing the self.writer.write_all(message.as_bytes())?; } - self.show_prompt() + let line_width = size()?.0; + let progress_bar = progress_bar( + self.current_exercise_ind as u16, + self.exercises.len() as u16, + line_width, + )?; + self.writer.write_all(progress_bar.as_bytes())?; + + self.show_prompt()?; + + Ok(()) } pub fn show_hint(&mut self) -> io::Result<()> { From 787bec9875ec3e76d5870808cc7299da1d26dea6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 21:16:27 +0200 Subject: [PATCH 0658/1432] Use exercises as leaked --- src/list.rs | 2 +- src/list/state.rs | 10 +++++----- src/main.rs | 16 ++++++++-------- src/verify.rs | 9 ++++++--- src/watch/state.rs | 2 +- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/list.rs b/src/list.rs index db83ea4f58..c92b369273 100644 --- a/src/list.rs +++ b/src/list.rs @@ -13,7 +13,7 @@ use crate::{exercise::Exercise, state_file::StateFile}; use self::state::{Filter, UiState}; -pub fn list(state_file: &mut StateFile, exercises: &[Exercise]) -> Result<()> { +pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result<()> { let mut stdout = io::stdout().lock(); stdout.execute(EnterAlternateScreen)?; enable_raw_mode()?; diff --git a/src/list/state.rs b/src/list/state.rs index 7bfc163ae1..b67c624b7f 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -16,18 +16,18 @@ pub enum Filter { None, } -pub struct UiState<'a> { - pub table: Table<'a>, +pub struct UiState { + pub table: Table<'static>, pub message: String, pub filter: Filter, - exercises: &'a [Exercise], + exercises: &'static [Exercise], progress: u16, selected: usize, table_state: TableState, last_ind: usize, } -impl<'a> UiState<'a> { +impl UiState { pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { let mut rows_counter: usize = 0; let mut progress: u16 = 0; @@ -79,7 +79,7 @@ impl<'a> UiState<'a> { self } - pub fn new(state_file: &StateFile, exercises: &'a [Exercise]) -> Self { + pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self { let header = Row::new(["Next", "State", "Name", "Path"]); let max_name_len = exercises diff --git a/src/main.rs b/src/main.rs index 6af66bd71b..62bfd98be6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,7 @@ enum Subcommands { List, } -fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<(usize, &'a Exercise)> { +fn find_exercise(name: &str, exercises: &'static [Exercise]) -> Result<(usize, &'static Exercise)> { if name == "next" { for (ind, exercise) in exercises.iter().enumerate() { if !exercise.looks_done()? { @@ -89,7 +89,7 @@ Try running `cargo --version` to diagnose the problem.", let exercises = InfoFile::parse()?.exercises.leak(); if matches!(args.command, Some(Subcommands::Init)) { - init::init(&exercises).context("Initialization failed")?; + init::init(exercises).context("Initialization failed")?; println!( "\nDone initialization!\n Run `cd rustlings` to go into the generated directory. @@ -107,7 +107,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let mut state_file = StateFile::read_or_default(&exercises); + let mut state_file = StateFile::read_or_default(exercises); match args.command { None | Some(Subcommands::Watch) => { @@ -116,23 +116,23 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::List) => { - list::list(&mut state_file, &exercises)?; + list::list(&mut state_file, exercises)?; } Some(Subcommands::Run { name }) => { - let (_, exercise) = find_exercise(&name, &exercises)?; + let (_, exercise) = find_exercise(&name, exercises)?; run(exercise).unwrap_or_else(|_| exit(1)); } Some(Subcommands::Reset { name }) => { - let (ind, exercise) = find_exercise(&name, &exercises)?; + let (ind, exercise) = find_exercise(&name, exercises)?; exercise.reset()?; state_file.reset(ind)?; println!("The exercise {exercise} has been reset!"); } Some(Subcommands::Hint { name }) => { - let (_, exercise) = find_exercise(&name, &exercises)?; + let (_, exercise) = find_exercise(&name, exercises)?; println!("{}", exercise.hint); } - Some(Subcommands::Verify) => match verify(&exercises, 0)? { + Some(Subcommands::Verify) => match verify(exercises, 0)? { VerifyState::AllExercisesDone => println!("All exercises done!"), VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), }, diff --git a/src/verify.rs b/src/verify.rs index c4368cc75b..cea6bdf6f9 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -4,9 +4,9 @@ use std::io::{stdout, Write}; use crate::exercise::{Exercise, Mode, State}; -pub enum VerifyState<'a> { +pub enum VerifyState { AllExercisesDone, - Failed(&'a Exercise), + Failed(&'static Exercise), } // Verify that the provided container of Exercise objects @@ -14,7 +14,10 @@ pub enum VerifyState<'a> { // Any such failures will be reported to the end user. // If the Exercise being verified is a test, the verbose boolean // determines whether or not the test harness outputs are displayed. -pub fn verify(exercises: &[Exercise], mut current_exercise_ind: usize) -> Result> { +pub fn verify( + exercises: &'static [Exercise], + mut current_exercise_ind: usize, +) -> Result { while current_exercise_ind < exercises.len() { let exercise = &exercises[current_exercise_ind]; diff --git a/src/watch/state.rs b/src/watch/state.rs index f614ae0dde..d8fed5b75d 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -18,7 +18,7 @@ use crate::{ pub struct WatchState<'a> { writer: StdoutLock<'a>, exercises: &'static [Exercise], - exercise: &'a Exercise, + exercise: &'static Exercise, current_exercise_ind: usize, stdout: Option>, stderr: Option>, From b15e0a279b17d29a3fa6408b76da35f0b843ce21 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 21:23:02 +0200 Subject: [PATCH 0659/1432] Use shrink to fit before leaking the vector --- src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 62bfd98be6..504c02dc3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,8 +85,10 @@ Did you already install Rust? Try running `cargo --version` to diagnose the problem.", )?; - // Leaking is not a problem since the exercises are used until the end of the program. - let exercises = InfoFile::parse()?.exercises.leak(); + let mut info_file = InfoFile::parse()?; + info_file.exercises.shrink_to_fit(); + // Leaking is not a problem since the exercises' slice is used until the end of the program. + let exercises = info_file.exercises.leak(); if matches!(args.command, Some(Subcommands::Init)) { init::init(exercises).context("Initialization failed")?; From 4110ae21afd2c026e49d330918e212f4ab0eb5cc Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 21:46:55 +0200 Subject: [PATCH 0660/1432] Handle notify errors --- src/watch.rs | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index abf40020e2..5a1e38aba6 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,6 +1,8 @@ use anyhow::{bail, Context, Result}; use notify_debouncer_mini::{ - new_debouncer, notify::RecursiveMode, DebounceEventResult, DebouncedEventKind, + new_debouncer, + notify::{self, RecursiveMode}, + DebounceEventResult, DebouncedEventKind, }; use std::{ io::{self, BufRead, Write}, @@ -26,6 +28,7 @@ enum InputEvent { enum WatchEvent { Input(InputEvent), FileChange { exercise_ind: usize }, + NotifyErr(notify::Error), TerminalResize, } @@ -36,30 +39,32 @@ struct DebouceEventHandler { impl notify_debouncer_mini::DebounceEventHandler for DebouceEventHandler { fn handle_event(&mut self, event: DebounceEventResult) { - let Ok(event) = event else { - // TODO - return; - }; - - let Some(exercise_ind) = event - .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { - return None; - } - - self.exercises + let event = match event { + Ok(event) => { + let Some(exercise_ind) = event .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) - }) - .min() - else { - return; + .filter_map(|event| { + if event.kind != DebouncedEventKind::Any + || !event.path.extension().is_some_and(|ext| ext == "rs") + { + return None; + } + + self.exercises + .iter() + .position(|exercise| event.path.ends_with(&exercise.path)) + }) + .min() + else { + return; + }; + + WatchEvent::FileChange { exercise_ind } + } + Err(e) => WatchEvent::NotifyErr(e), }; - self.tx.send(WatchEvent::FileChange { exercise_ind }); + let _ = self.tx.send(event); } } @@ -125,6 +130,7 @@ pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<( watch_state.run_exercise_with_ind(exercise_ind)?; watch_state.render()?; } + WatchEvent::NotifyErr(e) => return Err(e.into()), } } From ff6c15f9c15ae80b48d3acd7091eb6328c931e7a Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 22:04:10 +0200 Subject: [PATCH 0661/1432] Don't try to join the input thread --- src/watch.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 5a1e38aba6..6324eb364b 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::Result; use notify_debouncer_mini::{ new_debouncer, notify::{self, RecursiveMode}, @@ -29,6 +29,7 @@ enum WatchEvent { Input(InputEvent), FileChange { exercise_ind: usize }, NotifyErr(notify::Error), + StdinErr(io::Error), TerminalResize, } @@ -64,18 +65,23 @@ impl notify_debouncer_mini::DebounceEventHandler for DebouceEventHandler { Err(e) => WatchEvent::NotifyErr(e), }; + // An error occurs when the receiver is dropped. + // After dropping the receiver, the debouncer guard should also be dropped. let _ = self.tx.send(event); } } -fn input_handler(tx: Sender) -> Result<()> { +fn input_handler(tx: Sender) { let mut stdin = io::stdin().lock(); let mut stdin_buf = String::with_capacity(8); loop { - stdin - .read_line(&mut stdin_buf) - .context("Failed to read the user's input from stdin")?; + if let Err(e) = stdin.read_line(&mut stdin_buf) { + // If `send` returns an error, then the receiver is dropped and + // a shutdown has been already initialized. + let _ = tx.send(WatchEvent::StdinErr(e)); + return; + } let event = match stdin_buf.trim() { "h" | "hint" => InputEvent::Hint, @@ -87,7 +93,8 @@ fn input_handler(tx: Sender) -> Result<()> { stdin_buf.clear(); if tx.send(WatchEvent::Input(event)).is_err() { - return Ok(()); + // The receiver was dropped. + return; } } } @@ -111,7 +118,7 @@ pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<( watch_state.run_exercise()?; watch_state.render()?; - let input_thread = thread::spawn(move || input_handler(tx)); + thread::spawn(move || input_handler(tx)); while let Ok(event) = rx.recv() { match event { @@ -131,21 +138,14 @@ pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<( watch_state.render()?; } WatchEvent::NotifyErr(e) => return Err(e.into()), + WatchEvent::StdinErr(e) => return Err(e.into()), } } - // Drop the receiver for the sender threads to exit. - drop(rx); - watch_state.into_writer().write_all(b" We hope you're enjoying learning Rust! If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. ")?; - match input_thread.join() { - Ok(res) => res?, - Err(_) => bail!("The input thread panicked"), - } - Ok(()) } From af85f2036cd545013225da04e67257fe4f6a4179 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 22:06:55 +0200 Subject: [PATCH 0662/1432] Print a newline before the progress bar --- src/watch/state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/watch/state.rs b/src/watch/state.rs index d8fed5b75d..8fae7e8765 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -136,6 +136,7 @@ You can keep working on this exercise or jump into the next one by removing the self.writer.write_all(message.as_bytes())?; } + self.writer.write_all(b"\n")?; let line_width = size()?.0; let progress_bar = progress_bar( self.current_exercise_ind as u16, From a8ddc07a9aea5b2e3840a7b6e0eb20f2189bdd60 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 22:15:41 +0200 Subject: [PATCH 0663/1432] Add "exercises" to the end of the progress bar --- src/progress_bar.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/progress_bar.rs b/src/progress_bar.rs index b4abbfc955..ee55ba7b51 100644 --- a/src/progress_bar.rs +++ b/src/progress_bar.rs @@ -7,13 +7,13 @@ pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result 99) + // 11 + 19 = 30 + let wrapper_width = 30; // If the line width is too low for a progress bar, just show the ratio. if line_width < wrapper_width + 4 { - return Ok(format!("Progress: {progress}/{total}")); + return Ok(format!("Progress: {progress}/{total} exercises")); } let mut line = String::with_capacity(usize::from(line_width)); @@ -34,7 +34,7 @@ pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result3}/{total:<3}")) + line.write_fmt(format_args!("] {progress:>3}/{total} exercises")) .unwrap(); Ok(line) From c8d217ad50a7117fe35735b4083f2aa1e2b47d97 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 9 Apr 2024 22:20:12 +0200 Subject: [PATCH 0664/1432] Fix showing stdout and stderr --- src/watch/state.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index 8fae7e8765..24978bb774 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -60,13 +60,15 @@ impl<'a> WatchState<'a> { pub fn run_exercise(&mut self) -> Result { let output = self.exercise.run()?; + self.stdout = Some(output.stdout); if !output.status.success() { - self.stdout = Some(output.stdout); self.stderr = Some(output.stderr); return Ok(false); } + self.stderr = None; + if let State::Pending(context) = self.exercise.state()? { let mut message = format!( " @@ -98,7 +100,6 @@ You can keep working on this exercise or jump into the next one by removing the )?; } - self.stdout = Some(output.stdout); self.message = Some(message); return Ok(false); } From 4a80bf64411f228c35c173b6188df5114d4c52fa Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 00:42:32 +0200 Subject: [PATCH 0665/1432] Colorize the progress bar --- src/list/state.rs | 6 +-- src/progress_bar.rs | 92 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index b67c624b7f..8918979485 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -7,7 +7,7 @@ use ratatui::{ Frame, }; -use crate::{exercise::Exercise, progress_bar::progress_bar, state_file::StateFile}; +use crate::{exercise::Exercise, progress_bar::progress_bar_ratatui, state_file::StateFile}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -166,11 +166,11 @@ impl UiState { ); frame.render_widget( - Paragraph::new(Span::raw(progress_bar( + Paragraph::new(progress_bar_ratatui( self.progress, self.exercises.len() as u16, area.width, - )?)) + )?) .block(Block::default().borders(Borders::BOTTOM)), Rect { x: 0, diff --git a/src/progress_bar.rs b/src/progress_bar.rs index ee55ba7b51..97c8ad9074 100644 --- a/src/progress_bar.rs +++ b/src/progress_bar.rs @@ -1,41 +1,97 @@ use anyhow::{bail, Result}; +use ratatui::text::{Line, Span}; use std::fmt::Write; +const PREFIX: &str = "Progress: ["; +const PREFIX_WIDTH: u16 = PREFIX.len() as u16; +// Leaving the last char empty (_) for `total` > 99. +const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16; +const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH; +const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; + +const PROGRESS_EXCEEDS_MAX_ERR: &str = + "The progress of the progress bar is higher than the maximum"; + pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result { + use crossterm::style::Stylize; + if progress > total { - bail!("The progress of the progress bar is higher than the maximum"); + bail!(PROGRESS_EXCEEDS_MAX_ERR); } - // "Progress: [".len() == 11 - // "] xxx/xx exercises_".len() == 19 (leaving the last char empty for `total` > 99) - // 11 + 19 = 30 - let wrapper_width = 30; - - // If the line width is too low for a progress bar, just show the ratio. - if line_width < wrapper_width + 4 { + if line_width < MIN_LINE_WIDTH { return Ok(format!("Progress: {progress}/{total} exercises")); } let mut line = String::with_capacity(usize::from(line_width)); - line.push_str("Progress: ["); + line.push_str(PREFIX); - let remaining_width = line_width.saturating_sub(wrapper_width); - let filled = (remaining_width * progress) / total; + let width = line_width - WRAPPER_WIDTH; + let filled = (width * progress) / total; + let mut green_part = String::with_capacity(usize::from(filled + 1)); for _ in 0..filled { - line.push('='); + green_part.push('#'); } - if filled < remaining_width { - line.push('>'); + if filled < width { + green_part.push('>'); } + write!(line, "{}", green_part.green()).unwrap(); - for _ in 0..(remaining_width - filled).saturating_sub(1) { - line.push(' '); + let width_minus_filled = width - filled; + if width_minus_filled > 1 { + let red_part_width = width_minus_filled - 1; + let mut red_part = String::with_capacity(usize::from(red_part_width)); + for _ in 0..red_part_width { + red_part.push('-'); + } + write!(line, "{}", red_part.red()).unwrap(); } - line.write_fmt(format_args!("] {progress:>3}/{total} exercises")) - .unwrap(); + write!(line, "] {progress:>3}/{total} exercises").unwrap(); Ok(line) } + +pub fn progress_bar_ratatui(progress: u16, total: u16, line_width: u16) -> Result> { + use ratatui::style::Stylize; + + if progress > total { + bail!(PROGRESS_EXCEEDS_MAX_ERR); + } + + if line_width < MIN_LINE_WIDTH { + return Ok(Line::raw(format!("Progress: {progress}/{total} exercises"))); + } + + let mut spans = Vec::with_capacity(4); + spans.push(Span::raw(PREFIX)); + + let width = line_width - WRAPPER_WIDTH; + let filled = (width * progress) / total; + + let mut green_part = String::with_capacity(usize::from(filled + 1)); + for _ in 0..filled { + green_part.push('#'); + } + + if filled < width { + green_part.push('>'); + } + spans.push(green_part.green()); + + let width_minus_filled = width - filled; + if width_minus_filled > 1 { + let red_part_width = width_minus_filled - 1; + let mut red_part = String::with_capacity(usize::from(red_part_width)); + for _ in 0..red_part_width { + red_part.push('-'); + } + spans.push(red_part.red()); + } + + spans.push(Span::raw(format!("] {progress:>3}/{total} exercises"))); + + Ok(Line::from(spans)) +} From 533a009257adba0714292d326f57671f77cffbd3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 00:51:41 +0200 Subject: [PATCH 0666/1432] Show the progress in the progress bar, not the current exercise index --- src/watch/state.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index 24978bb774..4db9440b90 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -20,6 +20,7 @@ pub struct WatchState<'a> { exercises: &'static [Exercise], exercise: &'static Exercise, current_exercise_ind: usize, + progress: u16, stdout: Option>, stderr: Option>, message: Option, @@ -29,6 +30,7 @@ pub struct WatchState<'a> { impl<'a> WatchState<'a> { pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self { let current_exercise_ind = state_file.next_exercise_ind(); + let progress = state_file.progress().iter().filter(|done| **done).count() as u16; let exercise = &exercises[current_exercise_ind]; let writer = io::stdout().lock(); @@ -46,6 +48,7 @@ impl<'a> WatchState<'a> { exercises, exercise, current_exercise_ind, + progress, stdout: None, stderr: None, message: None, @@ -139,11 +142,7 @@ You can keep working on this exercise or jump into the next one by removing the self.writer.write_all(b"\n")?; let line_width = size()?.0; - let progress_bar = progress_bar( - self.current_exercise_ind as u16, - self.exercises.len() as u16, - line_width, - )?; + let progress_bar = progress_bar(self.progress, self.exercises.len() as u16, line_width)?; self.writer.write_all(progress_bar.as_bytes())?; self.show_prompt()?; From d1a965f019d0e8f22d5a57f0a7abd8cd4a8d0d0c Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 02:12:50 +0200 Subject: [PATCH 0667/1432] Make the list mode part of the watch mode --- src/main.rs | 19 +++++++++++-------- src/watch.rs | 27 +++++++++++++++++++++++---- src/watch/state.rs | 5 +++-- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 504c02dc3e..fc83e0fd03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,9 +16,11 @@ mod watch; use self::{ consts::WELCOME, exercise::{Exercise, InfoFile}, + list::list, run::run, state_file::StateFile, verify::{verify, VerifyState}, + watch::{watch, WatchExit}, }; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code @@ -52,8 +54,6 @@ enum Subcommands { /// The name of the exercise name: String, }, - /// List the exercises available in Rustlings - List, } fn find_exercise(name: &str, exercises: &'static [Exercise]) -> Result<(usize, &'static Exercise)> { @@ -112,14 +112,17 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini let mut state_file = StateFile::read_or_default(exercises); match args.command { - None | Some(Subcommands::Watch) => { - watch::watch(&state_file, exercises)?; - } + None | Some(Subcommands::Watch) => loop { + match watch(&mut state_file, exercises)? { + 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 state_file, exercises)?, + } + }, // `Init` is handled above. Some(Subcommands::Init) => (), - Some(Subcommands::List) => { - list::list(&mut state_file, exercises)?; - } Some(Subcommands::Run { name }) => { let (_, exercise) = find_exercise(&name, exercises)?; run(exercise).unwrap_or_else(|_| exit(1)); diff --git a/src/watch.rs b/src/watch.rs index 6324eb364b..004a13f68f 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -18,9 +18,19 @@ use crate::{exercise::Exercise, state_file::StateFile}; use self::state::WatchState; +/// Returned by the watch mode to indicate what to do afterwards. +pub enum WatchExit { + /// Exit the program. + Shutdown, + /// Enter the list mode and restart the watch mode afterwards. + List, +} + +#[derive(Copy, Clone)] enum InputEvent { Hint, Clear, + List, Quit, Unrecognized, } @@ -86,20 +96,26 @@ fn input_handler(tx: Sender) { let event = match stdin_buf.trim() { "h" | "hint" => InputEvent::Hint, "c" | "clear" => InputEvent::Clear, + "l" | "list" => InputEvent::List, "q" | "quit" => InputEvent::Quit, _ => InputEvent::Unrecognized, }; - stdin_buf.clear(); - if tx.send(WatchEvent::Input(event)).is_err() { // The receiver was dropped. return; } + + match event { + InputEvent::List | InputEvent::Quit => return, + _ => (), + } + + stdin_buf.clear(); } } -pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<()> { +pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), @@ -125,6 +141,9 @@ pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<( WatchEvent::Input(InputEvent::Hint) => { watch_state.show_hint()?; } + WatchEvent::Input(InputEvent::List) => { + return Ok(WatchExit::List); + } WatchEvent::Input(InputEvent::Clear) | WatchEvent::TerminalResize => { watch_state.render()?; } @@ -147,5 +166,5 @@ We hope you're enjoying learning Rust! If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. ")?; - Ok(()) + Ok(WatchExit::Shutdown) } diff --git a/src/watch/state.rs b/src/watch/state.rs index 4db9440b90..393ea02c3e 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -36,10 +36,11 @@ impl<'a> WatchState<'a> { let writer = io::stdout().lock(); let prompt = format!( - "\n\n{}int/{}lear/{}uit? ", + "\n\n{}int/{}lear/{}ist/{}uit? ", "h".bold(), "c".bold(), - "q".bold() + "l".bold(), + "q".bold(), ) .into_bytes(); From c9a5fa6097997e95bc415cd76ef931a1a4bb1510 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 02:19:14 +0200 Subject: [PATCH 0668/1432] Accept repeat keyboard events --- src/list.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/list.rs b/src/list.rs index c92b369273..560b85a874 100644 --- a/src/list.rs +++ b/src/list.rs @@ -28,13 +28,10 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul let key = loop { match event::read()? { - Event::Key(key) => { - if key.kind != KeyEventKind::Press { - continue; - } - - break key; - } + Event::Key(key) => match key.kind { + KeyEventKind::Press | KeyEventKind::Repeat => break key, + KeyEventKind::Release => (), + }, // Redraw Event::Resize(_, _) => continue 'outer, // Ignore From f034899c7f8de93ff572722b1cdf44f73c6452b5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 03:54:48 +0200 Subject: [PATCH 0669/1432] Capture terminal resize events --- src/watch.rs | 91 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 004a13f68f..7b4a02dddd 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,11 +1,12 @@ -use anyhow::Result; +use anyhow::{Error, Result}; +use crossterm::event::{self, Event, KeyCode, KeyEventKind}; use notify_debouncer_mini::{ new_debouncer, notify::{self, RecursiveMode}, DebounceEventResult, DebouncedEventKind, }; use std::{ - io::{self, BufRead, Write}, + io::{self, Write}, path::Path, sync::mpsc::{channel, Sender}, thread, @@ -39,7 +40,7 @@ enum WatchEvent { Input(InputEvent), FileChange { exercise_ind: usize }, NotifyErr(notify::Error), - StdinErr(io::Error), + TerminalEventErr(io::Error), TerminalResize, } @@ -81,37 +82,61 @@ impl notify_debouncer_mini::DebounceEventHandler for DebouceEventHandler { } } -fn input_handler(tx: Sender) { - let mut stdin = io::stdin().lock(); - let mut stdin_buf = String::with_capacity(8); +fn terminal_event_handler(tx: Sender) { + let mut input = String::with_capacity(8); loop { - if let Err(e) = stdin.read_line(&mut stdin_buf) { - // If `send` returns an error, then the receiver is dropped and - // a shutdown has been already initialized. - let _ = tx.send(WatchEvent::StdinErr(e)); - return; - } - - let event = match stdin_buf.trim() { - "h" | "hint" => InputEvent::Hint, - "c" | "clear" => InputEvent::Clear, - "l" | "list" => InputEvent::List, - "q" | "quit" => InputEvent::Quit, - _ => InputEvent::Unrecognized, + let terminal_event = match event::read() { + Ok(v) => v, + Err(e) => { + // If `send` returns an error, then the receiver is dropped and + // a shutdown has been already initialized. + let _ = tx.send(WatchEvent::TerminalEventErr(e)); + return; + } }; - if tx.send(WatchEvent::Input(event)).is_err() { - // The receiver was dropped. - return; - } + match terminal_event { + Event::Key(key) => { + match key.kind { + KeyEventKind::Release => continue, + KeyEventKind::Press | KeyEventKind::Repeat => (), + } + + match key.code { + KeyCode::Enter => { + let input_event = match input.trim() { + "h" | "hint" => InputEvent::Hint, + "c" | "clear" => InputEvent::Clear, + "l" | "list" => InputEvent::List, + "q" | "quit" => InputEvent::Quit, + _ => InputEvent::Unrecognized, + }; + + if tx.send(WatchEvent::Input(input_event)).is_err() { + return; + } - match event { - InputEvent::List | InputEvent::Quit => return, - _ => (), - } + match input_event { + InputEvent::List | InputEvent::Quit => return, + _ => (), + } - stdin_buf.clear(); + input.clear(); + } + KeyCode::Char(c) => { + input.push(c); + } + _ => (), + } + } + Event::Resize(_, _) => { + if tx.send(WatchEvent::TerminalResize).is_err() { + return; + } + } + Event::FocusGained | Event::FocusLost | Event::Mouse(_) | Event::Paste(_) => continue, + } } } @@ -134,7 +159,7 @@ pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resu watch_state.run_exercise()?; watch_state.render()?; - thread::spawn(move || input_handler(tx)); + thread::spawn(move || terminal_event_handler(tx)); while let Ok(event) = rx.recv() { match event { @@ -156,8 +181,12 @@ pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resu watch_state.run_exercise_with_ind(exercise_ind)?; watch_state.render()?; } - WatchEvent::NotifyErr(e) => return Err(e.into()), - WatchEvent::StdinErr(e) => return Err(e.into()), + WatchEvent::NotifyErr(e) => { + return Err(Error::from(e).context("Exercise file watcher failed")) + } + WatchEvent::TerminalEventErr(e) => { + return Err(Error::from(e).context("Terminal event listener failed")) + } } } From a46d66134b26095e553f284c02de9a895e15f180 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 03:56:41 +0200 Subject: [PATCH 0670/1432] Fix shift of first output line --- src/watch/state.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/watch/state.rs b/src/watch/state.rs index 393ea02c3e..08707a45d7 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -127,6 +127,9 @@ You can keep working on this exercise or jump into the next one by removing the } pub fn render(&mut self) -> Result<()> { + // Prevent having the first line shifted after clearing because of the prompt. + self.writer.write_all(b"\n")?; + self.writer.execute(Clear(ClearType::All))?; if let Some(stdout) = &self.stdout { From 6255efe8b2de9d8d7f69871584444ab34fae122d Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 04:08:40 +0200 Subject: [PATCH 0671/1432] Show the invalid command to avoid confusion after resizing the terminal --- src/watch.rs | 24 ++++++++++-------------- src/watch/state.rs | 9 +++++++-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 7b4a02dddd..8b2110352e 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -27,13 +27,12 @@ pub enum WatchExit { List, } -#[derive(Copy, Clone)] enum InputEvent { Hint, Clear, List, Quit, - Unrecognized, + Unrecognized(String), } enum WatchEvent { @@ -85,7 +84,7 @@ impl notify_debouncer_mini::DebounceEventHandler for DebouceEventHandler { fn terminal_event_handler(tx: Sender) { let mut input = String::with_capacity(8); - loop { + let last_input_event = loop { let terminal_event = match event::read() { Ok(v) => v, Err(e) => { @@ -108,20 +107,15 @@ fn terminal_event_handler(tx: Sender) { let input_event = match input.trim() { "h" | "hint" => InputEvent::Hint, "c" | "clear" => InputEvent::Clear, - "l" | "list" => InputEvent::List, - "q" | "quit" => InputEvent::Quit, - _ => InputEvent::Unrecognized, + "l" | "list" => break InputEvent::List, + "q" | "quit" => break InputEvent::Quit, + _ => InputEvent::Unrecognized(input.clone()), }; if tx.send(WatchEvent::Input(input_event)).is_err() { return; } - match input_event { - InputEvent::List | InputEvent::Quit => return, - _ => (), - } - input.clear(); } KeyCode::Char(c) => { @@ -137,7 +131,9 @@ fn terminal_event_handler(tx: Sender) { } Event::FocusGained | Event::FocusLost | Event::Mouse(_) | Event::Paste(_) => continue, } - } + }; + + let _ = tx.send(WatchEvent::Input(last_input_event)); } pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result { @@ -173,8 +169,8 @@ pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resu watch_state.render()?; } WatchEvent::Input(InputEvent::Quit) => break, - WatchEvent::Input(InputEvent::Unrecognized) => { - watch_state.handle_invalid_cmd()?; + WatchEvent::Input(InputEvent::Unrecognized(cmd)) => { + watch_state.handle_invalid_cmd(&cmd)?; } WatchEvent::FileChange { exercise_ind } => { // TODO: bool diff --git a/src/watch/state.rs b/src/watch/state.rs index 08707a45d7..751285fc58 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -159,8 +159,13 @@ You can keep working on this exercise or jump into the next one by removing the self.show_prompt() } - pub fn handle_invalid_cmd(&mut self) -> io::Result<()> { - self.writer.write_all(b"Invalid command")?; + pub fn handle_invalid_cmd(&mut self, cmd: &str) -> io::Result<()> { + self.writer.write_all(b"Invalid command: ")?; + self.writer.write_all(cmd.as_bytes())?; + if cmd.len() > 1 { + self.writer + .write_all(b" (confusing input can occur after resizing the terminal)")?; + } self.show_prompt() } } From 62e92476e6dad1fc191fd666eae2fccb263f5ff0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 04:10:05 +0200 Subject: [PATCH 0672/1432] Fix typo --- src/watch.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 8b2110352e..cf63627d52 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -43,12 +43,12 @@ enum WatchEvent { TerminalResize, } -struct DebouceEventHandler { +struct DebounceEventHandler { tx: Sender, exercises: &'static [Exercise], } -impl notify_debouncer_mini::DebounceEventHandler for DebouceEventHandler { +impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { fn handle_event(&mut self, event: DebounceEventResult) { let event = match event { Ok(event) => { @@ -140,7 +140,7 @@ pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resu let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), - DebouceEventHandler { + DebounceEventHandler { tx: tx.clone(), exercises, }, From a59acf88354c8dfba301e59173653bc9a5f4bfb2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 14:29:31 +0200 Subject: [PATCH 0673/1432] Show the current exercise path --- src/progress_bar.rs | 2 +- src/watch/state.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/progress_bar.rs b/src/progress_bar.rs index 97c8ad9074..d6962b8c03 100644 --- a/src/progress_bar.rs +++ b/src/progress_bar.rs @@ -49,7 +49,7 @@ pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result3}/{total} exercises").unwrap(); + writeln!(line, "] {progress:>3}/{total} exercises").unwrap(); Ok(line) } diff --git a/src/watch/state.rs b/src/watch/state.rs index 751285fc58..da5ac3d70d 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -149,6 +149,12 @@ You can keep working on this exercise or jump into the next one by removing the let progress_bar = progress_bar(self.progress, self.exercises.len() as u16, line_width)?; self.writer.write_all(progress_bar.as_bytes())?; + self.writer.write_all(b"Current exercise: ")?; + self.writer.write_fmt(format_args!( + "{}", + self.exercise.path.to_string_lossy().bold() + ))?; + self.show_prompt()?; Ok(()) From 193e0a03b2cde094b2a668371b7ed94f81d33de7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 14:31:08 +0200 Subject: [PATCH 0674/1432] Use light blue for the message --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index 8918979485..209374b1b4 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -186,7 +186,7 @@ impl UiState { "↓/j ↑/k home/g end/G β”‚ filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit", ) } else { - self.message.as_str().blue() + self.message.as_str().light_blue() }; frame.render_widget( message, From b3642b0219252e97213fd4348379f272a3002f39 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 14:35:42 +0200 Subject: [PATCH 0675/1432] Remove todo --- src/state_file.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/state_file.rs b/src/state_file.rs index 693c78dcbc..583e0436d9 100644 --- a/src/state_file.rs +++ b/src/state_file.rs @@ -33,7 +33,6 @@ impl StateFile { } fn write(&self) -> Result<()> { - // TODO: Capacity let mut buf = Vec::with_capacity(1024); serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; fs::write(".rustlings-state.json", buf) From 27e95206658e8f86cad351ce163f03c0d36e05ea Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 14:40:49 +0200 Subject: [PATCH 0676/1432] Add deny_unknown_fields --- src/exercise.rs | 2 ++ src/state_file.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/exercise.rs b/src/exercise.rs index 232d7f951d..ca47009d43 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -46,6 +46,7 @@ pub enum Mode { } #[derive(Deserialize)] +#[serde(deny_unknown_fields)] pub struct InfoFile { pub exercises: Vec, } @@ -65,6 +66,7 @@ impl InfoFile { // Deserialized from the `info.toml` file. #[derive(Deserialize)] +#[serde(deny_unknown_fields)] pub struct Exercise { // Name of the exercise pub name: String, diff --git a/src/state_file.rs b/src/state_file.rs index 583e0436d9..6b80354e8f 100644 --- a/src/state_file.rs +++ b/src/state_file.rs @@ -5,6 +5,7 @@ use std::fs; use crate::exercise::Exercise; #[derive(Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct StateFile { next_exercise_ind: usize, progress: Vec, From 256c4013b759368b97f08aeb38d1b03f2eb42d7a Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 15:56:38 +0200 Subject: [PATCH 0677/1432] Keep hint displayed after resizing the terminal --- src/watch.rs | 4 +--- src/watch/state.rs | 41 +++++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index cf63627d52..6d791f4bf6 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -29,7 +29,6 @@ pub enum WatchExit { enum InputEvent { Hint, - Clear, List, Quit, Unrecognized(String), @@ -106,7 +105,6 @@ fn terminal_event_handler(tx: Sender) { KeyCode::Enter => { let input_event = match input.trim() { "h" | "hint" => InputEvent::Hint, - "c" | "clear" => InputEvent::Clear, "l" | "list" => break InputEvent::List, "q" | "quit" => break InputEvent::Quit, _ => InputEvent::Unrecognized(input.clone()), @@ -165,7 +163,7 @@ pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resu WatchEvent::Input(InputEvent::List) => { return Ok(WatchExit::List); } - WatchEvent::Input(InputEvent::Clear) | WatchEvent::TerminalResize => { + WatchEvent::TerminalResize => { watch_state.render()?; } WatchEvent::Input(InputEvent::Quit) => break, diff --git a/src/watch/state.rs b/src/watch/state.rs index da5ac3d70d..6f6d2f1076 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -6,7 +6,7 @@ use crossterm::{ }; use std::{ fmt::Write as _, - io::{self, StdoutLock, Write as _}, + io::{self, StdoutLock, Write}, }; use crate::{ @@ -24,7 +24,7 @@ pub struct WatchState<'a> { stdout: Option>, stderr: Option>, message: Option, - prompt: Vec, + hint_displayed: bool, } impl<'a> WatchState<'a> { @@ -35,15 +35,6 @@ impl<'a> WatchState<'a> { let writer = io::stdout().lock(); - let prompt = format!( - "\n\n{}int/{}lear/{}ist/{}uit? ", - "h".bold(), - "c".bold(), - "l".bold(), - "q".bold(), - ) - .into_bytes(); - Self { writer, exercises, @@ -53,7 +44,7 @@ impl<'a> WatchState<'a> { stdout: None, stderr: None, message: None, - prompt, + hint_displayed: false, } } @@ -122,7 +113,15 @@ You can keep working on this exercise or jump into the next one by removing the } pub fn show_prompt(&mut self) -> io::Result<()> { - self.writer.write_all(&self.prompt)?; + self.writer.write_all(b"\n\n")?; + + if !self.hint_displayed { + self.writer.write_fmt(format_args!("{}int/", 'h'.bold()))?; + } + + self.writer + .write_fmt(format_args!("{}ist/{}uit? ", 'l'.bold(), 'q'.bold()))?; + self.writer.flush() } @@ -134,10 +133,12 @@ You can keep working on this exercise or jump into the next one by removing the if let Some(stdout) = &self.stdout { self.writer.write_all(stdout)?; + self.writer.write_all(b"\n")?; } if let Some(stderr) = &self.stderr { self.writer.write_all(stderr)?; + self.writer.write_all(b"\n")?; } if let Some(message) = &self.message { @@ -145,6 +146,14 @@ You can keep working on this exercise or jump into the next one by removing the } self.writer.write_all(b"\n")?; + + if self.hint_displayed { + self.writer + .write_fmt(format_args!("\n{}\n", "Hint".bold().cyan().underlined()))?; + self.writer.write_all(self.exercise.hint.as_bytes())?; + self.writer.write_all(b"\n\n")?; + } + let line_width = size()?.0; let progress_bar = progress_bar(self.progress, self.exercises.len() as u16, line_width)?; self.writer.write_all(progress_bar.as_bytes())?; @@ -160,9 +169,9 @@ You can keep working on this exercise or jump into the next one by removing the Ok(()) } - pub fn show_hint(&mut self) -> io::Result<()> { - self.writer.write_all(self.exercise.hint.as_bytes())?; - self.show_prompt() + pub fn show_hint(&mut self) -> Result<()> { + self.hint_displayed = true; + self.render() } pub fn handle_invalid_cmd(&mut self, cmd: &str) -> io::Result<()> { From 4bb6bda9f6416e30233342e73fc9a8486faa3f98 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Apr 2024 16:02:12 +0200 Subject: [PATCH 0678/1432] Separate event handlers --- src/watch.rs | 123 ++++-------------------------------- src/watch/debounce_event.rs | 44 +++++++++++++ src/watch/terminal_event.rs | 65 +++++++++++++++++++ 3 files changed, 123 insertions(+), 109 deletions(-) create mode 100644 src/watch/debounce_event.rs create mode 100644 src/watch/terminal_event.rs diff --git a/src/watch.rs b/src/watch.rs index 6d791f4bf6..b29169b3e4 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,38 +1,27 @@ use anyhow::{Error, Result}; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; use notify_debouncer_mini::{ new_debouncer, notify::{self, RecursiveMode}, - DebounceEventResult, DebouncedEventKind, }; use std::{ io::{self, Write}, path::Path, - sync::mpsc::{channel, Sender}, + sync::mpsc::channel, thread, time::Duration, }; +mod debounce_event; mod state; +mod terminal_event; use crate::{exercise::Exercise, state_file::StateFile}; -use self::state::WatchState; - -/// Returned by the watch mode to indicate what to do afterwards. -pub enum WatchExit { - /// Exit the program. - Shutdown, - /// Enter the list mode and restart the watch mode afterwards. - List, -} - -enum InputEvent { - Hint, - List, - Quit, - Unrecognized(String), -} +use self::{ + debounce_event::DebounceEventHandler, + state::WatchState, + terminal_event::{terminal_event_handler, InputEvent}, +}; enum WatchEvent { Input(InputEvent), @@ -42,96 +31,12 @@ enum WatchEvent { TerminalResize, } -struct DebounceEventHandler { - tx: Sender, - exercises: &'static [Exercise], -} - -impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { - fn handle_event(&mut self, event: DebounceEventResult) { - let event = match event { - Ok(event) => { - let Some(exercise_ind) = event - .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { - return None; - } - - self.exercises - .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) - }) - .min() - else { - return; - }; - - WatchEvent::FileChange { exercise_ind } - } - Err(e) => WatchEvent::NotifyErr(e), - }; - - // An error occurs when the receiver is dropped. - // After dropping the receiver, the debouncer guard should also be dropped. - let _ = self.tx.send(event); - } -} - -fn terminal_event_handler(tx: Sender) { - let mut input = String::with_capacity(8); - - let last_input_event = loop { - let terminal_event = match event::read() { - Ok(v) => v, - Err(e) => { - // If `send` returns an error, then the receiver is dropped and - // a shutdown has been already initialized. - let _ = tx.send(WatchEvent::TerminalEventErr(e)); - return; - } - }; - - match terminal_event { - Event::Key(key) => { - match key.kind { - KeyEventKind::Release => continue, - KeyEventKind::Press | KeyEventKind::Repeat => (), - } - - match key.code { - KeyCode::Enter => { - let input_event = match input.trim() { - "h" | "hint" => InputEvent::Hint, - "l" | "list" => break InputEvent::List, - "q" | "quit" => break InputEvent::Quit, - _ => InputEvent::Unrecognized(input.clone()), - }; - - if tx.send(WatchEvent::Input(input_event)).is_err() { - return; - } - - input.clear(); - } - KeyCode::Char(c) => { - input.push(c); - } - _ => (), - } - } - Event::Resize(_, _) => { - if tx.send(WatchEvent::TerminalResize).is_err() { - return; - } - } - Event::FocusGained | Event::FocusLost | Event::Mouse(_) | Event::Paste(_) => continue, - } - }; - - let _ = tx.send(WatchEvent::Input(last_input_event)); +/// Returned by the watch mode to indicate what to do afterwards. +pub enum WatchExit { + /// Exit the program. + Shutdown, + /// Enter the list mode and restart the watch mode afterwards. + List, } pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result { diff --git a/src/watch/debounce_event.rs b/src/watch/debounce_event.rs new file mode 100644 index 0000000000..1dc92cb49b --- /dev/null +++ b/src/watch/debounce_event.rs @@ -0,0 +1,44 @@ +use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; +use std::sync::mpsc::Sender; + +use crate::exercise::Exercise; + +use super::WatchEvent; + +pub struct DebounceEventHandler { + pub tx: Sender, + pub exercises: &'static [Exercise], +} + +impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { + fn handle_event(&mut self, event: DebounceEventResult) { + let event = match event { + Ok(event) => { + let Some(exercise_ind) = event + .iter() + .filter_map(|event| { + if event.kind != DebouncedEventKind::Any + || !event.path.extension().is_some_and(|ext| ext == "rs") + { + return None; + } + + self.exercises + .iter() + .position(|exercise| event.path.ends_with(&exercise.path)) + }) + .min() + else { + return; + }; + + WatchEvent::FileChange { exercise_ind } + } + Err(e) => WatchEvent::NotifyErr(e), + }; + + // An error occurs when the receiver is dropped. + // After dropping the receiver, the debouncer guard should also be dropped. + let _ = self.tx.send(event); + } +} diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs new file mode 100644 index 0000000000..7c85b5bdf7 --- /dev/null +++ b/src/watch/terminal_event.rs @@ -0,0 +1,65 @@ +use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use std::sync::mpsc::Sender; + +use super::WatchEvent; + +pub enum InputEvent { + Hint, + List, + Quit, + Unrecognized(String), +} + +pub fn terminal_event_handler(tx: Sender) { + let mut input = String::with_capacity(8); + + let last_input_event = loop { + let terminal_event = match event::read() { + Ok(v) => v, + Err(e) => { + // If `send` returns an error, then the receiver is dropped and + // a shutdown has been already initialized. + let _ = tx.send(WatchEvent::TerminalEventErr(e)); + return; + } + }; + + match terminal_event { + Event::Key(key) => { + match key.kind { + KeyEventKind::Release => continue, + KeyEventKind::Press | KeyEventKind::Repeat => (), + } + + match key.code { + KeyCode::Enter => { + let input_event = match input.trim() { + "h" | "hint" => InputEvent::Hint, + "l" | "list" => break InputEvent::List, + "q" | "quit" => break InputEvent::Quit, + _ => InputEvent::Unrecognized(input.clone()), + }; + + if tx.send(WatchEvent::Input(input_event)).is_err() { + return; + } + + input.clear(); + } + KeyCode::Char(c) => { + input.push(c); + } + _ => (), + } + } + Event::Resize(_, _) => { + if tx.send(WatchEvent::TerminalResize).is_err() { + return; + } + } + Event::FocusGained | Event::FocusLost | Event::Mouse(_) | Event::Paste(_) => continue, + } + }; + + let _ = tx.send(WatchEvent::Input(last_input_event)); +} From fa1f239a702eb2c0b7e0115e986481156961bbc8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:02 +0200 Subject: [PATCH 0679/1432] Remove "I AM NOT DONE" and the verify mode and add AppState --- Cargo.lock | 1 - Cargo.toml | 1 - README.md | 8 +- exercises/00_intro/intro1.rs | 4 +- exercises/00_intro/intro2.rs | 2 - exercises/01_variables/variables1.rs | 2 - exercises/01_variables/variables2.rs | 2 - exercises/01_variables/variables3.rs | 2 - exercises/01_variables/variables4.rs | 2 - exercises/01_variables/variables5.rs | 2 - exercises/01_variables/variables6.rs | 2 - exercises/02_functions/functions1.rs | 2 - exercises/02_functions/functions2.rs | 2 - exercises/02_functions/functions3.rs | 2 - exercises/02_functions/functions4.rs | 2 - exercises/02_functions/functions5.rs | 2 - exercises/03_if/if1.rs | 2 - exercises/03_if/if2.rs | 2 - exercises/03_if/if3.rs | 2 - .../04_primitive_types/primitive_types1.rs | 2 - .../04_primitive_types/primitive_types2.rs | 2 - .../04_primitive_types/primitive_types3.rs | 2 - .../04_primitive_types/primitive_types4.rs | 2 - .../04_primitive_types/primitive_types5.rs | 2 - .../04_primitive_types/primitive_types6.rs | 2 - exercises/05_vecs/vecs1.rs | 2 - exercises/05_vecs/vecs2.rs | 2 - .../06_move_semantics/move_semantics1.rs | 2 - .../06_move_semantics/move_semantics2.rs | 2 - .../06_move_semantics/move_semantics3.rs | 2 - .../06_move_semantics/move_semantics4.rs | 2 - .../06_move_semantics/move_semantics5.rs | 2 - .../06_move_semantics/move_semantics6.rs | 2 - exercises/07_structs/structs1.rs | 2 - exercises/07_structs/structs2.rs | 2 - exercises/07_structs/structs3.rs | 2 - exercises/08_enums/enums1.rs | 2 - exercises/08_enums/enums2.rs | 2 - exercises/08_enums/enums3.rs | 2 - exercises/09_strings/strings1.rs | 2 - exercises/09_strings/strings2.rs | 2 - exercises/09_strings/strings3.rs | 2 - exercises/09_strings/strings4.rs | 2 - exercises/10_modules/modules1.rs | 2 - exercises/10_modules/modules2.rs | 2 - exercises/10_modules/modules3.rs | 2 - exercises/11_hashmaps/hashmaps1.rs | 2 - exercises/11_hashmaps/hashmaps2.rs | 2 - exercises/11_hashmaps/hashmaps3.rs | 2 - exercises/12_options/options1.rs | 2 - exercises/12_options/options2.rs | 2 - exercises/12_options/options3.rs | 2 - exercises/13_error_handling/errors1.rs | 2 - exercises/13_error_handling/errors2.rs | 2 - exercises/13_error_handling/errors3.rs | 2 - exercises/13_error_handling/errors4.rs | 2 - exercises/13_error_handling/errors5.rs | 2 - exercises/13_error_handling/errors6.rs | 2 - exercises/14_generics/generics1.rs | 2 - exercises/14_generics/generics2.rs | 2 - exercises/15_traits/traits1.rs | 2 - exercises/15_traits/traits2.rs | 2 - exercises/15_traits/traits3.rs | 2 - exercises/15_traits/traits4.rs | 2 - exercises/15_traits/traits5.rs | 2 - exercises/16_lifetimes/lifetimes1.rs | 2 - exercises/16_lifetimes/lifetimes2.rs | 2 - exercises/16_lifetimes/lifetimes3.rs | 2 - exercises/17_tests/tests1.rs | 2 - exercises/17_tests/tests2.rs | 2 - exercises/17_tests/tests3.rs | 2 - exercises/17_tests/tests4.rs | 2 - exercises/18_iterators/iterators1.rs | 2 - exercises/18_iterators/iterators2.rs | 2 - exercises/18_iterators/iterators3.rs | 2 - exercises/18_iterators/iterators4.rs | 2 - exercises/18_iterators/iterators5.rs | 2 - exercises/19_smart_pointers/arc1.rs | 2 - exercises/19_smart_pointers/box1.rs | 2 - exercises/19_smart_pointers/cow1.rs | 2 - exercises/19_smart_pointers/rc1.rs | 2 - exercises/20_threads/threads1.rs | 2 - exercises/20_threads/threads2.rs | 2 - exercises/20_threads/threads3.rs | 2 - exercises/21_macros/macros1.rs | 2 - exercises/21_macros/macros2.rs | 2 - exercises/21_macros/macros3.rs | 2 - exercises/21_macros/macros4.rs | 2 - exercises/22_clippy/clippy1.rs | 2 - exercises/22_clippy/clippy2.rs | 2 - exercises/22_clippy/clippy3.rs | 2 - exercises/23_conversions/as_ref_mut.rs | 2 - exercises/23_conversions/from_into.rs | 2 - exercises/23_conversions/from_str.rs | 2 - exercises/23_conversions/try_from_into.rs | 2 - exercises/23_conversions/using_as.rs | 2 - exercises/quiz1.rs | 2 - exercises/quiz2.rs | 2 - exercises/quiz3.rs | 2 - info.toml | 7 +- src/app_state.rs | 185 ++++++++++++++++ src/exercise.rs | 206 +----------------- src/list.rs | 21 +- src/list/state.rs | 71 +++--- src/main.rs | 67 ++---- src/run.rs | 23 +- src/state_file.rs | 68 ------ src/verify.rs | 85 -------- src/watch.rs | 10 +- src/watch/state.rs | 96 ++------ .../state/exercises/pending_exercise.rs | 2 - .../state/exercises/pending_test_exercise.rs | 2 - tests/integration_tests.rs | 28 +-- 113 files changed, 306 insertions(+), 769 deletions(-) create mode 100644 src/app_state.rs delete mode 100644 src/state_file.rs delete mode 100644 src/verify.rs diff --git a/Cargo.lock b/Cargo.lock index ee4694376a..aeb6c61ff5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -699,7 +699,6 @@ dependencies = [ "serde_json", "toml_edit", "which", - "winnow", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index da09ba18fd..435dfd4957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ serde_json = "1.0.115" serde.workspace = true toml_edit.workspace = true which = "6.0.1" -winnow = "0.6.5" [dev-dependencies] assert_cmd = "2.0.14" diff --git a/README.md b/README.md index 6b9c98336d..fd76fdff88 100644 --- a/README.md +++ b/README.md @@ -101,13 +101,7 @@ The task is simple. Most exercises contain an error that keeps them from compili rustlings watch ``` -This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. If you want to only run it once, you can use: - -```bash -rustlings verify -``` - -This will do the same as watch, but it'll quit after running. +This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. In case you want to go by your own order, or want to only verify a single exercise, you can run: diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 5dd18b4599..aa505a13ac 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,6 @@ // intro1.rs // -// About this `I AM NOT DONE` thing: +// TODO: Update comment // We sometimes encourage you to keep trying things on a given exercise, even // after you already figured it out. If you got everything working and feel // ready for the next exercise, remove the `I AM NOT DONE` comment below. @@ -13,8 +13,6 @@ // Execute `rustlings hint intro1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { println!("Hello and"); println!(r#" welcome to... "#); diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index a28ad3dc45..84e0d75c05 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint intro2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { printline!("Hello there!") } diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs index b3e089a527..56408f3532 100644 --- a/exercises/01_variables/variables1.rs +++ b/exercises/01_variables/variables1.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint variables1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { x = 5; println!("x has the value {}", x); diff --git a/exercises/01_variables/variables2.rs b/exercises/01_variables/variables2.rs index e1c23edf7d..0f417e017a 100644 --- a/exercises/01_variables/variables2.rs +++ b/exercises/01_variables/variables2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let x; if x == 10 { diff --git a/exercises/01_variables/variables3.rs b/exercises/01_variables/variables3.rs index 86bed419f4..421c6b1549 100644 --- a/exercises/01_variables/variables3.rs +++ b/exercises/01_variables/variables3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let x: i32; println!("Number {}", x); diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 5394f39462..68f8f50b02 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let x = 3; println!("Number {}", x); diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index a29b38be8e..7014c56874 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - 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 853183babc..9f47682519 100644 --- a/exercises/01_variables/variables6.rs +++ b/exercises/01_variables/variables6.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables6` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - const NUMBER = 3; fn main() { println!("Number {}", NUMBER); diff --git a/exercises/02_functions/functions1.rs b/exercises/02_functions/functions1.rs index 40ed9a0753..2365f91b28 100644 --- a/exercises/02_functions/functions1.rs +++ b/exercises/02_functions/functions1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { call_me(); } diff --git a/exercises/02_functions/functions2.rs b/exercises/02_functions/functions2.rs index 5154f34d8c..64dbd66546 100644 --- a/exercises/02_functions/functions2.rs +++ b/exercises/02_functions/functions2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { call_me(3); } diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs index 74f44d6d08..503712122e 100644 --- a/exercises/02_functions/functions3.rs +++ b/exercises/02_functions/functions3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { call_me(); } diff --git a/exercises/02_functions/functions4.rs b/exercises/02_functions/functions4.rs index 77c4b2aa3b..6b449edf21 100644 --- a/exercises/02_functions/functions4.rs +++ b/exercises/02_functions/functions4.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint functions4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let original_price = 51; println!("Your sale price is {}", sale_price(original_price)); diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs index f1b63f480b..0c963223d1 100644 --- a/exercises/02_functions/functions5.rs +++ b/exercises/02_functions/functions5.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - 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 d2afccf841..a1df66bbcd 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -2,8 +2,6 @@ // // Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! // If both numbers are equal, any of them can be returned. diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index f512f13ffc..7b9c05f6a4 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -5,8 +5,6 @@ // // Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index 169627406c..caba172bb9 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -2,8 +2,6 @@ // // Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub fn animal_habitat(animal: &str) -> &'static str { let identifier = if animal == "crab" { 1 diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index 36633400b5..f9169c8489 100644 --- a/exercises/04_primitive_types/primitive_types1.rs +++ b/exercises/04_primitive_types/primitive_types1.rs @@ -3,8 +3,6 @@ // Fill in the rest of the line that has code missing! No hints, there's no // tricks, just get used to typing these :) -// I AM NOT DONE - fn main() { // Booleans (`bool`) diff --git a/exercises/04_primitive_types/primitive_types2.rs b/exercises/04_primitive_types/primitive_types2.rs index f1616ed3b2..1911b12a1f 100644 --- a/exercises/04_primitive_types/primitive_types2.rs +++ b/exercises/04_primitive_types/primitive_types2.rs @@ -3,8 +3,6 @@ // Fill in the rest of the line that has code missing! No hints, there's no // tricks, just get used to typing these :) -// I AM NOT DONE - fn main() { // Characters (`char`) diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index 8b0de44e96..70a8cc20bc 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - fn main() { let a = ??? diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index d44d8776fa..8ed0a82a32 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs index f646986ea8..5754a3d89f 100644 --- a/exercises/04_primitive_types/primitive_types5.rs +++ b/exercises/04_primitive_types/primitive_types5.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - fn main() { let cat = ("Furry McFurson", 3.5); let /* your pattern here */ = cat; diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs index 07cc46c65a..5f82f10fc5 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn indexing_tuple() { let numbers = (1, 2, 3); diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index 65b7a7f835..c64acbbd80 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -7,8 +7,6 @@ // // Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn array_and_vec() -> ([i32; 4], Vec) { let a = [10, 20, 30, 40]; // a plain array let v = // TODO: declare your vector here with the macro for vectors diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index e92c970aab..d64d3d1640 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -7,8 +7,6 @@ // // Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn vec_loop(mut v: Vec) -> Vec { for element in v.iter_mut() { // TODO: Fill this up so that each element in the Vec `v` is diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs index e063937539..c612ba937e 100644 --- a/exercises/06_move_semantics/move_semantics1.rs +++ b/exercises/06_move_semantics/move_semantics1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index dc58be5086..3457d111f1 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs index 7152c71681..9415eb159a 100644 --- a/exercises/06_move_semantics/move_semantics3.rs +++ b/exercises/06_move_semantics/move_semantics3.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index bfc917fa87..1509f5d23f 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index 267bdccc24..c84d2fea2c 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let mut x = 100; diff --git a/exercises/06_move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs index cace4ca656..6059e61c52 100644 --- a/exercises/06_move_semantics/move_semantics6.rs +++ b/exercises/06_move_semantics/move_semantics6.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - fn main() { let data = "Rust is great!".to_string(); diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs index 5fa5821c5b..2978121440 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint structs1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct ColorClassicStruct { // TODO: Something goes here } diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs index 328567f03a..a7a2deca11 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint structs2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(Debug)] struct Order { name: String, diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 7cda5af103..9835b811ac 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint structs3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(Debug)] struct Package { sender_country: String, diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index 25525b252d..330269ca44 100644 --- a/exercises/08_enums/enums1.rs +++ b/exercises/08_enums/enums1.rs @@ -2,8 +2,6 @@ // // No hints this time! ;) -// I AM NOT DONE - #[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 df93fe0f15..f0e4e6d341 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint enums2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[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 92d18c46a4..580a553ee7 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint enums3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - enum Message { // TODO: implement the message variant types based on their usage below } diff --git a/exercises/09_strings/strings1.rs b/exercises/09_strings/strings1.rs index f50e1fa988..a1255a329b 100644 --- a/exercises/09_strings/strings1.rs +++ b/exercises/09_strings/strings1.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint strings1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let answer = current_favorite_color(); println!("My current favorite color is {}", answer); diff --git a/exercises/09_strings/strings2.rs b/exercises/09_strings/strings2.rs index 4d95d16a16..ba76fe656a 100644 --- a/exercises/09_strings/strings2.rs +++ b/exercises/09_strings/strings2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint strings2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let word = String::from("green"); // Try not changing this line :) if is_a_color_word(word) { diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index 384e7ce3ea..dedc081fa7 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint strings3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn trim_me(input: &str) -> String { // TODO: Remove whitespace from both ends of a string! ??? diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index e8c54acc55..a034aa49bc 100644 --- a/exercises/09_strings/strings4.rs +++ b/exercises/09_strings/strings4.rs @@ -7,8 +7,6 @@ // // No hints this time! -// I AM NOT DONE - fn string_slice(arg: &str) { println!("{}", arg); } diff --git a/exercises/10_modules/modules1.rs b/exercises/10_modules/modules1.rs index 9eb5a48b72..c750946c07 100644 --- a/exercises/10_modules/modules1.rs +++ b/exercises/10_modules/modules1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint modules1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - 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 0415454313..4d3106c4e6 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint modules2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - mod delicious_snacks { // TODO: Fix these use statements use self::fruits::PEAR as ??? diff --git a/exercises/10_modules/modules3.rs b/exercises/10_modules/modules3.rs index f2bb050389..c211a7695f 100644 --- a/exercises/10_modules/modules3.rs +++ b/exercises/10_modules/modules3.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint modules3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // TODO: Complete this use statement use ??? diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index 80829eaa6c..5a52f611d4 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -11,8 +11,6 @@ // Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; fn fruit_basket() -> HashMap { diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index a592569093..273064395a 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -14,8 +14,6 @@ // Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; #[derive(Hash, PartialEq, Eq)] diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 8d9236df96..775a401483 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -15,8 +15,6 @@ // Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; // A structure to store the goal details of a team. diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index 3cbfecd66f..ba4b1cda9d 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint options1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // This function returns how much icecream there is left in the fridge. // If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it // all, so there'll be no more left :( diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index 4d998e7d02..73f707e33f 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint options2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[cfg(test)] mod tests { #[test] diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 23c15eab80..7922ef92e0 100644 --- a/exercises/12_options/options3.rs +++ b/exercises/12_options/options3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint options3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Point { x: i32, y: i32, diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index 0ba59a57a6..9767f2c889 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint errors1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub fn generate_nametag_text(name: String) -> Option { if name.is_empty() { // Empty names aren't allowed. diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 631fe67f90..88d1bf436f 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -19,8 +19,6 @@ // Execute `rustlings hint errors2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::num::ParseIntError; pub fn total_cost(item_quantity: &str) -> Result { diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index d42d3b17ca..56bb31b1b0 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint errors3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::num::ParseIntError; fn main() { diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index d6d6fcb6ab..0e5c08bfe6 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint errors4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 92461a7e01..0bcb4b8ca5 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -22,8 +22,6 @@ // Execute `rustlings hint errors5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::error; use std::fmt; use std::num::ParseIntError; diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index aaf0948ef7..de73a9a5fe 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint errors6` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::num::ParseIntError; // This is a custom error type that we will be using in `parse_pos_nonzero()`. diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs index 35c1d2feed..545fd95c01 100644 --- a/exercises/14_generics/generics1.rs +++ b/exercises/14_generics/generics1.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint generics1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let mut shopping_list: Vec = Vec::new(); shopping_list.push("milk"); diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index 074cd938c6..d50ed17445 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint generics2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Wrapper { value: u32, } diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs index 37dfcbfe85..c51d3b88b7 100644 --- a/exercises/15_traits/traits1.rs +++ b/exercises/15_traits/traits1.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint traits1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - trait AppendBar { fn append_bar(self) -> Self; } diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index 3e35f8e11b..9a2bc07a9e 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -8,8 +8,6 @@ // // Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - trait AppendBar { fn append_bar(self) -> Self; } diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 4e2b06b0e5..357f1d7f7a 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint traits3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub trait Licensed { fn licensing_info(&self) -> String; } diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index 4bda3e571e..7242c4834d 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint traits4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub trait Licensed { fn licensing_info(&self) -> String { "some information".to_string() diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index df1838054a..f258d32738 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint traits5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub trait SomeTrait { fn some_function(&self) -> bool { true diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index 87bde490cc..4f544b41c3 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs index 4f3d8c185b..33b5565fba 100644 --- a/exercises/16_lifetimes/lifetimes2.rs +++ b/exercises/16_lifetimes/lifetimes2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs index 9c59f9c021..de6005ecef 100644 --- a/exercises/16_lifetimes/lifetimes3.rs +++ b/exercises/16_lifetimes/lifetimes3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Book { author: &str, title: &str, diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index 810277ac3d..bde21083f7 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -10,8 +10,6 @@ // Execute `rustlings hint tests1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[cfg(test)] mod tests { #[test] diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index f8024e9f22..aea5c0e4c3 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint tests2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[cfg(test)] mod tests { #[test] diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index 4013e38415..d815e0587d 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint tests3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub fn is_even(num: i32) -> bool { num % 2 == 0 } diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs index 935d0db173..0972a5b4f6 100644 --- a/exercises/17_tests/tests4.rs +++ b/exercises/17_tests/tests4.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint tests4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Rectangle { width: i32, height: i32 diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 31076bb9da..7ec7da2caf 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[test] fn main() { let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index dda82a085f..4ca7742e94 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // Step 1. // Complete the `capitalize_first` function. // "hello" -> "Hello" diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 29fa23a3e3..f7da049c8d 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(Debug, PartialEq, Eq)] pub enum DivisionError { NotDivisible(NotDivisibleError), diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 3c0724e9b8..af3958c820 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub fn factorial(num: u64) -> u64 { // Complete this function to return the factorial of num // Do not use: diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index a062ee4c74..ceec536913 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -11,8 +11,6 @@ // Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; #[derive(Clone, Copy, PartialEq, Eq)] diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index 3526ddcb9a..0647eea7dc 100644 --- a/exercises/19_smart_pointers/arc1.rs +++ b/exercises/19_smart_pointers/arc1.rs @@ -21,8 +21,6 @@ // // Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - #![forbid(unused_imports)] // Do not change this, (or the next) line. use std::sync::Arc; use std::thread; diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index 513e7daa37..2abc0249c2 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -18,8 +18,6 @@ // // Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - #[derive(PartialEq, Debug)] pub enum List { Cons(i32, List), diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index fcd3e0bb14..b24591b7f1 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -12,8 +12,6 @@ // // Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - use std::borrow::Cow; fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index 1b903469aa..e96e6255a6 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -10,8 +10,6 @@ // // Execute `rustlings hint rc1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - use std::rc::Rc; #[derive(Debug)] diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index 80b6def3e9..be1301d738 100644 --- a/exercises/20_threads/threads1.rs +++ b/exercises/20_threads/threads1.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint threads1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::thread; use std::time::{Duration, Instant}; diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs index 60d68241c8..13cb840e36 100644 --- a/exercises/20_threads/threads2.rs +++ b/exercises/20_threads/threads2.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint threads2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::sync::Arc; use std::thread; use std::time::Duration; diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index acb97b4bb1..35b914ac27 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint threads3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::sync::mpsc; use std::sync::Arc; use std::thread; diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs index 678de6eec2..65986db00c 100644 --- a/exercises/21_macros/macros1.rs +++ b/exercises/21_macros/macros1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint macros1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/21_macros/macros2.rs b/exercises/21_macros/macros2.rs index 788fc16a92..b7c37fd9ce 100644 --- a/exercises/21_macros/macros2.rs +++ b/exercises/21_macros/macros2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint macros2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { my_macro!(); } diff --git a/exercises/21_macros/macros3.rs b/exercises/21_macros/macros3.rs index b795c14939..92a19227c4 100644 --- a/exercises/21_macros/macros3.rs +++ b/exercises/21_macros/macros3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint macros3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - mod macros { macro_rules! my_macro { () => { diff --git a/exercises/21_macros/macros4.rs b/exercises/21_macros/macros4.rs index 71b45a0955..83a6e44f6a 100644 --- a/exercises/21_macros/macros4.rs +++ b/exercises/21_macros/macros4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint macros4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[rustfmt::skip] macro_rules! my_macro { () => { diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index e0c6ce7c4e..1e0f42e2d0 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::f32; fn main() { diff --git a/exercises/22_clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs index 9b87a0b700..37ac089ea3 100644 --- a/exercises/22_clippy/clippy2.rs +++ b/exercises/22_clippy/clippy2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - 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 5a95f5b821..6a6a36b5f7 100644 --- a/exercises/22_clippy/clippy3.rs +++ b/exercises/22_clippy/clippy3.rs @@ -3,8 +3,6 @@ // Here's a couple more easy Clippy fixes, so you can see its utility. // No hints. -// I AM NOT DONE - #[allow(unused_variables, unused_assignments)] fn main() { let my_option: Option<()> = None; diff --git a/exercises/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs index 2ba9e3f0f9..cd2c93beab 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // Obtain the number of bytes (not characters) in the given argument. // TODO: Add the AsRef trait appropriately as a trait bound. fn byte_counter(arg: T) -> usize { diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 11787c37dc..d2a1609e71 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -41,8 +41,6 @@ impl Default for Person { // If while parsing the age, something goes wrong, then return the default of // Person Otherwise, then return an instantiated Person object with the results -// I AM NOT DONE - impl From<&str> for Person { fn from(s: &str) -> Person {} } diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index e209347475..ed91ca5c0c 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -31,8 +31,6 @@ enum ParsePersonError { ParseInt(ParseIntError), } -// I AM NOT DONE - // Steps: // 1. If the length of the provided string is 0, an error should be returned // 2. Split the given string on the commas present in it diff --git a/exercises/23_conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs index 32d6ef39e1..23166555a0 100644 --- a/exercises/23_conversions/try_from_into.rs +++ b/exercises/23_conversions/try_from_into.rs @@ -27,8 +27,6 @@ enum IntoColorError { IntConversion, } -// I AM NOT DONE - // Your task is to complete this implementation and return an Ok result of inner // type Color. You need to create an implementation for a tuple of three // integers, an array of three integers, and a slice of integers. diff --git a/exercises/23_conversions/using_as.rs b/exercises/23_conversions/using_as.rs index 414cef3a05..9f617ec58a 100644 --- a/exercises/23_conversions/using_as.rs +++ b/exercises/23_conversions/using_as.rs @@ -10,8 +10,6 @@ // Execute `rustlings hint using_as` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn average(values: &[f64]) -> f64 { let total = values.iter().sum::(); total / values.len() diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 4ee5ada7d8..b9e71f5925 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -13,8 +13,6 @@ // // No hints this time ;) -// I AM NOT DONE - // Put your function here! // fn calculate_price_of_apples { diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 29925cafcd..8ace3fe082 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -20,8 +20,6 @@ // // No hints this time! -// I AM NOT DONE - pub enum Command { Uppercase, Trim, diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs index 3b01d31320..24f708293b 100644 --- a/exercises/quiz3.rs +++ b/exercises/quiz3.rs @@ -16,8 +16,6 @@ // // Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub struct ReportCard { pub grade: f32, pub student_name: String, diff --git a/info.toml b/info.toml index 36629b38c9..c085e89c7b 100644 --- a/info.toml +++ b/info.toml @@ -4,6 +4,7 @@ name = "intro1" path = "exercises/00_intro/intro1.rs" mode = "compile" +# TODO: Fix hint hint = """ Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file to move on to the next exercise.""" @@ -129,11 +130,7 @@ path = "exercises/02_functions/functions3.rs" mode = "compile" hint = """ This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function. - -As a reminder, you can freely play around with different solutions in Rustlings! -Watch mode will only jump to the next exercise if you remove the `I AM NOT -DONE` comment.""" +with the place where we're calling the function.""" [[exercises]] name = "functions4" diff --git a/src/app_state.rs b/src/app_state.rs new file mode 100644 index 0000000000..4a0912e4ed --- /dev/null +++ b/src/app_state.rs @@ -0,0 +1,185 @@ +use anyhow::{bail, Context, Result}; +use serde::{Deserialize, Serialize}; +use std::fs; + +use crate::exercise::Exercise; + +const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; + +#[derive(Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +struct StateFile { + current_exercise_ind: usize, + progress: Vec, +} + +impl StateFile { + fn read(exercises: &[Exercise]) -> Option { + let file_content = fs::read(".rustlings-state.json").ok()?; + + let slf: Self = serde_json::de::from_slice(&file_content).ok()?; + + if slf.progress.len() != exercises.len() || slf.current_exercise_ind >= exercises.len() { + return None; + } + + Some(slf) + } + + fn read_or_default(exercises: &[Exercise]) -> Self { + Self::read(exercises).unwrap_or_else(|| Self { + current_exercise_ind: 0, + progress: vec![false; exercises.len()], + }) + } + + fn write(&self) -> Result<()> { + let mut buf = Vec::with_capacity(1024); + serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; + fs::write(".rustlings-state.json", buf) + .context("Failed to write the state file `.rustlings-state.json`")?; + + Ok(()) + } +} + +pub struct AppState { + state_file: StateFile, + exercises: &'static [Exercise], + n_done: u16, + current_exercise: &'static Exercise, +} + +#[must_use] +pub enum ExercisesProgress { + AllDone, + Pending, +} + +impl AppState { + pub fn new(exercises: Vec) -> Self { + // Leaking for sending the exercises to the debounce event handler. + // Leaking is not a problem since the exercises' slice is used until the end of the program. + let exercises = exercises.leak(); + + let state_file = StateFile::read_or_default(exercises); + let n_done = state_file + .progress + .iter() + .fold(0, |acc, done| acc + u16::from(*done)); + let current_exercise = &exercises[state_file.current_exercise_ind]; + + Self { + state_file, + exercises, + n_done, + current_exercise, + } + } + + #[inline] + pub fn current_exercise_ind(&self) -> usize { + self.state_file.current_exercise_ind + } + + #[inline] + pub fn progress(&self) -> &[bool] { + &self.state_file.progress + } + + #[inline] + pub fn exercises(&self) -> &'static [Exercise] { + self.exercises + } + + #[inline] + pub fn n_done(&self) -> u16 { + self.n_done + } + + #[inline] + pub fn current_exercise(&self) -> &'static Exercise { + self.current_exercise + } + + pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { + if ind >= self.exercises.len() { + bail!(BAD_INDEX_ERR); + } + + self.state_file.current_exercise_ind = ind; + self.current_exercise = &self.exercises[ind]; + + self.state_file.write() + } + + pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> { + let (ind, exercise) = self + .exercises + .iter() + .enumerate() + .find(|(_, exercise)| exercise.name == name) + .with_context(|| format!("No exercise found for '{name}'!"))?; + + self.state_file.current_exercise_ind = ind; + self.current_exercise = exercise; + + self.state_file.write() + } + + pub fn set_pending(&mut self, ind: usize) -> Result<()> { + let done = self + .state_file + .progress + .get_mut(ind) + .context(BAD_INDEX_ERR)?; + + if *done { + *done = false; + self.n_done -= 1; + self.state_file.write()?; + } + + Ok(()) + } + + fn next_exercise_ind(&self) -> Option { + let current_ind = self.state_file.current_exercise_ind; + + if current_ind == self.state_file.progress.len() - 1 { + // The last exercise is done. + // Search for exercises not done from the start. + return self.state_file.progress[..current_ind] + .iter() + .position(|done| !done); + } + + // The done exercise isn't the last one. + // Search for a pending exercise after the current one and then from the start. + match self.state_file.progress[current_ind + 1..] + .iter() + .position(|done| !done) + { + Some(ind) => Some(current_ind + 1 + ind), + None => self.state_file.progress[..current_ind] + .iter() + .position(|done| !done), + } + } + + pub fn done_current_exercise(&mut self) -> Result { + let done = &mut self.state_file.progress[self.state_file.current_exercise_ind]; + if !*done { + *done = true; + self.n_done += 1; + } + + let Some(ind) = self.next_exercise_ind() else { + return Ok(ExercisesProgress::AllDone); + }; + + self.set_current_exercise_ind(ind)?; + + Ok(ExercisesProgress::Pending) + } +} diff --git a/src/exercise.rs b/src/exercise.rs index ca47009d43..de435d1321 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,38 +1,14 @@ use anyhow::{Context, Result}; use serde::Deserialize; use std::{ - array, fmt::{self, Debug, Display, Formatter}, - fs::{self, File}, - io::{self, BufRead, BufReader}, - mem, + fs::{self}, path::PathBuf, process::{Command, Output}, }; -use winnow::{ - ascii::{space0, Caseless}, - combinator::opt, - Parser, -}; use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; -// The number of context lines above and below a highlighted line. -const CONTEXT: usize = 2; - -// Check if the line contains the "I AM NOT DONE" comment. -fn contains_not_done_comment(input: &str) -> bool { - ( - space0::<_, ()>, - "//", - opt('/'), - space0, - Caseless("I AM NOT DONE"), - ) - .parse_next(&mut &*input) - .is_ok() -} - // The mode of the exercise. #[derive(Deserialize, Copy, Clone)] #[serde(rename_all = "lowercase")] @@ -78,13 +54,6 @@ pub struct Exercise { pub hint: String, } -// The state of an Exercise. -#[derive(PartialEq, Eq, Debug)] -pub enum State { - Done, - Pending(Vec), -} - // The context information of a pending exercise. #[derive(PartialEq, Eq, Debug)] pub struct ContextLine { @@ -129,105 +98,6 @@ impl Exercise { } } - pub fn state(&self) -> Result { - let source_file = File::open(&self.path) - .with_context(|| format!("Failed to open the exercise file {}", self.path.display()))?; - let mut source_reader = BufReader::new(source_file); - - // Read the next line into `buf` without the newline at the end. - let mut read_line = |buf: &mut String| -> io::Result<_> { - let n = source_reader.read_line(buf)?; - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); - } - } - Ok(n) - }; - - let mut current_line_number: usize = 1; - // Keep the last `CONTEXT` lines while iterating over the file lines. - let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); - let mut line = String::with_capacity(256); - - loop { - let n = read_line(&mut line).with_context(|| { - format!("Failed to read the exercise file {}", self.path.display()) - })?; - - // Reached the end of the file and didn't find the comment. - if n == 0 { - return Ok(State::Done); - } - - if contains_not_done_comment(&line) { - let mut context = Vec::with_capacity(2 * CONTEXT + 1); - // Previous lines. - for (ind, prev_line) in prev_lines - .into_iter() - .take(current_line_number - 1) - .enumerate() - .rev() - { - context.push(ContextLine { - line: prev_line, - number: current_line_number - 1 - ind, - important: false, - }); - } - - // Current line. - context.push(ContextLine { - line, - number: current_line_number, - important: true, - }); - - // Next lines. - for ind in 0..CONTEXT { - let mut next_line = String::with_capacity(256); - let Ok(n) = read_line(&mut next_line) else { - // If an error occurs, just ignore the next lines. - break; - }; - - // Reached the end of the file. - if n == 0 { - break; - } - - context.push(ContextLine { - line: next_line, - number: current_line_number + 1 + ind, - important: false, - }); - } - - return Ok(State::Pending(context)); - } - - current_line_number += 1; - // Add the current line as a previous line and shift the older lines by one. - for prev_line in &mut prev_lines { - mem::swap(&mut line, prev_line); - } - // The current line now contains the oldest previous line. - // Recycle it for reading the next line. - line.clear(); - } - } - - // Check that the exercise looks to be solved using self.state() - // This is not the best way to check since - // the user can just remove the "I AM NOT DONE" string from the file - // without actually having solved anything. - // The only other way to truly check this would to compile and run - // the exercise; which would be both costly and counterintuitive - pub fn looks_done(&self) -> Result { - self.state().map(|state| state == State::Done) - } - pub fn reset(&self) -> Result<()> { EMBEDDED_FILES .write_exercise_to_disk(&self.path, WriteStrategy::Overwrite) @@ -240,77 +110,3 @@ impl Display for Exercise { self.path.fmt(f) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_pending_state() { - let exercise = Exercise { - name: "pending_exercise".into(), - path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"), - mode: Mode::Compile, - hint: String::new(), - }; - - let state = exercise.state(); - let expected = vec![ - ContextLine { - line: "// fake_exercise".to_string(), - number: 1, - important: false, - }, - ContextLine { - line: "".to_string(), - number: 2, - important: false, - }, - ContextLine { - line: "// I AM NOT DONE".to_string(), - number: 3, - important: true, - }, - ContextLine { - line: "".to_string(), - number: 4, - important: false, - }, - ContextLine { - line: "fn main() {".to_string(), - number: 5, - important: false, - }, - ]; - - assert_eq!(state.unwrap(), State::Pending(expected)); - } - - #[test] - fn test_finished_exercise() { - let exercise = Exercise { - name: "finished_exercise".into(), - path: PathBuf::from("tests/fixture/state/exercises/finished_exercise.rs"), - mode: Mode::Compile, - hint: String::new(), - }; - - assert_eq!(exercise.state().unwrap(), State::Done); - } - - #[test] - fn test_not_done() { - assert!(contains_not_done_comment("// I AM NOT DONE")); - assert!(contains_not_done_comment("/// I AM NOT DONE")); - assert!(contains_not_done_comment("// I AM NOT DONE")); - assert!(contains_not_done_comment("/// I AM NOT DONE")); - assert!(contains_not_done_comment("// I AM NOT DONE ")); - assert!(contains_not_done_comment("// I AM NOT DONE!")); - assert!(contains_not_done_comment("// I am not done")); - assert!(contains_not_done_comment("// i am NOT done")); - - assert!(!contains_not_done_comment("I AM NOT DONE")); - assert!(!contains_not_done_comment("// NOT DONE")); - assert!(!contains_not_done_comment("DONE")); - } -} diff --git a/src/list.rs b/src/list.rs index 560b85a874..80b78e8de8 100644 --- a/src/list.rs +++ b/src/list.rs @@ -9,11 +9,11 @@ use std::{fmt::Write, io}; mod state; -use crate::{exercise::Exercise, state_file::StateFile}; +use crate::app_state::AppState; use self::state::{Filter, UiState}; -pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result<()> { +pub fn list(app_state: &mut AppState) -> Result<()> { let mut stdout = io::stdout().lock(); stdout.execute(EnterAlternateScreen)?; enable_raw_mode()?; @@ -21,7 +21,7 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; terminal.clear()?; - let mut ui_state = UiState::new(state_file, exercises); + let mut ui_state = UiState::new(app_state); 'outer: loop { terminal.draw(|frame| ui_state.draw(frame).unwrap())?; @@ -56,7 +56,7 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul "Enabled filter DONE β”‚ Press d again to disable the filter" }; - ui_state = ui_state.with_updated_rows(state_file); + ui_state = ui_state.with_updated_rows(); ui_state.message.push_str(message); } KeyCode::Char('p') => { @@ -68,23 +68,20 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul "Enabled filter PENDING β”‚ Press p again to disable the filter" }; - ui_state = ui_state.with_updated_rows(state_file); + ui_state = ui_state.with_updated_rows(); ui_state.message.push_str(message); } KeyCode::Char('r') => { - let selected = ui_state.selected(); - let exercise = &exercises[selected]; - exercise.reset()?; - state_file.reset(selected)?; + let exercise = ui_state.reset_selected()?; - ui_state = ui_state.with_updated_rows(state_file); + ui_state = ui_state.with_updated_rows(); ui_state .message .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; } KeyCode::Char('c') => { - state_file.set_next_exercise_ind(ui_state.selected())?; - ui_state = ui_state.with_updated_rows(state_file); + ui_state.selected_to_current_exercise()?; + ui_state = ui_state.with_updated_rows(); } _ => (), } diff --git a/src/list/state.rs b/src/list/state.rs index 209374b1b4..7714268c5a 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -7,7 +7,7 @@ use ratatui::{ Frame, }; -use crate::{exercise::Exercise, progress_bar::progress_bar_ratatui, state_file::StateFile}; +use crate::{app_state::AppState, exercise::Exercise, progress_bar::progress_bar_ratatui}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -16,30 +16,29 @@ pub enum Filter { None, } -pub struct UiState { +pub struct UiState<'a> { pub table: Table<'static>, pub message: String, pub filter: Filter, - exercises: &'static [Exercise], - progress: u16, - selected: usize, + app_state: &'a mut AppState, table_state: TableState, + selected: usize, last_ind: usize, } -impl UiState { - pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { +impl<'a> UiState<'a> { + pub fn with_updated_rows(mut self) -> Self { + let current_exercise_ind = self.app_state.current_exercise_ind(); + let mut rows_counter: usize = 0; - let mut progress: u16 = 0; let rows = self - .exercises + .app_state + .exercises() .iter() - .zip(state_file.progress().iter().copied()) + .zip(self.app_state.progress().iter().copied()) .enumerate() .filter_map(|(ind, (exercise, done))| { let exercise_state = if done { - progress += 1; - if self.filter == Filter::Pending { return None; } @@ -55,7 +54,7 @@ impl UiState { rows_counter += 1; - let next = if ind == state_file.next_exercise_ind() { + let next = if ind == current_exercise_ind { ">>>>".bold().red() } else { Span::default() @@ -74,15 +73,14 @@ impl UiState { self.last_ind = rows_counter.saturating_sub(1); self.select(self.selected.min(self.last_ind)); - self.progress = progress; - self } - pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self { + pub fn new(app_state: &'a mut AppState) -> Self { let header = Row::new(["Next", "State", "Name", "Path"]); - let max_name_len = exercises + let max_name_len = app_state + .exercises() .iter() .map(|exercise| exercise.name.len()) .max() @@ -104,7 +102,7 @@ impl UiState { .highlight_symbol("πŸ¦€") .block(Block::default().borders(Borders::BOTTOM)); - let selected = state_file.next_exercise_ind(); + let selected = app_state.current_exercise_ind(); let table_state = TableState::default() .with_offset(selected.saturating_sub(10)) .with_selected(Some(selected)); @@ -113,19 +111,13 @@ impl UiState { table, message: String::with_capacity(128), filter: Filter::None, - exercises, - progress: 0, - selected, + app_state, table_state, + selected, last_ind: 0, }; - slf.with_updated_rows(state_file) - } - - #[inline] - pub fn selected(&self) -> usize { - self.selected + slf.with_updated_rows() } fn select(&mut self, ind: usize) { @@ -134,11 +126,13 @@ impl UiState { } pub fn select_next(&mut self) { - self.select(self.selected.saturating_add(1).min(self.last_ind)); + let next = (self.selected + 1).min(self.last_ind); + self.select(next); } pub fn select_previous(&mut self) { - self.select(self.selected.saturating_sub(1)); + let previous = self.selected.saturating_sub(1); + self.select(previous); } #[inline] @@ -167,8 +161,8 @@ impl UiState { frame.render_widget( Paragraph::new(progress_bar_ratatui( - self.progress, - self.exercises.len() as u16, + self.app_state.n_done(), + self.app_state.exercises().len() as u16, area.width, )?) .block(Block::default().borders(Borders::BOTTOM)), @@ -200,4 +194,19 @@ impl UiState { Ok(()) } + + pub fn reset_selected(&mut self) -> Result<&'static Exercise> { + self.app_state.set_pending(self.selected)?; + // TODO: Take care of filters! + let exercise = &self.app_state.exercises()[self.selected]; + exercise.reset()?; + + Ok(exercise) + } + + #[inline] + pub fn selected_to_current_exercise(&mut self) -> Result<()> { + // TODO: Take care of filters! + self.app_state.set_current_exercise_ind(self.selected) + } } diff --git a/src/main.rs b/src/main.rs index fc83e0fd03..926605c127 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use std::{path::Path, process::exit}; +mod app_state; mod consts; mod embedded; mod exercise; @@ -9,17 +10,15 @@ mod init; mod list; mod progress_bar; mod run; -mod state_file; -mod verify; mod watch; use self::{ + app_state::AppState, consts::WELCOME, - exercise::{Exercise, InfoFile}, + exercise::InfoFile, + init::init, list::list, run::run, - state_file::StateFile, - verify::{verify, VerifyState}, watch::{watch, WatchExit}, }; @@ -35,14 +34,12 @@ struct Args { enum Subcommands { /// Initialize Rustlings Init, - /// Verify all exercises according to the recommended order - Verify, /// Same as just running `rustlings` without a subcommand. Watch, - /// Run/Test a single exercise + /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified. Run { /// The name of the exercise - name: String, + name: Option, }, /// Reset a single exercise Reset { @@ -56,26 +53,6 @@ enum Subcommands { }, } -fn find_exercise(name: &str, exercises: &'static [Exercise]) -> Result<(usize, &'static Exercise)> { - if name == "next" { - for (ind, exercise) in exercises.iter().enumerate() { - if !exercise.looks_done()? { - return Ok((ind, exercise)); - } - } - - println!("πŸŽ‰ Congratulations! You have done all the exercises!"); - println!("πŸ”š There are no more exercises to do next!"); - exit(0); - } - - exercises - .iter() - .enumerate() - .find(|(_, exercise)| exercise.name == name) - .with_context(|| format!("No exercise found for '{name}'!")) -} - fn main() -> Result<()> { let args = Args::parse(); @@ -87,11 +64,10 @@ Try running `cargo --version` to diagnose the problem.", let mut info_file = InfoFile::parse()?; info_file.exercises.shrink_to_fit(); - // Leaking is not a problem since the exercises' slice is used until the end of the program. - let exercises = info_file.exercises.leak(); + let exercises = info_file.exercises; if matches!(args.command, Some(Subcommands::Init)) { - init::init(exercises).context("Initialization failed")?; + init(&exercises).context("Initialization failed")?; println!( "\nDone initialization!\n Run `cd rustlings` to go into the generated directory. @@ -109,38 +85,37 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let mut state_file = StateFile::read_or_default(exercises); + let mut app_state = AppState::new(exercises); match args.command { None | Some(Subcommands::Watch) => loop { - match watch(&mut state_file, exercises)? { + match watch(&mut app_state)? { WatchExit::Shutdown => break, // It is much easier to exit the watch mode, launch the list mode and then restart // the watch mode instead of trying to pause the watch threads and correct the // watch state. - WatchExit::List => list(&mut state_file, exercises)?, + WatchExit::List => list(&mut app_state)?, } }, // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { - let (_, exercise) = find_exercise(&name, exercises)?; - run(exercise).unwrap_or_else(|_| exit(1)); + if let Some(name) = name { + app_state.set_current_exercise_by_name(&name)?; + } + run(&mut app_state)?; } Some(Subcommands::Reset { name }) => { - let (ind, exercise) = find_exercise(&name, exercises)?; + app_state.set_current_exercise_by_name(&name)?; + app_state.set_pending(app_state.current_exercise_ind())?; + let exercise = app_state.current_exercise(); exercise.reset()?; - state_file.reset(ind)?; println!("The exercise {exercise} has been reset!"); } Some(Subcommands::Hint { name }) => { - let (_, exercise) = find_exercise(&name, exercises)?; - println!("{}", exercise.hint); + app_state.set_current_exercise_by_name(&name)?; + println!("{}", app_state.current_exercise().hint); } - Some(Subcommands::Verify) => match verify(exercises, 0)? { - VerifyState::AllExercisesDone => println!("All exercises done!"), - VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), - }, } Ok(()) diff --git a/src/run.rs b/src/run.rs index 2fd6f40719..18da193b76 100644 --- a/src/run.rs +++ b/src/run.rs @@ -2,13 +2,10 @@ use anyhow::{bail, Result}; use crossterm::style::Stylize; use std::io::{stdout, Write}; -use crate::exercise::Exercise; +use crate::app_state::{AppState, ExercisesProgress}; -// Invoke the rust compiler on the path of the given exercise, -// and run the ensuing binary. -// The verbose argument helps determine whether or not to show -// the output from the test harnesses (if the mode of the exercise is test) -pub fn run(exercise: &Exercise) -> Result<()> { +pub fn run(app_state: &mut AppState) -> Result<()> { + let exercise = app_state.current_exercise(); let output = exercise.run()?; { @@ -22,7 +19,19 @@ pub fn run(exercise: &Exercise) -> Result<()> { bail!("Ran {exercise} with errors"); } - println!("{}", "βœ“ Successfully ran {exercise}".green()); + println!( + "{}{}", + "βœ“ Successfully ran ".green(), + exercise.path.to_string_lossy().green(), + ); + + match app_state.done_current_exercise()? { + ExercisesProgress::AllDone => println!( + "πŸŽ‰ Congratulations! You have done all the exercises! +πŸ”š There are no more exercises to do next!" + ), + ExercisesProgress::Pending => println!("Next exercise: {}", app_state.current_exercise()), + } Ok(()) } diff --git a/src/state_file.rs b/src/state_file.rs deleted file mode 100644 index 6b80354e8f..0000000000 --- a/src/state_file.rs +++ /dev/null @@ -1,68 +0,0 @@ -use anyhow::{bail, Context, Result}; -use serde::{Deserialize, Serialize}; -use std::fs; - -use crate::exercise::Exercise; - -#[derive(Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct StateFile { - next_exercise_ind: usize, - progress: Vec, -} - -const BAD_INDEX_ERR: &str = "The next exercise index is higher than the number of exercises"; - -impl StateFile { - fn read(exercises: &[Exercise]) -> Option { - let file_content = fs::read(".rustlings-state.json").ok()?; - - let slf: Self = serde_json::de::from_slice(&file_content).ok()?; - - if slf.progress.len() != exercises.len() || slf.next_exercise_ind >= exercises.len() { - return None; - } - - Some(slf) - } - - pub fn read_or_default(exercises: &[Exercise]) -> Self { - Self::read(exercises).unwrap_or_else(|| Self { - next_exercise_ind: 0, - progress: vec![false; exercises.len()], - }) - } - - fn write(&self) -> Result<()> { - let mut buf = Vec::with_capacity(1024); - serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; - fs::write(".rustlings-state.json", buf) - .context("Failed to write the state file `.rustlings-state.json`")?; - - Ok(()) - } - - #[inline] - pub fn next_exercise_ind(&self) -> usize { - self.next_exercise_ind - } - - pub fn set_next_exercise_ind(&mut self, ind: usize) -> Result<()> { - if ind >= self.progress.len() { - bail!(BAD_INDEX_ERR); - } - self.next_exercise_ind = ind; - self.write() - } - - #[inline] - pub fn progress(&self) -> &[bool] { - &self.progress - } - - pub fn reset(&mut self, ind: usize) -> Result<()> { - let done = self.progress.get_mut(ind).context(BAD_INDEX_ERR)?; - *done = false; - self.write() - } -} diff --git a/src/verify.rs b/src/verify.rs deleted file mode 100644 index cea6bdf6f9..0000000000 --- a/src/verify.rs +++ /dev/null @@ -1,85 +0,0 @@ -use anyhow::Result; -use crossterm::style::{Attribute, ContentStyle, Stylize}; -use std::io::{stdout, Write}; - -use crate::exercise::{Exercise, Mode, State}; - -pub enum VerifyState { - AllExercisesDone, - Failed(&'static Exercise), -} - -// Verify that the provided container of Exercise objects -// can be compiled and run without any failures. -// Any such failures will be reported to the end user. -// If the Exercise being verified is a test, the verbose boolean -// determines whether or not the test harness outputs are displayed. -pub fn verify( - exercises: &'static [Exercise], - mut current_exercise_ind: usize, -) -> Result { - while current_exercise_ind < exercises.len() { - let exercise = &exercises[current_exercise_ind]; - - println!( - "Progress: {current_exercise_ind}/{} ({:.1}%)\n", - exercises.len(), - current_exercise_ind as f32 / exercises.len() as f32 * 100.0, - ); - - let output = exercise.run()?; - - { - let mut stdout = stdout().lock(); - stdout.write_all(&output.stdout)?; - stdout.write_all(&output.stderr)?; - stdout.flush()?; - } - - if !output.status.success() { - return Ok(VerifyState::Failed(exercise)); - } - - println!(); - // TODO: Color - match exercise.mode { - Mode::Compile => println!("Successfully ran {exercise}!"), - Mode::Test => println!("Successfully tested {exercise}!"), - Mode::Clippy => println!("Successfully checked {exercise}!"), - } - - if let State::Pending(context) = exercise.state()? { - println!( - "\nYou can keep working on this exercise, -or jump into the next one by removing the {} comment:\n", - "`I AM NOT DONE`".bold() - ); - - for context_line in context { - let formatted_line = if context_line.important { - format!("{}", context_line.line.bold()) - } else { - context_line.line - }; - - println!( - "{:>2} {} {}", - ContentStyle { - foreground_color: Some(crossterm::style::Color::Blue), - background_color: None, - underline_color: None, - attributes: Attribute::Bold.into() - } - .apply(context_line.number), - "|".blue(), - formatted_line, - ); - } - return Ok(VerifyState::Failed(exercise)); - } - - current_exercise_ind += 1; - } - - Ok(VerifyState::AllExercisesDone) -} diff --git a/src/watch.rs b/src/watch.rs index b29169b3e4..929275f1f6 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -15,7 +15,7 @@ mod debounce_event; mod state; mod terminal_event; -use crate::{exercise::Exercise, state_file::StateFile}; +use crate::app_state::AppState; use self::{ debounce_event::DebounceEventHandler, @@ -39,23 +39,23 @@ pub enum WatchExit { List, } -pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result { +pub fn watch(app_state: &mut AppState) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), DebounceEventHandler { tx: tx.clone(), - exercises, + exercises: app_state.exercises(), }, )?; debouncer .watcher() .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - let mut watch_state = WatchState::new(state_file, exercises); + let mut watch_state = WatchState::new(app_state); // TODO: bool - watch_state.run_exercise()?; + watch_state.run_current_exercise()?; watch_state.render()?; thread::spawn(move || terminal_event_handler(tx)); diff --git a/src/watch/state.rs b/src/watch/state.rs index 6f6d2f1076..a7647d8de2 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,26 +1,16 @@ -use anyhow::{Context, Result}; +use anyhow::Result; use crossterm::{ - style::{Attribute, ContentStyle, Stylize}, + style::Stylize, terminal::{size, Clear, ClearType}, ExecutableCommand, }; -use std::{ - fmt::Write as _, - io::{self, StdoutLock, Write}, -}; +use std::io::{self, StdoutLock, Write}; -use crate::{ - exercise::{Exercise, State}, - progress_bar::progress_bar, - state_file::StateFile, -}; +use crate::{app_state::AppState, progress_bar::progress_bar}; pub struct WatchState<'a> { writer: StdoutLock<'a>, - exercises: &'static [Exercise], - exercise: &'static Exercise, - current_exercise_ind: usize, - progress: u16, + app_state: &'a mut AppState, stdout: Option>, stderr: Option>, message: Option, @@ -28,19 +18,12 @@ pub struct WatchState<'a> { } impl<'a> WatchState<'a> { - pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self { - let current_exercise_ind = state_file.next_exercise_ind(); - let progress = state_file.progress().iter().filter(|done| **done).count() as u16; - let exercise = &exercises[current_exercise_ind]; - + pub fn new(app_state: &'a mut AppState) -> Self { let writer = io::stdout().lock(); Self { writer, - exercises, - exercise, - current_exercise_ind, - progress, + app_state, stdout: None, stderr: None, message: None, @@ -53,8 +36,8 @@ impl<'a> WatchState<'a> { self.writer } - pub fn run_exercise(&mut self) -> Result { - let output = self.exercise.run()?; + pub fn run_current_exercise(&mut self) -> Result { + let output = self.app_state.current_exercise().run()?; self.stdout = Some(output.stdout); if !output.status.success() { @@ -64,55 +47,15 @@ impl<'a> WatchState<'a> { self.stderr = None; - if let State::Pending(context) = self.exercise.state()? { - let mut message = format!( - " -You can keep working on this exercise or jump into the next one by removing the {} comment: - -", - "`I AM NOT DONE`".bold(), - ); - - for context_line in context { - let formatted_line = if context_line.important { - context_line.line.bold() - } else { - context_line.line.stylize() - }; - - writeln!( - message, - "{:>2} {} {}", - ContentStyle { - foreground_color: Some(crossterm::style::Color::Blue), - background_color: None, - underline_color: None, - attributes: Attribute::Bold.into() - } - .apply(context_line.number), - "|".blue(), - formatted_line, - )?; - } - - self.message = Some(message); - return Ok(false); - } - Ok(true) } pub fn run_exercise_with_ind(&mut self, exercise_ind: usize) -> Result { - self.exercise = self - .exercises - .get(exercise_ind) - .context("Invalid exercise index")?; - self.current_exercise_ind = exercise_ind; - - self.run_exercise() + self.app_state.set_current_exercise_ind(exercise_ind)?; + self.run_current_exercise() } - pub fn show_prompt(&mut self) -> io::Result<()> { + fn show_prompt(&mut self) -> io::Result<()> { self.writer.write_all(b"\n\n")?; if !self.hint_displayed { @@ -150,18 +93,27 @@ You can keep working on this exercise or jump into the next one by removing the if self.hint_displayed { self.writer .write_fmt(format_args!("\n{}\n", "Hint".bold().cyan().underlined()))?; - self.writer.write_all(self.exercise.hint.as_bytes())?; + self.writer + .write_all(self.app_state.current_exercise().hint.as_bytes())?; self.writer.write_all(b"\n\n")?; } let line_width = size()?.0; - let progress_bar = progress_bar(self.progress, self.exercises.len() as u16, line_width)?; + let progress_bar = progress_bar( + self.app_state.n_done(), + self.app_state.exercises().len() as u16, + line_width, + )?; self.writer.write_all(progress_bar.as_bytes())?; self.writer.write_all(b"Current exercise: ")?; self.writer.write_fmt(format_args!( "{}", - self.exercise.path.to_string_lossy().bold() + self.app_state + .current_exercise() + .path + .to_string_lossy() + .bold(), ))?; self.show_prompt()?; diff --git a/tests/fixture/state/exercises/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs index f579d0b4b4..016b827c89 100644 --- a/tests/fixture/state/exercises/pending_exercise.rs +++ b/tests/fixture/state/exercises/pending_exercise.rs @@ -1,7 +1,5 @@ // fake_exercise -// I AM NOT DONE - fn main() { } diff --git a/tests/fixture/state/exercises/pending_test_exercise.rs b/tests/fixture/state/exercises/pending_test_exercise.rs index 8756f02dbb..2002ef17dd 100644 --- a/tests/fixture/state/exercises/pending_test_exercise.rs +++ b/tests/fixture/state/exercises/pending_test_exercise.rs @@ -1,4 +1,2 @@ -// I AM NOT DONE - #[test] fn it_works() {} diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f8f4383fb1..51cdefb821 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,7 +1,6 @@ use assert_cmd::prelude::*; -use glob::glob; use predicates::boolean::PredicateBooleanExt; -use std::{fs::File, io::Read, process::Command}; +use std::process::Command; #[test] fn fails_when_in_wrong_dir() { @@ -137,31 +136,6 @@ fn get_hint_for_single_test() { .stdout("Hello!\n"); } -#[test] -fn all_exercises_require_confirmation() { - for exercise in glob("exercises/**/*.rs").unwrap() { - let path = exercise.unwrap(); - if path.file_name().unwrap() == "mod.rs" { - continue; - } - let source = { - let mut file = File::open(&path).unwrap(); - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - s - }; - source - .matches("// I AM NOT DONE") - .next() - .unwrap_or_else(|| { - panic!( - "There should be an `I AM NOT DONE` annotation in {:?}", - path - ) - }); - } -} - #[test] fn run_compile_exercise_does_not_prompt() { Command::cargo_bin("rustlings") From 65849629f5877a5d9f51accbb593d431938bd60c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:23 +0200 Subject: [PATCH 0680/1432] Remove glob --- Cargo.lock | 7 ------- Cargo.toml | 1 - 2 files changed, 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeb6c61ff5..a8ffb8ec49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,12 +320,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "hashbrown" version = "0.14.3" @@ -690,7 +684,6 @@ dependencies = [ "assert_cmd", "clap", "crossterm", - "glob", "notify-debouncer-mini", "predicates", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index 435dfd4957..83f01c250a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,6 @@ which = "6.0.1" [dev-dependencies] assert_cmd = "2.0.14" -glob = "0.3.0" predicates = "3.1.0" [profile.release] From c3933904f643238eaafe42e7da967c8262fef22a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:50 +0200 Subject: [PATCH 0681/1432] Update deps --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- rustlings-macros/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8ffb8ec49..554db288e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "assert_cmd" @@ -598,9 +598,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] diff --git a/Cargo.toml b/Cargo.toml index 83f01c250a..285e7df6e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" edition = "2021" [workspace.dependencies] -anyhow = "1.0.81" +anyhow = "1.0.82" serde = { version = "1.0.197", features = ["derive"] } toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 0114c8f0ac..79279f578b 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -9,4 +9,4 @@ edition.workspace = true proc-macro = true [dependencies] -quote = "1.0.35" +quote = "1.0.36" From 686143100fbb89e2a7ba4098134fe37bf0c69ad2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:55:58 +0200 Subject: [PATCH 0682/1432] Update intro1 --- exercises/00_intro/intro1.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index aa505a13ac..e4e0444af0 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -27,13 +27,6 @@ fn main() { println!("or logic error. The central concept behind Rustlings is to fix these errors and"); println!("solve the exercises. Good luck!"); println!(); - println!("The source for this exercise is in `exercises/00_intro/intro1.rs`. Have a look!"); - println!( - "Going forward, the source of the exercises will always be in the success/failure output." - ); - println!(); - println!( - "If you want to use rust-analyzer, Rust's LSP implementation, make sure your editor is set" - ); - println!("up, and then run `rustlings lsp` before continuing.") + println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!"); + println!("The current exercise path is shown under the progress bar in the watch mode."); } From 470dc65956dae034f17deefbc0b45490e1ec1448 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 14:35:30 +0200 Subject: [PATCH 0683/1432] Fix selected when there are no rows --- src/list.rs | 4 ++- src/list/state.rs | 75 +++++++++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/list.rs b/src/list.rs index 80b78e8de8..de120eaf64 100644 --- a/src/list.rs +++ b/src/list.rs @@ -72,7 +72,9 @@ pub fn list(app_state: &mut AppState) -> Result<()> { ui_state.message.push_str(message); } KeyCode::Char('r') => { - let exercise = ui_state.reset_selected()?; + let Some(exercise) = ui_state.reset_selected()? else { + continue; + }; ui_state = ui_state.with_updated_rows(); ui_state diff --git a/src/list/state.rs b/src/list/state.rs index 7714268c5a..3344fbbd4b 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -22,15 +22,14 @@ pub struct UiState<'a> { pub filter: Filter, app_state: &'a mut AppState, table_state: TableState, - selected: usize, - last_ind: usize, + n_rows: usize, } impl<'a> UiState<'a> { pub fn with_updated_rows(mut self) -> Self { let current_exercise_ind = self.app_state.current_exercise_ind(); - let mut rows_counter: usize = 0; + self.n_rows = 0; let rows = self .app_state .exercises() @@ -52,7 +51,7 @@ impl<'a> UiState<'a> { "PENDING".yellow() }; - rows_counter += 1; + self.n_rows += 1; let next = if ind == current_exercise_ind { ">>>>".bold().red() @@ -70,8 +69,15 @@ impl<'a> UiState<'a> { self.table = self.table.rows(rows); - self.last_ind = rows_counter.saturating_sub(1); - self.select(self.selected.min(self.last_ind)); + if self.n_rows == 0 { + self.table_state.select(None); + } else { + self.table_state.select(Some( + self.table_state + .selected() + .map_or(0, |selected| selected.min(self.n_rows - 1)), + )); + } self } @@ -107,42 +113,53 @@ impl<'a> UiState<'a> { .with_offset(selected.saturating_sub(10)) .with_selected(Some(selected)); + let filter = Filter::None; + let n_rows = app_state.exercises().len(); + let slf = Self { table, message: String::with_capacity(128), - filter: Filter::None, + filter, app_state, table_state, - selected, - last_ind: 0, + n_rows, }; slf.with_updated_rows() } - fn select(&mut self, ind: usize) { - self.selected = ind; - self.table_state.select(Some(ind)); - } - pub fn select_next(&mut self) { - let next = (self.selected + 1).min(self.last_ind); - self.select(next); + if self.n_rows > 0 { + let next = self + .table_state + .selected() + .map_or(0, |selected| (selected + 1).min(self.n_rows - 1)); + self.table_state.select(Some(next)); + } } pub fn select_previous(&mut self) { - let previous = self.selected.saturating_sub(1); - self.select(previous); + if self.n_rows > 0 { + let previous = self + .table_state + .selected() + .map_or(0, |selected| selected.saturating_sub(1)); + self.table_state.select(Some(previous)); + } } #[inline] pub fn select_first(&mut self) { - self.select(0); + if self.n_rows > 0 { + self.table_state.select(Some(0)); + } } #[inline] pub fn select_last(&mut self) { - self.select(self.last_ind); + if self.n_rows > 0 { + self.table_state.select(Some(self.n_rows - 1)); + } } pub fn draw(&mut self, frame: &mut Frame) -> Result<()> { @@ -195,18 +212,26 @@ impl<'a> UiState<'a> { Ok(()) } - pub fn reset_selected(&mut self) -> Result<&'static Exercise> { - self.app_state.set_pending(self.selected)?; + pub fn reset_selected(&mut self) -> Result> { + let Some(selected) = self.table_state.selected() else { + return Ok(None); + }; + + self.app_state.set_pending(selected)?; // TODO: Take care of filters! - let exercise = &self.app_state.exercises()[self.selected]; + let exercise = &self.app_state.exercises()[selected]; exercise.reset()?; - Ok(exercise) + Ok(Some(exercise)) } #[inline] pub fn selected_to_current_exercise(&mut self) -> Result<()> { + let Some(selected) = self.table_state.selected() else { + return Ok(()); + }; + // TODO: Take care of filters! - self.app_state.set_current_exercise_ind(self.selected) + self.app_state.set_current_exercise_ind(selected) } } From f53a0e870045ac0ff1bb4a3be7fe125680d477a5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 14:39:19 +0200 Subject: [PATCH 0684/1432] Panic if there are no exercises --- src/exercise.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index de435d1321..f01c6fccf2 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -31,12 +31,21 @@ impl InfoFile { pub fn parse() -> Result { // Read a local `info.toml` if it exists. // Mainly to let the tests work for now. - if let Ok(file_content) = fs::read_to_string("info.toml") { + let slf: Self = if let Ok(file_content) = fs::read_to_string("info.toml") { toml_edit::de::from_str(&file_content) } else { toml_edit::de::from_str(include_str!("../info.toml")) } - .context("Failed to parse `info.toml`") + .context("Failed to parse `info.toml`")?; + + if slf.exercises.is_empty() { + panic!( + "There are no exercises yet! +If you are developing third-party exercises, add at least one exercise before testing." + ); + } + + Ok(slf) } } From 2e1a87d7d3671c82932eb63b38ba383ce1fc7d53 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 14:58:56 +0200 Subject: [PATCH 0685/1432] Take care of filters when resolving the selected exercise --- src/list/state.rs | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 3344fbbd4b..0dcfe88a3b 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use ratatui::{ layout::{Constraint, Rect}, style::{Style, Stylize}, @@ -217,21 +217,44 @@ impl<'a> UiState<'a> { return Ok(None); }; - self.app_state.set_pending(selected)?; - // TODO: Take care of filters! - let exercise = &self.app_state.exercises()[selected]; + let (ind, exercise) = self + .app_state + .exercises() + .iter() + .zip(self.app_state.progress()) + .enumerate() + .filter_map(|(ind, (exercise, done))| match self.filter { + Filter::Done => done.then_some((ind, exercise)), + Filter::Pending => (!done).then_some((ind, exercise)), + Filter::None => Some((ind, exercise)), + }) + .nth(selected) + .context("Invalid selection index")?; + + self.app_state.set_pending(ind)?; exercise.reset()?; Ok(Some(exercise)) } - #[inline] pub fn selected_to_current_exercise(&mut self) -> Result<()> { let Some(selected) = self.table_state.selected() else { return Ok(()); }; - // TODO: Take care of filters! - self.app_state.set_current_exercise_ind(selected) + let ind = self + .app_state + .progress() + .iter() + .enumerate() + .filter_map(|(ind, done)| match self.filter { + Filter::Done => done.then_some(ind), + Filter::Pending => (!done).then_some(ind), + Filter::None => Some(ind), + }) + .nth(selected) + .context("Invalid selection index")?; + + self.app_state.set_current_exercise_ind(ind) } } From e79bc727f07bbe99092f30e66f4df845a2cd2ec5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 15:08:46 +0200 Subject: [PATCH 0686/1432] Don't listen on keys with modifiers --- src/list.rs | 16 +++++++++++----- src/watch/terminal_event.rs | 6 +++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/list.rs b/src/list.rs index de120eaf64..2430ed7361 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,6 +1,6 @@ use anyhow::Result; use crossterm::{ - event::{self, Event, KeyCode, KeyEventKind}, + event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, }; @@ -28,10 +28,16 @@ pub fn list(app_state: &mut AppState) -> Result<()> { let key = loop { match event::read()? { - Event::Key(key) => match key.kind { - KeyEventKind::Press | KeyEventKind::Repeat => break key, - KeyEventKind::Release => (), - }, + Event::Key(key) => { + if key.modifiers != KeyModifiers::NONE { + continue; + } + + match key.kind { + KeyEventKind::Press | KeyEventKind::Repeat => break key, + KeyEventKind::Release => (), + } + } // Redraw Event::Resize(_, _) => continue 'outer, // Ignore diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 7c85b5bdf7..faca8a2bc1 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -1,4 +1,4 @@ -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; use std::sync::mpsc::Sender; use super::WatchEvent; @@ -26,6 +26,10 @@ pub fn terminal_event_handler(tx: Sender) { match terminal_event { Event::Key(key) => { + if key.modifiers != KeyModifiers::NONE { + continue; + } + match key.kind { KeyEventKind::Release => continue, KeyEventKind::Press | KeyEventKind::Repeat => (), From 864cfa725be9dc78b1b962f13c8b6a0bc971d4c4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 15:10:15 +0200 Subject: [PATCH 0687/1432] Remove outdated tests --- tests/integration_tests.rs | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 51cdefb821..f81cc94b14 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -11,26 +11,6 @@ fn fails_when_in_wrong_dir() { .code(1); } -#[test] -fn verify_all_success() { - Command::cargo_bin("rustlings") - .unwrap() - .arg("verify") - .current_dir("tests/fixture/success") - .assert() - .success(); -} - -#[test] -fn verify_fails_if_some_fails() { - Command::cargo_bin("rustlings") - .unwrap() - .arg("verify") - .current_dir("tests/fixture/failure") - .assert() - .code(1); -} - #[test] fn run_single_compile_success() { Command::cargo_bin("rustlings") @@ -81,19 +61,6 @@ fn run_single_test_not_passed() { .code(1); } -#[test] -fn run_single_test_no_filename() { - Command::cargo_bin("rustlings") - .unwrap() - .arg("run") - .current_dir("tests/fixture/") - .assert() - .code(2) - .stderr(predicates::str::contains( - "required arguments were not provided", - )); -} - #[test] fn run_single_test_no_exercise() { Command::cargo_bin("rustlings") From 6494a8c50be2e3b8fbd9bb0ae50d8dfbf0569e2a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 16:54:27 +0200 Subject: [PATCH 0688/1432] Remove the watch subcommand --- src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 926605c127..7bc10ac826 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,8 +34,6 @@ struct Args { enum Subcommands { /// Initialize Rustlings Init, - /// Same as just running `rustlings` without a subcommand. - Watch, /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified. Run { /// The name of the exercise @@ -88,7 +86,7 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini let mut app_state = AppState::new(exercises); match args.command { - None | Some(Subcommands::Watch) => loop { + None => loop { match watch(&mut app_state)? { WatchExit::Shutdown => break, // It is much easier to exit the watch mode, launch the list mode and then restart From d8160f9113ea4f896c0843a40b9444a6e175826f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 00:56:40 +0200 Subject: [PATCH 0689/1432] Remove outdated installation methods --- .devcontainer/devcontainer.json | 8 ---- .gitignore | 4 -- .gitpod.yml | 7 --- .vscode/extensions.json | 5 --- README.md | 77 +------------------------------- flake.lock | 78 --------------------------------- flake.nix | 78 --------------------------------- shell.nix | 6 --- 8 files changed, 1 insertion(+), 262 deletions(-) delete mode 100644 .devcontainer/devcontainer.json delete mode 100644 .gitpod.yml delete mode 100644 .vscode/extensions.json delete mode 100644 flake.lock delete mode 100644 flake.nix delete mode 100644 shell.nix diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index f25e8bd8d6..0000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "image": "mcr.microsoft.com/devcontainers/rust:1", - "updateContentCommand": ["cargo", "build"], - "postAttachCommand": ["rustlings", "watch"], - "remoteEnv": { - "PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/target/debug" - } -} diff --git a/.gitignore b/.gitignore index 2d4a04dc67..c9172e01a2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,5 @@ public/ .idea *.iml -# VS Code extension recommendations -.vscode/* -!.vscode/extensions.json - # Ignore file for editors like Helix .ignore diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 0691933560..0000000000 --- a/.gitpod.yml +++ /dev/null @@ -1,7 +0,0 @@ -tasks: - - init: /workspace/rustlings/install.sh - command: /workspace/.cargo/bin/rustlings watch - -vscode: - extensions: - - rust-lang.rust-analyzer@0.3.1348 diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index b85de74971..0000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "recommendations": [ - "rust-lang.rust-analyzer" - ] -} diff --git a/README.md b/README.md index fd76fdff88..96421ebdf4 100644 --- a/README.md +++ b/README.md @@ -18,78 +18,7 @@ _Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt instal You will need to have Rust installed. You can get it by visiting . This'll also install Cargo, Rust's package/project manager. -## MacOS/Linux - -Just run: - -```bash -curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -``` - -Or if you want it to be installed to a different path: - -```bash -curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -s mypath/ -``` - -This will install Rustlings and give you access to the `rustlings` command. Run it to get started! - -### Nix - -Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. - -```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.1) -git clone -b 5.6.1 --depth 1 https://github.com/rust-lang/rustlings -cd rustlings -# if nix version > 2.3 -nix develop -# if nix version <= 2.3 -nix-shell -``` - -## Windows - -In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`: - -```ps1 -Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -``` - -Then, you can run: - -```ps1 -Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 -``` - -To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. Keep in mind that this works best in PowerShell, and any other terminals may give you errors. - -If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus. - -## Browser - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) - -[![Open Rustlings On Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/?repo=rust-lang%2Frustlings&ref=main) - -## Manually - -Basically: Clone the repository at the latest tag, run `cargo install --path .`. - -```bash -# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.1) -git clone -b 5.6.1 --depth 1 https://github.com/rust-lang/rustlings -cd rustlings -cargo install --force --path . -``` - -If there are installation errors, ensure that your toolchain is up to date. For the latest, run: - -```bash -rustup update -``` - -Then, same as above, run `rustlings` to get started. + ## Doing exercises @@ -138,10 +67,6 @@ rustlings list After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. These quizzes are found in `exercises/quizN.rs`. -## Enabling `rust-analyzer` - -Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise. - ## Continuing On Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 15238981d8..0000000000 --- a/flake.lock +++ /dev/null @@ -1,78 +0,0 @@ -{ - "nodes": { - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1692799911, - "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1694183432, - "narHash": "sha256-YyPGNapgZNNj51ylQMw9lAgvxtM2ai1HZVUu3GS8Fng=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "db9208ab987cdeeedf78ad9b4cf3c55f5ebd269b", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-compat": "flake-compat", - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 152d38e61a..0000000000 --- a/flake.nix +++ /dev/null @@ -1,78 +0,0 @@ -{ - description = "Small exercises to get you used to reading and writing Rust code"; - - inputs = { - flake-compat = { - url = "github:edolstra/flake-compat"; - flake = false; - }; - flake-utils.url = "github:numtide/flake-utils"; - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - }; - - outputs = { self, flake-utils, nixpkgs, ... }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacyPackages.${system}; - - cargoBuildInputs = with pkgs; lib.optionals stdenv.isDarwin [ - darwin.apple_sdk.frameworks.CoreServices - ]; - - rustlings = - pkgs.rustPlatform.buildRustPackage { - name = "rustlings"; - version = "5.6.1"; - - buildInputs = cargoBuildInputs; - nativeBuildInputs = [pkgs.git]; - - src = with pkgs.lib; cleanSourceWith { - src = self; - # a function that returns a bool determining if the path should be included in the cleaned source - filter = path: type: - let - # filename - baseName = builtins.baseNameOf (toString path); - # path from root directory - path' = builtins.replaceStrings [ "${self}/" ] [ "" ] path; - # checks if path is in the directory - inDirectory = directory: hasPrefix directory path'; - in - inDirectory "src" || - inDirectory "tests" || - hasPrefix "Cargo" baseName || - baseName == "info.toml"; - }; - - cargoLock.lockFile = ./Cargo.lock; - }; - in - { - devShell = pkgs.mkShell { - RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; - - buildInputs = with pkgs; [ - cargo - rustc - rust-analyzer - rustlings - rustfmt - clippy - ] ++ cargoBuildInputs; - }; - apps = let - rustlings-app = { - type = "app"; - program = "${rustlings}/bin/rustlings"; - }; - in { - default = rustlings-app; - rustlings = rustlings-app; - }; - packages = { - inherit rustlings; - default = rustlings; - }; - }); -} diff --git a/shell.nix b/shell.nix deleted file mode 100644 index fa2a56c769..0000000000 --- a/shell.nix +++ /dev/null @@ -1,6 +0,0 @@ -(import (let lock = builtins.fromJSON (builtins.readFile ./flake.lock); -in fetchTarball { - url = - "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; - sha256 = lock.nodes.flake-compat.locked.narHash; -}) { src = ./.; }).shellNix From 1e3745ccdf5ca41ae47d4f4d8594e8070df200a5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 00:58:26 +0200 Subject: [PATCH 0690/1432] Update winnow --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 554db288e3..a5ad8c9d25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1130,9 +1130,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] From 2a95a3e96644a0f769019204a518816c9f2e2aee Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 01:24:01 +0200 Subject: [PATCH 0691/1432] Deal with long strings --- info.toml | 32 ++++++++++++++++++++++++ src/consts.rs | 59 -------------------------------------------- src/exercise.rs | 12 ++++++--- src/init.rs | 40 ++++++++++++++++-------------- src/main.rs | 65 ++++++++++++++++++++++++++++++++++--------------- src/watch.rs | 10 +++++--- 6 files changed, 114 insertions(+), 104 deletions(-) delete mode 100644 src/consts.rs diff --git a/info.toml b/info.toml index c085e89c7b..d35b570252 100644 --- a/info.toml +++ b/info.toml @@ -1,3 +1,35 @@ +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: + +1. The central concept behind Rustlings is that you solve exercises. These + exercises usually have some sort of syntax error in them, which will cause + them to fail compilation or testing. Sometimes there's a logic error instead + of a syntax error. No matter what error, it's your job to find it and fix it! + You'll know when you fixed it because then, the exercise will compile and + Rustlings will be able to move on to the next exercise. +2. If you run Rustlings in watch mode (which we recommend), it'll automatically + start with the first exercise. Don't get confused by an error message popping + up as soon as you run Rustlings! This is part of the exercise that you're + supposed to solve, so open the exercise file in an editor and start your + detective work! +3. If you're stuck on an exercise, there is a helpful hint you can view by typing + 'hint' (in watch mode), or running `rustlings hint exercise_name`. +4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! + (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, + and sometimes, other learners do too so you can help each other out! + +Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. +Make sure to have your editor open in the `rustlings` directory!""" + +final_message = """We hope you enjoyed learning about the various aspects of Rust! +If you noticed any issues, please don't hesitate to report them to our repo. +You can also contribute your own exercises to help the greater community! + +Before reporting an issue or contributing, please read our guidelines: +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md +""" + # INTRO [[exercises]] diff --git a/src/consts.rs b/src/consts.rs deleted file mode 100644 index 40bf150fd7..0000000000 --- a/src/consts.rs +++ /dev/null @@ -1,59 +0,0 @@ -pub const WELCOME: &str = r" welcome to... - _ _ _ - _ __ _ _ ___| |_| (_)_ __ __ _ ___ - | '__| | | / __| __| | | '_ \ / _` / __| - | | | |_| \__ \ |_| | | | | | (_| \__ \ - |_| \__,_|___/\__|_|_|_| |_|\__, |___/ - |___/"; - -pub const DEFAULT_OUT: &str = - "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: - -1. The central concept behind Rustlings is that you solve exercises. These - exercises usually have some sort of syntax error in them, which will cause - them to fail compilation or testing. Sometimes there's a logic error instead - of a syntax error. No matter what error, it's your job to find it and fix it! - You'll know when you fixed it because then, the exercise will compile and - Rustlings will be able to move on to the next exercise. -2. If you run Rustlings in watch mode (which we recommend), it'll automatically - start with the first exercise. Don't get confused by an error message popping - up as soon as you run Rustlings! This is part of the exercise that you're - supposed to solve, so open the exercise file in an editor and start your - detective work! -3. If you're stuck on an exercise, there is a helpful hint you can view by typing - 'hint' (in watch mode), or running `rustlings hint exercise_name`. -4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, - and sometimes, other learners do too so you can help each other out! - -Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. -Make sure to have your editor open in the `rustlings` directory!"; - -pub const FENISH_LINE: &str = "+----------------------------------------------------+ -| You made it to the Fe-nish line! | -+-------------------------- ------------------------+ - \\/\x1b[31m - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ - β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ - β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ - β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ - β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m - -We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, please don't hesitate to report them to our repo. -You can also contribute your own exercises to help the greater community! - -Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; diff --git a/src/exercise.rs b/src/exercise.rs index f01c6fccf2..d28f4dbe8d 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -24,6 +24,10 @@ pub enum Mode { #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct InfoFile { + // TODO + pub welcome_message: Option, + // TODO + pub final_message: Option, pub exercises: Vec, } @@ -39,10 +43,7 @@ impl InfoFile { .context("Failed to parse `info.toml`")?; if slf.exercises.is_empty() { - panic!( - "There are no exercises yet! -If you are developing third-party exercises, add at least one exercise before testing." - ); + panic!("{NO_EXERCISES_ERR}"); } Ok(slf) @@ -119,3 +120,6 @@ impl Display for Exercise { self.path.fmt(f) } } + +const NO_EXERCISES_ERR: &str = "There are no exercises yet! +If you are developing third-party exercises, add at least one exercise before testing."; diff --git a/src/init.rs b/src/init.rs index bc561eaffc..4474743851 100644 --- a/src/init.rs +++ b/src/init.rs @@ -36,47 +36,33 @@ publish = false } fn create_gitignore() -> io::Result<()> { - let gitignore = b"/target -/.rustlings-state.json"; OpenOptions::new() .create_new(true) .write(true) .open(".gitignore")? - .write_all(gitignore) + .write_all(GITIGNORE) } fn create_vscode_dir() -> Result<()> { create_dir(".vscode").context("Failed to create the directory `.vscode`")?; - let vs_code_extensions_json = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; OpenOptions::new() .create_new(true) .write(true) .open(".vscode/extensions.json")? - .write_all(vs_code_extensions_json)?; + .write_all(VS_CODE_EXTENSIONS_JSON)?; Ok(()) } pub fn init(exercises: &[Exercise]) -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { - bail!( - "A directory with the name `exercises` and a file with the name `Cargo.toml` already exist -in the current directory. It looks like Rustlings was already initialized here. -Run `rustlings` for instructions on getting started with the exercises. - -If you didn't already initialize Rustlings, please initialize it in another directory." - ); + bail!(PROBABLY_IN_RUSTLINGS_DIR_ERR); } let rustlings_path = Path::new("rustlings"); if let Err(e) = create_dir(rustlings_path) { if e.kind() == ErrorKind::AlreadyExists { - bail!( - "A directory with the name `rustlings` already exists in the current directory. -You probably already initialized Rustlings. -Run `cd rustlings` -Then run `rustlings` again" - ); + bail!(RUSTLINGS_DIR_ALREADY_EXISTS_ERR); } return Err(e.into()); } @@ -96,3 +82,21 @@ Then run `rustlings` again" Ok(()) } + +const GITIGNORE: &[u8] = b"/target +/.rustlings-state.json"; + +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 +in the current directory. It looks like Rustlings was already initialized here. +Run `rustlings` for instructions on getting started with the exercises. + +If you didn't already initialize Rustlings, please initialize it in another directory."; + +const RUSTLINGS_DIR_ALREADY_EXISTS_ERR: &str = + "A directory with the name `rustlings` already exists in the current directory. +You probably already initialized Rustlings. +Run `cd rustlings` +Then run `rustlings` again"; diff --git a/src/main.rs b/src/main.rs index 7bc10ac826..fdbb710c43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ use clap::{Parser, Subcommand}; use std::{path::Path, process::exit}; mod app_state; -mod consts; mod embedded; mod exercise; mod init; @@ -14,7 +13,6 @@ mod watch; use self::{ app_state::AppState, - consts::WELCOME, exercise::InfoFile, init::init, list::list, @@ -54,11 +52,7 @@ enum Subcommands { fn main() -> Result<()> { let args = Args::parse(); - which::which("cargo").context( - "Failed to find `cargo`. -Did you already install Rust? -Try running `cargo --version` to diagnose the problem.", - )?; + which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; let mut info_file = InfoFile::parse()?; info_file.exercises.shrink_to_fit(); @@ -66,20 +60,11 @@ Try running `cargo --version` to diagnose the problem.", if matches!(args.command, Some(Subcommands::Init)) { init(&exercises).context("Initialization failed")?; - println!( - "\nDone initialization!\n -Run `cd rustlings` to go into the generated directory. -Then run `rustlings` for further instructions on getting started." - ); + + println!("{POST_INIT_MSG}"); return Ok(()); } else if !Path::new("exercises").is_dir() { - println!( - " -{WELCOME} - -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." - ); + println!("{PRE_INIT_MSG}"); exit(1); } @@ -118,3 +103,45 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini Ok(()) } + +const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`. +Did you already install Rust? +Try running `cargo --version` to diagnose the problem."; + +const PRE_INIT_MSG: &str = r" + welcome to... + _ _ _ + _ __ _ _ ___| |_| (_)_ __ __ _ ___ + | '__| | | / __| __| | | '_ \ / _` / __| + | | | |_| \__ \ |_| | | | | | (_| \__ \ + |_| \__,_|___/\__|_|_|_| |_|\__, |___/ + |___/ + +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` for further instructions on getting 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 929275f1f6..bfa0f88a1e 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -89,10 +89,12 @@ pub fn watch(app_state: &mut AppState) -> Result { } } - watch_state.into_writer().write_all(b" -We hope you're enjoying learning Rust! -If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. -")?; + watch_state.into_writer().write_all(QUIT_MSG)?; Ok(WatchExit::Shutdown) } + +const QUIT_MSG: &[u8] = b" +We hope you're enjoying learning Rust! +If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. +"; From 6807e63c5f26ee01b60460355ce2c5411c603f16 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 02:45:54 +0200 Subject: [PATCH 0692/1432] Show done message --- src/watch.rs | 4 ---- src/watch/state.rs | 52 +++++++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index bfa0f88a1e..928fc5fb61 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -54,9 +54,7 @@ pub fn watch(app_state: &mut AppState) -> Result { let mut watch_state = WatchState::new(app_state); - // TODO: bool watch_state.run_current_exercise()?; - watch_state.render()?; thread::spawn(move || terminal_event_handler(tx)); @@ -76,9 +74,7 @@ pub fn watch(app_state: &mut AppState) -> Result { watch_state.handle_invalid_cmd(&cmd)?; } WatchEvent::FileChange { exercise_ind } => { - // TODO: bool watch_state.run_exercise_with_ind(exercise_ind)?; - watch_state.render()?; } WatchEvent::NotifyErr(e) => { return Err(Error::from(e).context("Exercise file watcher failed")) diff --git a/src/watch/state.rs b/src/watch/state.rs index a7647d8de2..5a5c0ca1c0 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -13,8 +13,8 @@ pub struct WatchState<'a> { app_state: &'a mut AppState, stdout: Option>, stderr: Option>, - message: Option, - hint_displayed: bool, + show_hint: bool, + show_done: bool, } impl<'a> WatchState<'a> { @@ -26,8 +26,8 @@ impl<'a> WatchState<'a> { app_state, stdout: None, stderr: None, - message: None, - hint_displayed: false, + show_hint: false, + show_done: false, } } @@ -36,29 +36,32 @@ impl<'a> WatchState<'a> { self.writer } - pub fn run_current_exercise(&mut self) -> Result { + pub fn run_current_exercise(&mut self) -> Result<()> { + self.show_hint = false; + let output = self.app_state.current_exercise().run()?; self.stdout = Some(output.stdout); - if !output.status.success() { + if output.status.success() { + self.stderr = None; + self.show_done = true; + } else { self.stderr = Some(output.stderr); - return Ok(false); + self.show_done = false; } - self.stderr = None; - - Ok(true) + self.render() } - pub fn run_exercise_with_ind(&mut self, exercise_ind: usize) -> Result { + pub fn run_exercise_with_ind(&mut self, exercise_ind: usize) -> Result<()> { self.app_state.set_current_exercise_ind(exercise_ind)?; self.run_current_exercise() } fn show_prompt(&mut self) -> io::Result<()> { - self.writer.write_all(b"\n\n")?; + self.writer.write_all(b"\n")?; - if !self.hint_displayed { + if !self.show_hint { self.writer.write_fmt(format_args!("{}int/", 'h'.bold()))?; } @@ -84,20 +87,26 @@ impl<'a> WatchState<'a> { self.writer.write_all(b"\n")?; } - if let Some(message) = &self.message { - self.writer.write_all(message.as_bytes())?; - } - self.writer.write_all(b"\n")?; - if self.hint_displayed { + if self.show_hint { self.writer - .write_fmt(format_args!("\n{}\n", "Hint".bold().cyan().underlined()))?; + .write_fmt(format_args!("{}\n", "Hint".bold().cyan().underlined()))?; self.writer .write_all(self.app_state.current_exercise().hint.as_bytes())?; self.writer.write_all(b"\n\n")?; } + if self.show_done { + self.writer.write_fmt(format_args!( + "{}\n\n", + "Exercise done βœ“ +When you are done experimenting, enter `n` or `next` to go to the next exercise πŸ¦€" + .bold() + .green(), + ))?; + } + let line_width = size()?.0; let progress_bar = progress_bar( self.app_state.n_done(), @@ -108,7 +117,7 @@ impl<'a> WatchState<'a> { self.writer.write_all(b"Current exercise: ")?; self.writer.write_fmt(format_args!( - "{}", + "{}\n", self.app_state .current_exercise() .path @@ -122,7 +131,7 @@ impl<'a> WatchState<'a> { } pub fn show_hint(&mut self) -> Result<()> { - self.hint_displayed = true; + self.show_hint = true; self.render() } @@ -133,6 +142,7 @@ impl<'a> WatchState<'a> { self.writer .write_all(b" (confusing input can occur after resizing the terminal)")?; } + self.writer.write_all(b"\n")?; self.show_prompt() } } From 98c5088a39439389a4e198839b47819bfa1b1712 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 14:52:50 +0200 Subject: [PATCH 0693/1432] Update deps --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5ad8c9d25..6c64661471 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anstream" From a534de0312ff47d5e87b3bf60d508bdaafb98fbc Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 15:27:29 +0200 Subject: [PATCH 0694/1432] Implement going to the next exercise --- src/watch.rs | 11 +++++++---- src/watch/state.rs | 23 ++++++++++++++++++++++- src/watch/terminal_event.rs | 2 ++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 928fc5fb61..357b5c7159 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -26,9 +26,9 @@ use self::{ enum WatchEvent { Input(InputEvent), FileChange { exercise_ind: usize }, + TerminalResize, NotifyErr(notify::Error), TerminalEventErr(io::Error), - TerminalResize, } /// Returned by the watch mode to indicate what to do afterwards. @@ -60,15 +60,15 @@ pub fn watch(app_state: &mut AppState) -> Result { while let Ok(event) = rx.recv() { match event { + WatchEvent::Input(InputEvent::Next) => { + watch_state.next_exercise()?; + } WatchEvent::Input(InputEvent::Hint) => { watch_state.show_hint()?; } WatchEvent::Input(InputEvent::List) => { return Ok(WatchExit::List); } - WatchEvent::TerminalResize => { - watch_state.render()?; - } WatchEvent::Input(InputEvent::Quit) => break, WatchEvent::Input(InputEvent::Unrecognized(cmd)) => { watch_state.handle_invalid_cmd(&cmd)?; @@ -76,6 +76,9 @@ pub fn watch(app_state: &mut AppState) -> Result { WatchEvent::FileChange { exercise_ind } => { watch_state.run_exercise_with_ind(exercise_ind)?; } + WatchEvent::TerminalResize => { + watch_state.render()?; + } WatchEvent::NotifyErr(e) => { return Err(Error::from(e).context("Exercise file watcher failed")) } diff --git a/src/watch/state.rs b/src/watch/state.rs index 5a5c0ca1c0..462633d1a9 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -6,7 +6,10 @@ use crossterm::{ }; use std::io::{self, StdoutLock, Write}; -use crate::{app_state::AppState, progress_bar::progress_bar}; +use crate::{ + app_state::{AppState, ExercisesProgress}, + progress_bar::progress_bar, +}; pub struct WatchState<'a> { writer: StdoutLock<'a>, @@ -58,9 +61,27 @@ impl<'a> WatchState<'a> { self.run_current_exercise() } + pub fn next_exercise(&mut self) -> Result<()> { + if !self.show_done { + self.writer + .write_all(b"The current exercise isn't done yet\n")?; + self.show_prompt()?; + return Ok(()); + } + + match self.app_state.done_current_exercise()? { + ExercisesProgress::AllDone => todo!(), + ExercisesProgress::Pending => self.run_current_exercise(), + } + } + fn show_prompt(&mut self) -> io::Result<()> { self.writer.write_all(b"\n")?; + if self.show_done { + self.writer.write_fmt(format_args!("{}ext/", 'n'.bold()))?; + } + if !self.show_hint { self.writer.write_fmt(format_args!("{}int/", 'h'.bold()))?; } diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index faca8a2bc1..7f7ebe06f3 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -4,6 +4,7 @@ use std::sync::mpsc::Sender; use super::WatchEvent; pub enum InputEvent { + Next, Hint, List, Quit, @@ -38,6 +39,7 @@ pub fn terminal_event_handler(tx: Sender) { match key.code { KeyCode::Enter => { let input_event = match input.trim() { + "n" | "next" => InputEvent::Next, "h" | "hint" => InputEvent::Hint, "l" | "list" => break InputEvent::List, "q" | "quit" => break InputEvent::Quit, From d5a6dee1b329f68d00bee61c6b6c7a0adbf8bab5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 18:57:04 +0200 Subject: [PATCH 0695/1432] Handle the case when all exercises are done --- src/app_state.rs | 52 +++++++++++++++++++++++++++++++++++++++++----- src/run.rs | 24 +++++++++------------ src/watch.rs | 17 ++++++++------- src/watch/state.rs | 34 +++++++++++++++--------------- 4 files changed, 84 insertions(+), 43 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 4a0912e4ed..b1440e8ae8 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,8 +1,16 @@ use anyhow::{bail, Context, Result}; +use crossterm::{ + style::Stylize, + terminal::{Clear, ClearType}, + ExecutableCommand, +}; use serde::{Deserialize, Serialize}; -use std::fs; +use std::{ + fs, + io::{StdoutLock, Write}, +}; -use crate::exercise::Exercise; +use crate::{exercise::Exercise, FENISH_LINE}; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -143,7 +151,7 @@ impl AppState { Ok(()) } - fn next_exercise_ind(&self) -> Option { + fn next_pending_exercise_ind(&self) -> Option { let current_ind = self.state_file.current_exercise_ind; if current_ind == self.state_file.progress.len() - 1 { @@ -167,14 +175,41 @@ impl AppState { } } - pub fn done_current_exercise(&mut self) -> Result { + pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { let done = &mut self.state_file.progress[self.state_file.current_exercise_ind]; if !*done { *done = true; self.n_done += 1; } - let Some(ind) = self.next_exercise_ind() else { + let Some(ind) = self.next_pending_exercise_ind() else { + writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; + + for (exercise_ind, exercise) in self.exercises().iter().enumerate() { + writer.write_fmt(format_args!("Running {exercise} ... "))?; + writer.flush()?; + + if !exercise.run()?.status.success() { + self.state_file.current_exercise_ind = exercise_ind; + self.current_exercise = exercise; + + // No check if the exercise is done before setting it to pending + // because no pending exercise was found. + self.state_file.progress[exercise_ind] = false; + self.n_done -= 1; + + self.state_file.write()?; + + return Ok(ExercisesProgress::Pending); + } + + writer.write_fmt(format_args!("{}\n", "ok".green()))?; + } + + writer.execute(Clear(ClearType::All))?; + writer.write_all(FENISH_LINE.as_bytes())?; + // TODO: Show final message. + return Ok(ExercisesProgress::AllDone); }; @@ -183,3 +218,10 @@ impl AppState { Ok(ExercisesProgress::Pending) } } + +const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" +All exercises seem to be done. +Recompiling and running all exercises to make sure that all of them are actually done. +This might take some minutes. + +"; diff --git a/src/run.rs b/src/run.rs index 18da193b76..ea790e9aed 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Result}; use crossterm::style::Stylize; -use std::io::{stdout, Write}; +use std::io::{self, Write}; use crate::app_state::{AppState, ExercisesProgress}; @@ -8,28 +8,24 @@ pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); let output = exercise.run()?; - { - let mut stdout = stdout().lock(); - stdout.write_all(&output.stdout)?; - stdout.write_all(&output.stderr)?; - stdout.flush()?; - } + let mut stdout = io::stdout().lock(); + stdout.write_all(&output.stdout)?; + stdout.write_all(b"\n")?; + stdout.write_all(&output.stderr)?; + stdout.flush()?; if !output.status.success() { bail!("Ran {exercise} with errors"); } - println!( + stdout.write_fmt(format_args!( "{}{}", "βœ“ Successfully ran ".green(), exercise.path.to_string_lossy().green(), - ); + ))?; - match app_state.done_current_exercise()? { - ExercisesProgress::AllDone => println!( - "πŸŽ‰ Congratulations! You have done all the exercises! -πŸ”š There are no more exercises to do next!" - ), + match app_state.done_current_exercise(&mut stdout)? { + ExercisesProgress::AllDone => (), ExercisesProgress::Pending => println!("Next exercise: {}", app_state.current_exercise()), } diff --git a/src/watch.rs b/src/watch.rs index 357b5c7159..beb69b3d9d 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -15,7 +15,7 @@ mod debounce_event; mod state; mod terminal_event; -use crate::app_state::AppState; +use crate::app_state::{AppState, ExercisesProgress}; use self::{ debounce_event::DebounceEventHandler, @@ -32,6 +32,7 @@ enum WatchEvent { } /// Returned by the watch mode to indicate what to do afterwards. +#[must_use] pub enum WatchExit { /// Exit the program. Shutdown, @@ -60,16 +61,20 @@ pub fn watch(app_state: &mut AppState) -> Result { while let Ok(event) = rx.recv() { match event { - WatchEvent::Input(InputEvent::Next) => { - watch_state.next_exercise()?; - } + WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise()? { + ExercisesProgress::AllDone => break, + ExercisesProgress::Pending => watch_state.run_current_exercise()?, + }, WatchEvent::Input(InputEvent::Hint) => { watch_state.show_hint()?; } WatchEvent::Input(InputEvent::List) => { return Ok(WatchExit::List); } - WatchEvent::Input(InputEvent::Quit) => break, + WatchEvent::Input(InputEvent::Quit) => { + watch_state.into_writer().write_all(QUIT_MSG)?; + break; + } WatchEvent::Input(InputEvent::Unrecognized(cmd)) => { watch_state.handle_invalid_cmd(&cmd)?; } @@ -88,8 +93,6 @@ pub fn watch(app_state: &mut AppState) -> Result { } } - watch_state.into_writer().write_all(QUIT_MSG)?; - Ok(WatchExit::Shutdown) } diff --git a/src/watch/state.rs b/src/watch/state.rs index 462633d1a9..70b6ae484d 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -4,7 +4,10 @@ use crossterm::{ terminal::{size, Clear, ClearType}, ExecutableCommand, }; -use std::io::{self, StdoutLock, Write}; +use std::{ + io::{self, StdoutLock, Write}, + process::Output, +}; use crate::{ app_state::{AppState, ExercisesProgress}, @@ -49,6 +52,9 @@ impl<'a> WatchState<'a> { self.stderr = None; self.show_done = true; } else { + self.app_state + .set_pending(self.app_state.current_exercise_ind())?; + self.stderr = Some(output.stderr); self.show_done = false; } @@ -61,18 +67,15 @@ impl<'a> WatchState<'a> { self.run_current_exercise() } - pub fn next_exercise(&mut self) -> Result<()> { + pub fn next_exercise(&mut self) -> Result { if !self.show_done { self.writer .write_all(b"The current exercise isn't done yet\n")?; self.show_prompt()?; - return Ok(()); + return Ok(ExercisesProgress::Pending); } - match self.app_state.done_current_exercise()? { - ExercisesProgress::AllDone => todo!(), - ExercisesProgress::Pending => self.run_current_exercise(), - } + self.app_state.done_current_exercise(&mut self.writer) } fn show_prompt(&mut self) -> io::Result<()> { @@ -93,7 +96,7 @@ impl<'a> WatchState<'a> { } pub fn render(&mut self) -> Result<()> { - // Prevent having the first line shifted after clearing because of the prompt. + // Prevent having the first line shifted. self.writer.write_all(b"\n")?; self.writer.execute(Clear(ClearType::All))?; @@ -111,11 +114,11 @@ impl<'a> WatchState<'a> { self.writer.write_all(b"\n")?; if self.show_hint { - self.writer - .write_fmt(format_args!("{}\n", "Hint".bold().cyan().underlined()))?; - self.writer - .write_all(self.app_state.current_exercise().hint.as_bytes())?; - self.writer.write_all(b"\n\n")?; + self.writer.write_fmt(format_args!( + "{}\n{}\n\n", + "Hint".bold().cyan().underlined(), + self.app_state.current_exercise().hint, + ))?; } if self.show_done { @@ -134,11 +137,8 @@ When you are done experimenting, enter `n` or `next` to go to the next exercise self.app_state.exercises().len() as u16, line_width, )?; - self.writer.write_all(progress_bar.as_bytes())?; - - self.writer.write_all(b"Current exercise: ")?; self.writer.write_fmt(format_args!( - "{}\n", + "{progress_bar}Current exercise: {}\n", self.app_state .current_exercise() .path From 8bd03093eb314f799d7daafbd3f7dcea9a5ef148 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 18:57:39 +0200 Subject: [PATCH 0696/1432] Add newline at the end of the generated .gitignore --- src/init.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index 4474743851..093610ab07 100644 --- a/src/init.rs +++ b/src/init.rs @@ -84,7 +84,8 @@ pub fn init(exercises: &[Exercise]) -> Result<()> { } const GITIGNORE: &[u8] = b"/target -/.rustlings-state.json"; +/.rustlings-state.json +"; const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; From 44824718b2155268c79d1ce216abc770df94d05d Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 18:58:01 +0200 Subject: [PATCH 0697/1432] Remove unused import --- src/watch/state.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index 70b6ae484d..6a97637b9e 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -4,10 +4,7 @@ use crossterm::{ terminal::{size, Clear, ClearType}, ExecutableCommand, }; -use std::{ - io::{self, StdoutLock, Write}, - process::Output, -}; +use std::io::{self, StdoutLock, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, From 9b0eeb815acd550d733a722c0563bfb703bb8513 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 19:07:17 +0200 Subject: [PATCH 0698/1432] Fix Display for Exercise --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index d28f4dbe8d..a9dcce34fb 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -117,7 +117,7 @@ impl Exercise { impl Display for Exercise { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.path.fmt(f) + Display::fmt(&self.path.display(), f) } } From 279ebdc1534d70d838110c16e46dce848a9de956 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 19:16:52 +0200 Subject: [PATCH 0699/1432] Remove the modifier filter in the list mode --- src/list.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/list.rs b/src/list.rs index 2430ed7361..de120eaf64 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,6 +1,6 @@ use anyhow::Result; use crossterm::{ - event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}, + event::{self, Event, KeyCode, KeyEventKind}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, }; @@ -28,16 +28,10 @@ pub fn list(app_state: &mut AppState) -> Result<()> { let key = loop { match event::read()? { - Event::Key(key) => { - if key.modifiers != KeyModifiers::NONE { - continue; - } - - match key.kind { - KeyEventKind::Press | KeyEventKind::Repeat => break key, - KeyEventKind::Release => (), - } - } + Event::Key(key) => match key.kind { + KeyEventKind::Press | KeyEventKind::Repeat => break key, + KeyEventKind::Release => (), + }, // Redraw Event::Resize(_, _) => continue 'outer, // Ignore From 6e827da570278b6ff282f3b5c23e2ab95624117e Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 19:18:16 +0200 Subject: [PATCH 0700/1432] It doesn't take minutes :P --- src/app_state.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app_state.rs b/src/app_state.rs index b1440e8ae8..18d9e2ae29 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -222,6 +222,5 @@ impl AppState { const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" All exercises seem to be done. Recompiling and running all exercises to make sure that all of them are actually done. -This might take some minutes. "; From 06d1089714d77e8619fd0b5c34361eec5312363e Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 19:24:26 +0200 Subject: [PATCH 0701/1432] Set pending on fail in run mode --- src/run.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/run.rs b/src/run.rs index ea790e9aed..ebe4f965f6 100644 --- a/src/run.rs +++ b/src/run.rs @@ -15,6 +15,8 @@ pub fn run(app_state: &mut AppState) -> Result<()> { stdout.flush()?; if !output.status.success() { + app_state.set_pending(app_state.current_exercise_ind())?; + bail!("Ran {exercise} with errors"); } From ff4c7529846ba13ecb2e90616ff8fd7a9ee87164 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 19:30:29 +0200 Subject: [PATCH 0702/1432] Print FAILED --- src/app_state.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app_state.rs b/src/app_state.rs index 18d9e2ae29..cb7debe12b 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -190,6 +190,8 @@ impl AppState { writer.flush()?; if !exercise.run()?.status.success() { + writer.write_fmt(format_args!("{}\n\n", "FAILED".red()))?; + self.state_file.current_exercise_ind = exercise_ind; self.current_exercise = exercise; From 757723a7e8db5822df3b7ca56012448ca292ce4f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 19:30:36 +0200 Subject: [PATCH 0703/1432] Add missing newline --- src/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run.rs b/src/run.rs index ebe4f965f6..4748549245 100644 --- a/src/run.rs +++ b/src/run.rs @@ -21,7 +21,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { } stdout.write_fmt(format_args!( - "{}{}", + "{}{}\n", "βœ“ Successfully ran ".green(), exercise.path.to_string_lossy().green(), ))?; From 24539666afb0e8c80fbccbca7ad212ba8fbd1189 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 20:06:56 +0200 Subject: [PATCH 0704/1432] Show the final message --- info.toml | 3 ++- src/app_state.rs | 27 +++++++++++++++++---------- src/exercise.rs | 1 - src/main.rs | 6 ++++-- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/info.toml b/info.toml index d35b570252..b6b68008d3 100644 --- a/info.toml +++ b/info.toml @@ -20,7 +20,8 @@ started, here's a couple of notes about how Rustlings operates: and sometimes, other learners do too so you can help each other out! Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. -Make sure to have your editor open in the `rustlings` directory!""" +Make sure to have your editor open in the `rustlings` directory! +""" final_message = """We hope you enjoyed learning about the various aspects of Rust! If you noticed any issues, please don't hesitate to report them to our repo. diff --git a/src/app_state.rs b/src/app_state.rs index cb7debe12b..2ea3db42f4 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -51,24 +51,29 @@ impl StateFile { } } +#[must_use] +pub enum ExercisesProgress { + AllDone, + Pending, +} + pub struct AppState { state_file: StateFile, exercises: &'static [Exercise], n_done: u16, current_exercise: &'static Exercise, -} - -#[must_use] -pub enum ExercisesProgress { - AllDone, - Pending, + final_message: &'static str, } impl AppState { - pub fn new(exercises: Vec) -> Self { - // Leaking for sending the exercises to the debounce event handler. - // Leaking is not a problem since the exercises' slice is used until the end of the program. + pub fn new(mut exercises: Vec, mut final_message: String) -> Self { + // Leaking especially for sending the exercises to the debounce event handler. + // Leaking is not a problem because the `AppState` instance lives until + // the end of the program. + exercises.shrink_to_fit(); let exercises = exercises.leak(); + final_message.shrink_to_fit(); + let final_message = final_message.leak(); let state_file = StateFile::read_or_default(exercises); let n_done = state_file @@ -82,6 +87,7 @@ impl AppState { exercises, n_done, current_exercise, + final_message, } } @@ -210,7 +216,8 @@ impl AppState { writer.execute(Clear(ClearType::All))?; writer.write_all(FENISH_LINE.as_bytes())?; - // TODO: Show final message. + writer.write_all(self.final_message.as_bytes())?; + writer.write_all(b"\n")?; return Ok(ExercisesProgress::AllDone); }; diff --git a/src/exercise.rs b/src/exercise.rs index a9dcce34fb..a29b83aa23 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -26,7 +26,6 @@ pub enum Mode { pub struct InfoFile { // TODO pub welcome_message: Option, - // TODO pub final_message: Option, pub exercises: Vec, } diff --git a/src/main.rs b/src/main.rs index fdbb710c43..cdfa21f62a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,7 +68,7 @@ fn main() -> Result<()> { exit(1); } - let mut app_state = AppState::new(exercises); + let mut app_state = AppState::new(exercises, info_file.final_message.unwrap_or_default()); match args.command { None => loop { @@ -144,4 +144,6 @@ const FENISH_LINE: &str = "+---------------------------------------------------- β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m"; + β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m + +"; From 2a26dfcb005d2a9ee24e920462b37dfb6d235c32 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 13 Apr 2024 15:30:35 +0200 Subject: [PATCH 0705/1432] Remove unused ContextLine --- src/exercise.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index a29b83aa23..6aa3b82ef0 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -63,17 +63,6 @@ pub struct Exercise { pub hint: String, } -// The context information of a pending exercise. -#[derive(PartialEq, Eq, Debug)] -pub struct ContextLine { - // The source code line - pub line: String, - // The line number - pub number: usize, - // Whether this is important and should be highlighted - pub important: bool, -} - impl Exercise { fn cargo_cmd(&self, command: &str, args: &[&str]) -> Result { let mut cmd = Command::new("cargo"); From 5c0073a9485c4226e58b657cb49628919a28a942 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 01:15:43 +0200 Subject: [PATCH 0706/1432] Tolerate changes in the state file --- Cargo.lock | 1 + Cargo.toml | 1 + exercises/00_intro/intro1.rs | 1 - info.toml | 272 +++++++++--------- src/app_state.rs | 207 +++++++------ src/app_state/state_file.rs | 112 ++++++++ src/exercise.rs | 72 +---- src/info_file.rs | 81 ++++++ src/init.rs | 23 +- src/list.rs | 11 +- src/list/state.rs | 35 +-- src/main.rs | 40 ++- src/run.rs | 2 +- src/watch.rs | 15 +- .../{debounce_event.rs => notify_event.rs} | 10 +- 15 files changed, 514 insertions(+), 369 deletions(-) create mode 100644 src/app_state/state_file.rs create mode 100644 src/info_file.rs rename src/watch/{debounce_event.rs => notify_event.rs} (84%) diff --git a/Cargo.lock b/Cargo.lock index 6c64661471..dbf1923e7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,6 +684,7 @@ dependencies = [ "assert_cmd", "clap", "crossterm", + "hashbrown", "notify-debouncer-mini", "predicates", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index 285e7df6e5..14ae9a1439 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ edition.workspace = true anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" +hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index e4e0444af0..170d19589a 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,5 @@ // intro1.rs // -// TODO: Update comment // We sometimes encourage you to keep trying things on a given exercise, even // after you already figured it out. If you got everything working and feel // ready for the next exercise, remove the `I AM NOT DONE` comment below. diff --git a/info.toml b/info.toml index b6b68008d3..fa90ad7044 100644 --- a/info.toml +++ b/info.toml @@ -33,10 +33,11 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md # INTRO +# TODO: Update exercise [[exercises]] name = "intro1" -path = "exercises/00_intro/intro1.rs" -mode = "compile" +dir = "00_intro" +mode = "run" # TODO: Fix hint hint = """ Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file @@ -44,8 +45,8 @@ to move on to the next exercise.""" [[exercises]] name = "intro2" -path = "exercises/00_intro/intro2.rs" -mode = "compile" +dir = "00_intro" +mode = "run" hint = """ The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" @@ -53,16 +54,16 @@ The compiler is informing us that we've got the name of the print macro wrong, a [[exercises]] name = "variables1" -path = "exercises/01_variables/variables1.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ The declaration in the first line in the main function is missing a keyword that is needed in Rust to create a new variable binding.""" [[exercises]] name = "variables2" -path = "exercises/01_variables/variables2.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ The compiler message is saying that Rust cannot infer the type that the variable binding `x` has with what is given here. @@ -80,8 +81,8 @@ What if `x` is the same type as `10`? What if it's a different type?""" [[exercises]] name = "variables3" -path = "exercises/01_variables/variables3.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ Oops! In this exercise, we have a variable binding that we've created on in the first line in the `main` function, and we're trying to use it in the next line, @@ -94,8 +95,8 @@ programming language -- thankfully the Rust compiler has caught this for us!""" [[exercises]] name = "variables4" -path = "exercises/01_variables/variables4.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ In Rust, variable bindings are immutable by default. But here we're trying to reassign a different value to `x`! There's a keyword we can use to make @@ -103,8 +104,8 @@ a variable binding mutable instead.""" [[exercises]] name = "variables5" -path = "exercises/01_variables/variables5.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ In `variables4` we already learned how to make an immutable variable mutable using a special keyword. Unfortunately this doesn't help us much in this @@ -121,8 +122,8 @@ Try to solve this exercise afterwards using this technique.""" [[exercises]] name = "variables6" -path = "exercises/01_variables/variables6.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ We know about variables and mutability, but there is another important type of variable available: constants. @@ -141,8 +142,8 @@ https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants [[exercises]] name = "functions1" -path = "exercises/02_functions/functions1.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This main function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. @@ -151,24 +152,24 @@ Sounds a lot like `main`, doesn't it?""" [[exercises]] name = "functions2" -path = "exercises/02_functions/functions2.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ Rust requires that all parts of a function's signature have type annotations, but `call_me` is missing the type annotation of `num`.""" [[exercises]] name = "functions3" -path = "exercises/02_functions/functions3.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This time, the function *declaration* is okay, but there's something wrong with the place where we're calling the function.""" [[exercises]] name = "functions4" -path = "exercises/02_functions/functions4.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ The error message points to the function `sale_price` and says it expects a type after the `->`. This is where the function's return type should be -- take a @@ -179,8 +180,8 @@ for the inputs of the functions here, since the original prices shouldn't be neg [[exercises]] name = "functions5" -path = "exercises/02_functions/functions5.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This is a really common error that can be fixed by removing one character. It happens because Rust distinguishes between expressions and statements: @@ -198,7 +199,7 @@ They are not the same. There are two solutions: [[exercises]] name = "if1" -path = "exercises/03_if/if1.rs" +dir = "03_if" mode = "test" hint = """ It's possible to do this in one line if you would like! @@ -214,7 +215,7 @@ Remember in Rust that: [[exercises]] name = "if2" -path = "exercises/03_if/if2.rs" +dir = "03_if" mode = "test" hint = """ For that first compiler error, it's important in Rust that each conditional @@ -223,7 +224,7 @@ conditions checking different input values.""" [[exercises]] name = "if3" -path = "exercises/03_if/if3.rs" +dir = "03_if" mode = "test" hint = """ In Rust, every arm of an `if` expression has to return the same type of value. @@ -233,7 +234,6 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" -path = "exercises/quiz1.rs" mode = "test" hint = "No hints this time ;)" @@ -241,20 +241,20 @@ hint = "No hints this time ;)" [[exercises]] name = "primitive_types1" -path = "exercises/04_primitive_types/primitive_types1.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "primitive_types2" -path = "exercises/04_primitive_types/primitive_types2.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "primitive_types3" -path = "exercises/04_primitive_types/primitive_types3.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = """ There's a shorthand to initialize Arrays with a certain size that does not require you to type in 100 items (but you certainly can if you want!). @@ -269,7 +269,7 @@ for `a.len() >= 100`?""" [[exercises]] name = "primitive_types4" -path = "exercises/04_primitive_types/primitive_types4.rs" +dir = "04_primitive_types" mode = "test" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section @@ -284,8 +284,8 @@ https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] name = "primitive_types5" -path = "exercises/04_primitive_types/primitive_types5.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = """ Take a look at the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type @@ -297,7 +297,7 @@ of the tuple. You can do it!!""" [[exercises]] name = "primitive_types6" -path = "exercises/04_primitive_types/primitive_types6.rs" +dir = "04_primitive_types" mode = "test" hint = """ While you could use a destructuring `let` for the tuple here, try @@ -310,7 +310,7 @@ Now you have another tool in your toolbox!""" [[exercises]] name = "vecs1" -path = "exercises/05_vecs/vecs1.rs" +dir = "05_vecs" mode = "test" hint = """ In Rust, there are two ways to define a Vector. @@ -325,7 +325,7 @@ of the Rust book to learn more. [[exercises]] name = "vecs2" -path = "exercises/05_vecs/vecs2.rs" +dir = "05_vecs" mode = "test" hint = """ In the first function we are looping over the Vector and getting a reference to @@ -348,7 +348,7 @@ What do you think is the more commonly used pattern under Rust developers? [[exercises]] name = "move_semantics1" -path = "exercises/06_move_semantics/move_semantics1.rs" +dir = "06_move_semantics" mode = "test" hint = """ So you've got the "cannot borrow immutable local variable `vec` as mutable" @@ -362,7 +362,7 @@ happens!""" [[exercises]] name = "move_semantics2" -path = "exercises/06_move_semantics/move_semantics2.rs" +dir = "06_move_semantics" mode = "test" hint = """ When running this exercise for the first time, you'll notice an error about @@ -383,7 +383,7 @@ try them all: [[exercises]] name = "move_semantics3" -path = "exercises/06_move_semantics/move_semantics3.rs" +dir = "06_move_semantics" mode = "test" hint = """ The difference between this one and the previous ones is that the first line @@ -393,7 +393,7 @@ an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" -path = "exercises/06_move_semantics/move_semantics4.rs" +dir = "06_move_semantics" mode = "test" hint = """ Stop reading whenever you feel like you have enough direction :) Or try @@ -407,7 +407,7 @@ So the end goal is to: [[exercises]] name = "move_semantics5" -path = "exercises/06_move_semantics/move_semantics5.rs" +dir = "06_move_semantics" mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in @@ -419,8 +419,8 @@ https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-ref [[exercises]] name = "move_semantics6" -path = "exercises/06_move_semantics/move_semantics6.rs" -mode = "compile" +dir = "06_move_semantics" +mode = "run" hint = """ To find the answer, you can consult the book section "References and Borrowing": https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html @@ -440,7 +440,7 @@ Another hint: it has to do with the `&` character.""" [[exercises]] name = "structs1" -path = "exercises/07_structs/structs1.rs" +dir = "07_structs" mode = "test" hint = """ Rust has more than one type of struct. Three actually, all variants are used to @@ -460,7 +460,7 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" -path = "exercises/07_structs/structs2.rs" +dir = "07_structs" mode = "test" hint = """ Creating instances of structs is easy, all you need to do is assign some values @@ -472,7 +472,7 @@ https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-ins [[exercises]] name = "structs3" -path = "exercises/07_structs/structs3.rs" +dir = "07_structs" mode = "test" hint = """ For `is_international`: What makes a package international? Seems related to @@ -488,21 +488,21 @@ https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" [[exercises]] name = "enums1" -path = "exercises/08_enums/enums1.rs" -mode = "compile" +dir = "08_enums" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "enums2" -path = "exercises/08_enums/enums2.rs" -mode = "compile" +dir = "08_enums" +mode = "run" hint = """ You can create enumerations that have different variants with different types such as no data, anonymous structs, a single string, tuples, ...etc""" [[exercises]] name = "enums3" -path = "exercises/08_enums/enums3.rs" +dir = "08_enums" mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. @@ -516,8 +516,8 @@ to get value in the variant.""" [[exercises]] name = "strings1" -path = "exercises/09_strings/strings1.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = """ The `current_favorite_color` function is currently returning a string slice with the `'static` lifetime. We know this because the data of the string lives @@ -530,8 +530,8 @@ another way that uses the `From` trait.""" [[exercises]] name = "strings2" -path = "exercises/09_strings/strings2.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way @@ -545,7 +545,7 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion [[exercises]] name = "strings3" -path = "exercises/09_strings/strings3.rs" +dir = "09_strings" mode = "test" hint = """ There's tons of useful standard library functions for strings. Let's try and use some of them: @@ -556,16 +556,16 @@ the string slice into an owned string, which you can then freely extend.""" [[exercises]] name = "strings4" -path = "exercises/09_strings/strings4.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = "No hints this time ;)" # MODULES [[exercises]] name = "modules1" -path = "exercises/10_modules/modules1.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ Everything is private in Rust by default-- but there's a keyword we can use to make something public! The compiler error should point to the thing that @@ -573,8 +573,8 @@ needs to be public.""" [[exercises]] name = "modules2" -path = "exercises/10_modules/modules2.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and @@ -585,8 +585,8 @@ Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-w [[exercises]] name = "modules3" -path = "exercises/10_modules/modules3.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a `use` statement for these two to bring them into scope. You can use nested @@ -596,7 +596,7 @@ paths or the glob operator to bring these two in using only one line.""" [[exercises]] name = "hashmaps1" -path = "exercises/11_hashmaps/hashmaps1.rs" +dir = "11_hashmaps" mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out @@ -608,7 +608,7 @@ Hint 2: Number of fruits should be at least 5. And you have to put [[exercises]] name = "hashmaps2" -path = "exercises/11_hashmaps/hashmaps2.rs" +dir = "11_hashmaps" mode = "test" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. @@ -617,7 +617,7 @@ Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only- [[exercises]] name = "hashmaps3" -path = "exercises/11_hashmaps/hashmaps3.rs" +dir = "11_hashmaps" mode = "test" hint = """ Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert @@ -635,7 +635,6 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" -path = "exercises/quiz2.rs" mode = "test" hint = "No hints this time ;)" @@ -643,7 +642,7 @@ hint = "No hints this time ;)" [[exercises]] name = "options1" -path = "exercises/12_options/options1.rs" +dir = "12_options" mode = "test" hint = """ Options can have a `Some` value, with an inner value, or a `None` value, @@ -655,7 +654,7 @@ it doesn't panic in your face later?""" [[exercises]] name = "options2" -path = "exercises/12_options/options2.rs" +dir = "12_options" mode = "test" hint = """ Check out: @@ -672,8 +671,8 @@ Also see `Option::flatten` [[exercises]] name = "options3" -path = "exercises/12_options/options3.rs" -mode = "compile" +dir = "12_options" +mode = "run" hint = """ The compiler says a partial move happened in the `match` statement. How can this be avoided? The compiler shows the correction needed. @@ -685,7 +684,7 @@ https://doc.rust-lang.org/std/keyword.ref.html""" [[exercises]] name = "errors1" -path = "exercises/13_error_handling/errors1.rs" +dir = "13_error_handling" mode = "test" hint = """ `Ok` and `Err` are the two variants of `Result`, so what the tests are saying @@ -701,7 +700,7 @@ To make this change, you'll need to: [[exercises]] name = "errors2" -path = "exercises/13_error_handling/errors2.rs" +dir = "13_error_handling" mode = "test" hint = """ One way to handle this is using a `match` statement on @@ -717,8 +716,8 @@ and give it a try!""" [[exercises]] name = "errors3" -path = "exercises/13_error_handling/errors3.rs" -mode = "compile" +dir = "13_error_handling" +mode = "run" hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your @@ -729,7 +728,7 @@ positive results.""" [[exercises]] name = "errors4" -path = "exercises/13_error_handling/errors4.rs" +dir = "13_error_handling" mode = "test" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning @@ -741,8 +740,8 @@ everything is... okay :)""" [[exercises]] name = "errors5" -path = "exercises/13_error_handling/errors5.rs" -mode = "compile" +dir = "13_error_handling" +mode = "run" hint = """ There are two different possible `Result` types produced within `main()`, which are propagated using `?` operators. How do we declare a return type from @@ -765,7 +764,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "errors6" -path = "exercises/13_error_handling/errors6.rs" +dir = "13_error_handling" mode = "test" hint = """ This exercise uses a completed version of `PositiveNonzeroInteger` from @@ -787,8 +786,8 @@ https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" [[exercises]] name = "generics1" -path = "exercises/14_generics/generics1.rs" -mode = "compile" +dir = "14_generics" +mode = "run" hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any type. @@ -797,7 +796,7 @@ You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] name = "generics2" -path = "exercises/14_generics/generics2.rs" +dir = "14_generics" mode = "test" hint = """ Currently we are wrapping only values of type `u32`. @@ -811,7 +810,7 @@ If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html [[exercises]] name = "traits1" -path = "exercises/15_traits/traits1.rs" +dir = "15_traits" mode = "test" hint = """ A discussion about Traits in Rust can be found at: @@ -820,7 +819,7 @@ https://doc.rust-lang.org/book/ch10-02-traits.html [[exercises]] name = "traits2" -path = "exercises/15_traits/traits2.rs" +dir = "15_traits" mode = "test" hint = """ Notice how the trait takes ownership of `self`, and returns `Self`. @@ -833,7 +832,7 @@ the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" [[exercises]] name = "traits3" -path = "exercises/15_traits/traits3.rs" +dir = "15_traits" mode = "test" hint = """ Traits can have a default implementation for functions. Structs that implement @@ -845,7 +844,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#def [[exercises]] name = "traits4" -path = "exercises/15_traits/traits4.rs" +dir = "15_traits" mode = "test" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing @@ -856,8 +855,8 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#tra [[exercises]] name = "traits5" -path = "exercises/15_traits/traits5.rs" -mode = "compile" +dir = "15_traits" +mode = "run" hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the '??' with 'impl <> + <>'. @@ -869,7 +868,6 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" -path = "exercises/quiz3.rs" mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back @@ -881,16 +879,16 @@ You may also need this: `use std::fmt::Display;`.""" [[exercises]] name = "lifetimes1" -path = "exercises/16_lifetimes/lifetimes1.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ Let the compiler guide you. Also take a look at the book if you need help: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] name = "lifetimes2" -path = "exercises/16_lifetimes/lifetimes2.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ Remember that the generic lifetime `'a` will get the concrete lifetime that is equal to the smaller of the lifetimes of `x` and `y`. @@ -903,8 +901,8 @@ inner block: [[exercises]] name = "lifetimes3" -path = "exercises/16_lifetimes/lifetimes3.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" @@ -913,7 +911,7 @@ to be added?""" [[exercises]] name = "tests1" -path = "exercises/17_tests/tests1.rs" +dir = "17_tests" mode = "test" hint = """ You don't even need to write any code to test -- you can just test values and @@ -928,7 +926,7 @@ ones pass, and which ones fail :)""" [[exercises]] name = "tests2" -path = "exercises/17_tests/tests2.rs" +dir = "17_tests" mode = "test" hint = """ Like the previous exercise, you don't need to write any code to get this test @@ -941,7 +939,7 @@ argument comes first and which comes second!""" [[exercises]] name = "tests3" -path = "exercises/17_tests/tests3.rs" +dir = "17_tests" mode = "test" hint = """ You can call a function right where you're passing arguments to `assert!`. So @@ -952,7 +950,7 @@ what you're doing using `!`, like `assert!(!having_fun())`.""" [[exercises]] name = "tests4" -path = "exercises/17_tests/tests4.rs" +dir = "17_tests" mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. @@ -966,7 +964,7 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa [[exercises]] name = "iterators1" -path = "exercises/18_iterators/iterators1.rs" +dir = "18_iterators" mode = "test" hint = """ Step 1: @@ -989,7 +987,7 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. [[exercises]] name = "iterators2" -path = "exercises/18_iterators/iterators2.rs" +dir = "18_iterators" mode = "test" hint = """ Step 1: @@ -1015,7 +1013,7 @@ powerful and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" -path = "exercises/18_iterators/iterators3.rs" +dir = "18_iterators" mode = "test" hint = """ The `divide` function needs to return the correct error when even division is @@ -1034,7 +1032,7 @@ powerful! It can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" -path = "exercises/18_iterators/iterators4.rs" +dir = "18_iterators" mode = "test" hint = """ In an imperative language, you might write a `for` loop that updates a mutable @@ -1046,7 +1044,7 @@ Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" -path = "exercises/18_iterators/iterators5.rs" +dir = "18_iterators" mode = "test" hint = """ The documentation for the `std::iter::Iterator` trait contains numerous methods @@ -1065,7 +1063,7 @@ a different method that could make your code more compact than using `fold`.""" [[exercises]] name = "box1" -path = "exercises/19_smart_pointers/box1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ Step 1: @@ -1089,7 +1087,7 @@ definition and try other types! [[exercises]] name = "rc1" -path = "exercises/19_smart_pointers/rc1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ This is a straightforward exercise to use the `Rc` type. Each `Planet` has @@ -1108,8 +1106,8 @@ See more at: https://doc.rust-lang.org/book/ch15-04-rc.html [[exercises]] name = "arc1" -path = "exercises/19_smart_pointers/arc1.rs" -mode = "compile" +dir = "19_smart_pointers" +mode = "run" hint = """ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` @@ -1126,7 +1124,7 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" -path = "exercises/19_smart_pointers/cow1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is @@ -1140,8 +1138,8 @@ on the `Cow` type. [[exercises]] name = "threads1" -path = "exercises/20_threads/threads1.rs" -mode = "compile" +dir = "20_threads" +mode = "run" hint = """ `JoinHandle` is a struct that is returned from a spawned thread: https://doc.rust-lang.org/std/thread/fn.spawn.html @@ -1158,8 +1156,8 @@ https://doc.rust-lang.org/std/thread/struct.JoinHandle.html [[exercises]] name = "threads2" -path = "exercises/20_threads/threads2.rs" -mode = "compile" +dir = "20_threads" +mode = "run" hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access to **immutable** data. But we want to *change* the number of `jobs_completed` @@ -1180,7 +1178,7 @@ https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-betwee [[exercises]] name = "threads3" -path = "exercises/20_threads/threads3.rs" +dir = "20_threads" mode = "test" hint = """ An alternate way to handle concurrency between threads is to use an `mpsc` @@ -1199,8 +1197,8 @@ See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. [[exercises]] name = "macros1" -path = "exercises/21_macros/macros1.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ When you call a macro, you need to add something special compared to a regular function call. If you're stuck, take a look at what's inside @@ -1208,8 +1206,8 @@ regular function call. If you're stuck, take a look at what's inside [[exercises]] name = "macros2" -path = "exercises/21_macros/macros2.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ Macros don't quite play by the same rules as the rest of Rust, in terms of what's available where. @@ -1219,8 +1217,8 @@ Unlike other things in Rust, the order of "where you define a macro" versus [[exercises]] name = "macros3" -path = "exercises/21_macros/macros3.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ In order to use a macro outside of its module, you need to do something special to the module to lift the macro out into its parent. @@ -1230,8 +1228,8 @@ exported macros, if you've seen any of those around.""" [[exercises]] name = "macros4" -path = "exercises/21_macros/macros4.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ You only need to add a single character to make this compile. @@ -1247,7 +1245,7 @@ https://veykril.github.io/tlborm/""" [[exercises]] name = "clippy1" -path = "exercises/22_clippy/clippy1.rs" +dir = "22_clippy" mode = "clippy" hint = """ Rust stores the highest precision version of any long or infinite precision @@ -1263,14 +1261,14 @@ appropriate replacement constant from `std::f32::consts`...""" [[exercises]] name = "clippy2" -path = "exercises/22_clippy/clippy2.rs" +dir = "22_clippy" mode = "clippy" hint = """ `for` loops over `Option` values are more clearly expressed as an `if let`""" [[exercises]] name = "clippy3" -path = "exercises/22_clippy/clippy3.rs" +dir = "22_clippy" mode = "clippy" hint = "No hints this time!" @@ -1278,7 +1276,7 @@ hint = "No hints this time!" [[exercises]] name = "using_as" -path = "exercises/23_conversions/using_as.rs" +dir = "23_conversions" mode = "test" hint = """ Use the `as` operator to cast one of the operands in the last line of the @@ -1286,14 +1284,14 @@ Use the `as` operator to cast one of the operands in the last line of the [[exercises]] name = "from_into" -path = "exercises/23_conversions/from_into.rs" +dir = "23_conversions" mode = "test" hint = """ Follow the steps provided right before the `From` implementation""" [[exercises]] name = "from_str" -path = "exercises/23_conversions/from_str.rs" +dir = "23_conversions" mode = "test" hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, @@ -1314,7 +1312,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "try_from_into" -path = "exercises/23_conversions/try_from_into.rs" +dir = "23_conversions" mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. @@ -1337,7 +1335,7 @@ Challenge: Can you make the `TryFrom` implementations generic over many integer [[exercises]] name = "as_ref_mut" -path = "exercises/23_conversions/as_ref_mut.rs" +dir = "23_conversions" mode = "test" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/src/app_state.rs b/src/app_state.rs index 2ea3db42f4..1a051b9766 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -4,52 +4,16 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; -use serde::{Deserialize, Serialize}; -use std::{ - fs, - io::{StdoutLock, Write}, -}; - -use crate::{exercise::Exercise, FENISH_LINE}; - -const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; - -#[derive(Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -struct StateFile { - current_exercise_ind: usize, - progress: Vec, -} - -impl StateFile { - fn read(exercises: &[Exercise]) -> Option { - let file_content = fs::read(".rustlings-state.json").ok()?; +use std::io::{StdoutLock, Write}; - let slf: Self = serde_json::de::from_slice(&file_content).ok()?; +mod state_file; - if slf.progress.len() != exercises.len() || slf.current_exercise_ind >= exercises.len() { - return None; - } - - Some(slf) - } - - fn read_or_default(exercises: &[Exercise]) -> Self { - Self::read(exercises).unwrap_or_else(|| Self { - current_exercise_ind: 0, - progress: vec![false; exercises.len()], - }) - } +use crate::{exercise::Exercise, info_file::InfoFile, FENISH_LINE}; - fn write(&self) -> Result<()> { - let mut buf = Vec::with_capacity(1024); - serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; - fs::write(".rustlings-state.json", buf) - .context("Failed to write the state file `.rustlings-state.json`")?; +use self::state_file::{write, StateFileDeser}; - Ok(()) - } -} +const STATE_FILE_NAME: &str = ".rustlings-state.json"; +const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; #[must_use] pub enum ExercisesProgress { @@ -58,52 +22,85 @@ pub enum ExercisesProgress { } pub struct AppState { - state_file: StateFile, - exercises: &'static [Exercise], + current_exercise_ind: usize, + exercises: Vec, n_done: u16, - current_exercise: &'static Exercise, - final_message: &'static str, + welcome_message: String, + final_message: String, } impl AppState { - pub fn new(mut exercises: Vec, mut final_message: String) -> Self { - // Leaking especially for sending the exercises to the debounce event handler. - // Leaking is not a problem because the `AppState` instance lives until - // the end of the program. - exercises.shrink_to_fit(); - let exercises = exercises.leak(); - final_message.shrink_to_fit(); - let final_message = final_message.leak(); - - let state_file = StateFile::read_or_default(exercises); - let n_done = state_file - .progress - .iter() - .fold(0, |acc, done| acc + u16::from(*done)); - let current_exercise = &exercises[state_file.current_exercise_ind]; + pub fn new(info_file: InfoFile) -> Self { + let mut exercises = info_file + .exercises + .into_iter() + .map(|mut exercise_info| { + // Leaking to be able to borrow in the watch mode `Table`. + // Leaking is not a problem because the `AppState` instance lives until + // the end of the program. + let path = Box::leak(exercise_info.path().into_boxed_path()); + + exercise_info.name.shrink_to_fit(); + let name = exercise_info.name.leak(); + + let hint = exercise_info.hint.trim().to_owned(); + + Exercise { + name, + path, + mode: exercise_info.mode, + hint, + done: false, + } + }) + .collect::>(); + + let (current_exercise_ind, n_done) = StateFileDeser::read().map_or((0, 0), |state_file| { + let mut state_file_exercises = + hashbrown::HashMap::with_capacity(state_file.exercises.len()); + + for (ind, exercise_state) in state_file.exercises.into_iter().enumerate() { + state_file_exercises.insert( + exercise_state.name, + (ind == state_file.current_exercise_ind, exercise_state.done), + ); + } + + let mut current_exercise_ind = 0; + let mut n_done = 0; + for (ind, exercise) in exercises.iter_mut().enumerate() { + if let Some((current, done)) = state_file_exercises.get(exercise.name) { + if *done { + exercise.done = true; + n_done += 1; + } + + if *current { + current_exercise_ind = ind; + } + } + } + + (current_exercise_ind, n_done) + }); Self { - state_file, + current_exercise_ind, exercises, n_done, - current_exercise, - final_message, + welcome_message: info_file.welcome_message.unwrap_or_default(), + final_message: info_file.final_message.unwrap_or_default(), } } #[inline] pub fn current_exercise_ind(&self) -> usize { - self.state_file.current_exercise_ind - } - - #[inline] - pub fn progress(&self) -> &[bool] { - &self.state_file.progress + self.current_exercise_ind } #[inline] - pub fn exercises(&self) -> &'static [Exercise] { - self.exercises + pub fn exercises(&self) -> &[Exercise] { + &self.exercises } #[inline] @@ -112,8 +109,8 @@ impl AppState { } #[inline] - pub fn current_exercise(&self) -> &'static Exercise { - self.current_exercise + pub fn current_exercise(&self) -> &Exercise { + &self.exercises[self.current_exercise_ind] } pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { @@ -121,70 +118,61 @@ impl AppState { bail!(BAD_INDEX_ERR); } - self.state_file.current_exercise_ind = ind; - self.current_exercise = &self.exercises[ind]; + self.current_exercise_ind = ind; - self.state_file.write() + write(self) } pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> { - let (ind, exercise) = self + // O(N) is fine since this method is used only once until the program exits. + // Building a hashmap would have more overhead. + self.current_exercise_ind = self .exercises .iter() - .enumerate() - .find(|(_, exercise)| exercise.name == name) + .position(|exercise| exercise.name == name) .with_context(|| format!("No exercise found for '{name}'!"))?; - self.state_file.current_exercise_ind = ind; - self.current_exercise = exercise; - - self.state_file.write() + write(self) } pub fn set_pending(&mut self, ind: usize) -> Result<()> { - let done = self - .state_file - .progress - .get_mut(ind) - .context(BAD_INDEX_ERR)?; - - if *done { - *done = false; + let exercise = self.exercises.get_mut(ind).context(BAD_INDEX_ERR)?; + + if exercise.done { + exercise.done = false; self.n_done -= 1; - self.state_file.write()?; + write(self)?; } Ok(()) } fn next_pending_exercise_ind(&self) -> Option { - let current_ind = self.state_file.current_exercise_ind; - - if current_ind == self.state_file.progress.len() - 1 { + if self.current_exercise_ind == self.exercises.len() - 1 { // The last exercise is done. // Search for exercises not done from the start. - return self.state_file.progress[..current_ind] + return self.exercises[..self.current_exercise_ind] .iter() - .position(|done| !done); + .position(|exercise| !exercise.done); } // The done exercise isn't the last one. // Search for a pending exercise after the current one and then from the start. - match self.state_file.progress[current_ind + 1..] + match self.exercises[self.current_exercise_ind + 1..] .iter() - .position(|done| !done) + .position(|exercise| !exercise.done) { - Some(ind) => Some(current_ind + 1 + ind), - None => self.state_file.progress[..current_ind] + Some(ind) => Some(self.current_exercise_ind + 1 + ind), + None => self.exercises[..self.current_exercise_ind] .iter() - .position(|done| !done), + .position(|exercise| !exercise.done), } } pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { - let done = &mut self.state_file.progress[self.state_file.current_exercise_ind]; - if !*done { - *done = true; + let exercise = &mut self.exercises[self.current_exercise_ind]; + if !exercise.done { + exercise.done = true; self.n_done += 1; } @@ -198,15 +186,14 @@ impl AppState { if !exercise.run()?.status.success() { writer.write_fmt(format_args!("{}\n\n", "FAILED".red()))?; - self.state_file.current_exercise_ind = exercise_ind; - self.current_exercise = exercise; + self.current_exercise_ind = exercise_ind; // No check if the exercise is done before setting it to pending // because no pending exercise was found. - self.state_file.progress[exercise_ind] = false; + self.exercises[exercise_ind].done = false; self.n_done -= 1; - self.state_file.write()?; + write(self)?; return Ok(ExercisesProgress::Pending); } diff --git a/src/app_state/state_file.rs b/src/app_state/state_file.rs new file mode 100644 index 0000000000..364a1fa382 --- /dev/null +++ b/src/app_state/state_file.rs @@ -0,0 +1,112 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::fs; + +use crate::exercise::Exercise; + +use super::{AppState, STATE_FILE_NAME}; + +#[derive(Deserialize)] +pub struct ExerciseStateDeser { + pub name: String, + pub done: bool, +} + +#[derive(Serialize)] +struct ExerciseStateSer<'a> { + name: &'a str, + done: bool, +} + +struct ExercisesStateSerializer<'a>(&'a [Exercise]); + +impl<'a> Serialize for ExercisesStateSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let iter = self.0.iter().map(|exercise| ExerciseStateSer { + name: exercise.name, + done: exercise.done, + }); + + serializer.collect_seq(iter) + } +} + +#[derive(Deserialize)] +pub struct StateFileDeser { + pub current_exercise_ind: usize, + pub exercises: Vec, +} + +#[derive(Serialize)] +struct StateFileSer<'a> { + current_exercise_ind: usize, + exercises: ExercisesStateSerializer<'a>, +} + +impl StateFileDeser { + pub fn read() -> Option { + let file_content = fs::read(STATE_FILE_NAME).ok()?; + serde_json::de::from_slice(&file_content).ok() + } +} + +pub fn write(app_state: &AppState) -> Result<()> { + let content = StateFileSer { + current_exercise_ind: app_state.current_exercise_ind, + exercises: ExercisesStateSerializer(&app_state.exercises), + }; + + let mut buf = Vec::with_capacity(1024); + serde_json::ser::to_writer(&mut buf, &content).context("Failed to serialize the state")?; + fs::write(STATE_FILE_NAME, buf) + .with_context(|| format!("Failed to write the state file `{STATE_FILE_NAME}`"))?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use crate::info_file::Mode; + + use super::*; + + #[test] + fn ser_deser_sync() { + let current_exercise_ind = 1; + let exercises = [ + Exercise { + name: "1", + path: Path::new("exercises/1.rs"), + mode: Mode::Run, + hint: String::new(), + done: true, + }, + Exercise { + name: "2", + path: Path::new("exercises/2.rs"), + mode: Mode::Test, + hint: String::new(), + done: false, + }, + ]; + + let ser = StateFileSer { + current_exercise_ind, + exercises: ExercisesStateSerializer(&exercises), + }; + let deser: StateFileDeser = + serde_json::de::from_slice(&serde_json::ser::to_vec(&ser).unwrap()).unwrap(); + + assert_eq!(deser.current_exercise_ind, current_exercise_ind); + assert!(deser + .exercises + .iter() + .zip(exercises) + .all(|(deser, ser)| deser.name == ser.name && deser.done == ser.done)); + } +} diff --git a/src/exercise.rs b/src/exercise.rs index 6aa3b82ef0..c5ece5f5f7 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,66 +1,25 @@ use anyhow::{Context, Result}; -use serde::Deserialize; use std::{ - fmt::{self, Debug, Display, Formatter}, - fs::{self}, - path::PathBuf, + fmt::{self, Display, Formatter}, + path::Path, process::{Command, Output}, }; -use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; - -// The mode of the exercise. -#[derive(Deserialize, Copy, Clone)] -#[serde(rename_all = "lowercase")] -pub enum Mode { - // The exercise should be compiled as a binary - Compile, - // The exercise should be compiled as a test harness - Test, - // The exercise should be linted with clippy - Clippy, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct InfoFile { - // TODO - pub welcome_message: Option, - pub final_message: Option, - pub exercises: Vec, -} - -impl InfoFile { - pub fn parse() -> Result { - // Read a local `info.toml` if it exists. - // Mainly to let the tests work for now. - let slf: Self = if let Ok(file_content) = fs::read_to_string("info.toml") { - toml_edit::de::from_str(&file_content) - } else { - toml_edit::de::from_str(include_str!("../info.toml")) - } - .context("Failed to parse `info.toml`")?; - - if slf.exercises.is_empty() { - panic!("{NO_EXERCISES_ERR}"); - } - - Ok(slf) - } -} +use crate::{ + embedded::{WriteStrategy, EMBEDDED_FILES}, + info_file::Mode, +}; -// Deserialized from the `info.toml` file. -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] pub struct Exercise { - // Name of the exercise - pub name: String, - // The path to the file containing the exercise's source code - pub path: PathBuf, + // Exercise's unique name + pub name: &'static str, + // Exercise's path + pub path: &'static Path, // The mode of the exercise pub mode: Mode, // The hint text associated with the exercise pub hint: String, + pub done: bool, } impl Exercise { @@ -79,7 +38,7 @@ impl Exercise { .arg("always") .arg("-q") .arg("--bin") - .arg(&self.name) + .arg(self.name) .args(args) .output() .context("Failed to run Cargo") @@ -87,7 +46,7 @@ impl Exercise { pub fn run(&self) -> Result { match self.mode { - Mode::Compile => self.cargo_cmd("run", &[]), + Mode::Run => self.cargo_cmd("run", &[]), Mode::Test => self.cargo_cmd("test", &["--", "--nocapture", "--format", "pretty"]), Mode::Clippy => self.cargo_cmd( "clippy", @@ -98,7 +57,7 @@ impl Exercise { pub fn reset(&self) -> Result<()> { EMBEDDED_FILES - .write_exercise_to_disk(&self.path, WriteStrategy::Overwrite) + .write_exercise_to_disk(self.path, WriteStrategy::Overwrite) .with_context(|| format!("Failed to reset the exercise {self}")) } } @@ -108,6 +67,3 @@ impl Display for Exercise { Display::fmt(&self.path.display(), f) } } - -const NO_EXERCISES_ERR: &str = "There are no exercises yet! -If you are developing third-party exercises, add at least one exercise before testing."; diff --git a/src/info_file.rs b/src/info_file.rs new file mode 100644 index 0000000000..dc97b9262a --- /dev/null +++ b/src/info_file.rs @@ -0,0 +1,81 @@ +use anyhow::{bail, Context, Error, Result}; +use serde::Deserialize; +use std::{fs, path::PathBuf}; + +// The mode of the exercise. +#[derive(Deserialize, Copy, Clone)] +#[serde(rename_all = "lowercase")] +pub enum Mode { + // The exercise should be compiled as a binary + Run, + // The exercise should be compiled as a test harness + Test, + // The exercise should be linted with clippy + Clippy, +} + +// Deserialized from the `info.toml` file. +#[derive(Deserialize)] +pub struct ExerciseInfo { + // Name of the exercise + pub name: String, + // The exercise's directory inside the `exercises` directory + pub dir: Option, + // The mode of the exercise + pub mode: Mode, + // The hint text associated with the exercise + pub hint: String, +} + +impl ExerciseInfo { + pub fn path(&self) -> PathBuf { + let path = if let Some(dir) = &self.dir { + format!("exercises/{dir}/{}.rs", self.name) + } else { + format!("exercises/{}.rs", self.name) + }; + + PathBuf::from(path) + } +} + +#[derive(Deserialize)] +pub struct InfoFile { + pub welcome_message: Option, + pub final_message: Option, + pub exercises: Vec, +} + +impl InfoFile { + pub fn parse() -> Result { + // Read a local `info.toml` if it exists. + let slf: Self = match fs::read_to_string("info.toml") { + Ok(file_content) => toml_edit::de::from_str(&file_content) + .context("Failed to parse the `info.toml` file")?, + Err(e) => match e.kind() { + std::io::ErrorKind::NotFound => { + toml_edit::de::from_str(include_str!("../info.toml")) + .context("Failed to parse the embedded `info.toml` file")? + } + _ => return Err(Error::from(e).context("Failed to read the `info.toml` file")), + }, + }; + + if slf.exercises.is_empty() { + bail!("{NO_EXERCISES_ERR}"); + } + + let mut names_set = hashbrown::HashSet::with_capacity(slf.exercises.len()); + for exercise in &slf.exercises { + if !names_set.insert(exercise.name.as_str()) { + bail!("Exercise names must all be unique!") + } + } + drop(names_set); + + Ok(slf) + } +} + +const NO_EXERCISES_ERR: &str = "There are no exercises yet! +If you are developing third-party exercises, add at least one exercise before testing."; diff --git a/src/init.rs b/src/init.rs index 093610ab07..2badf3767e 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,17 +6,21 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, exercise::Exercise}; +use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; -fn create_cargo_toml(exercises: &[Exercise]) -> io::Result<()> { +fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { let mut cargo_toml = Vec::with_capacity(1 << 13); cargo_toml.extend_from_slice(b"bin = [\n"); - for exercise in exercises { + for exercise_info in exercise_infos { cargo_toml.extend_from_slice(b" { name = \""); - cargo_toml.extend_from_slice(exercise.name.as_bytes()); - cargo_toml.extend_from_slice(b"\", path = \""); - cargo_toml.extend_from_slice(exercise.path.to_str().unwrap().as_bytes()); - cargo_toml.extend_from_slice(b"\" },\n"); + cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); + cargo_toml.extend_from_slice(b"\", path = \"exercises/"); + if let Some(dir) = &exercise_info.dir { + cargo_toml.extend_from_slice(dir.as_bytes()); + cargo_toml.extend_from_slice(b"/"); + } + cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); + cargo_toml.extend_from_slice(b".rs\" },\n"); } cargo_toml.extend_from_slice( @@ -54,7 +58,7 @@ fn create_vscode_dir() -> Result<()> { Ok(()) } -pub fn init(exercises: &[Exercise]) -> Result<()> { +pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { bail!(PROBABLY_IN_RUSTLINGS_DIR_ERR); } @@ -74,7 +78,8 @@ pub fn init(exercises: &[Exercise]) -> Result<()> { .init_exercises_dir() .context("Failed to initialize the `rustlings/exercises` directory")?; - create_cargo_toml(exercises).context("Failed to create the file `rustlings/Cargo.toml`")?; + create_cargo_toml(exercise_infos) + .context("Failed to create the file `rustlings/Cargo.toml`")?; create_gitignore().context("Failed to create the file `rustlings/.gitignore`")?; diff --git a/src/list.rs b/src/list.rs index de120eaf64..2bb813d8d4 100644 --- a/src/list.rs +++ b/src/list.rs @@ -5,7 +5,7 @@ use crossterm::{ ExecutableCommand, }; use ratatui::{backend::CrosstermBackend, Terminal}; -use std::{fmt::Write, io}; +use std::io; mod state; @@ -72,14 +72,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> { ui_state.message.push_str(message); } KeyCode::Char('r') => { - let Some(exercise) = ui_state.reset_selected()? else { - continue; - }; - - ui_state = ui_state.with_updated_rows(); - ui_state - .message - .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; + ui_state = ui_state.with_reset_selected()?; } KeyCode::Char('c') => { ui_state.selected_to_current_exercise()?; diff --git a/src/list/state.rs b/src/list/state.rs index 0dcfe88a3b..38391a4940 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -6,8 +6,9 @@ use ratatui::{ widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, Frame, }; +use std::fmt::Write; -use crate::{app_state::AppState, exercise::Exercise, progress_bar::progress_bar_ratatui}; +use crate::{app_state::AppState, progress_bar::progress_bar_ratatui}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -34,10 +35,9 @@ impl<'a> UiState<'a> { .app_state .exercises() .iter() - .zip(self.app_state.progress().iter().copied()) .enumerate() - .filter_map(|(ind, (exercise, done))| { - let exercise_state = if done { + .filter_map(|(ind, exercise)| { + let exercise_state = if exercise.done { if self.filter == Filter::Pending { return None; } @@ -62,7 +62,7 @@ impl<'a> UiState<'a> { Some(Row::new([ next, exercise_state, - Span::raw(&exercise.name), + Span::raw(exercise.name), Span::raw(exercise.path.to_string_lossy()), ])) }); @@ -212,29 +212,30 @@ impl<'a> UiState<'a> { Ok(()) } - pub fn reset_selected(&mut self) -> Result> { + pub fn with_reset_selected(mut self) -> Result { let Some(selected) = self.table_state.selected() else { - return Ok(None); + return Ok(self); }; let (ind, exercise) = self .app_state .exercises() .iter() - .zip(self.app_state.progress()) .enumerate() - .filter_map(|(ind, (exercise, done))| match self.filter { - Filter::Done => done.then_some((ind, exercise)), - Filter::Pending => (!done).then_some((ind, exercise)), + .filter_map(|(ind, exercise)| match self.filter { + Filter::Done => exercise.done.then_some((ind, exercise)), + Filter::Pending => (!exercise.done).then_some((ind, exercise)), Filter::None => Some((ind, exercise)), }) .nth(selected) .context("Invalid selection index")?; - self.app_state.set_pending(ind)?; exercise.reset()?; + self.message + .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; + self.app_state.set_pending(ind)?; - Ok(Some(exercise)) + Ok(self.with_updated_rows()) } pub fn selected_to_current_exercise(&mut self) -> Result<()> { @@ -244,12 +245,12 @@ impl<'a> UiState<'a> { let ind = self .app_state - .progress() + .exercises() .iter() .enumerate() - .filter_map(|(ind, done)| match self.filter { - Filter::Done => done.then_some(ind), - Filter::Pending => (!done).then_some(ind), + .filter_map(|(ind, exercise)| match self.filter { + Filter::Done => exercise.done.then_some(ind), + Filter::Pending => (!exercise.done).then_some(ind), Filter::None => Some(ind), }) .nth(selected) diff --git a/src/main.rs b/src/main.rs index cdfa21f62a..a96e323098 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use std::{path::Path, process::exit}; mod app_state; mod embedded; mod exercise; +mod info_file; mod init; mod list; mod progress_bar; @@ -13,7 +14,7 @@ mod watch; use self::{ app_state::AppState, - exercise::InfoFile, + info_file::InfoFile, init::init, list::list, run::run, @@ -54,12 +55,10 @@ fn main() -> Result<()> { which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; - let mut info_file = InfoFile::parse()?; - info_file.exercises.shrink_to_fit(); - let exercises = info_file.exercises; + let info_file = InfoFile::parse()?; if matches!(args.command, Some(Subcommands::Init)) { - init(&exercises).context("Initialization failed")?; + init(&info_file.exercises).context("Initialization failed")?; println!("{POST_INIT_MSG}"); return Ok(()); @@ -68,18 +67,29 @@ fn main() -> Result<()> { exit(1); } - let mut app_state = AppState::new(exercises, info_file.final_message.unwrap_or_default()); + let mut app_state = AppState::new(info_file); match args.command { - None => loop { - match watch(&mut app_state)? { - WatchExit::Shutdown => break, - // It is much easier to exit the watch mode, launch the list mode and then restart - // the watch mode instead of trying to pause the watch threads and correct the - // watch state. - WatchExit::List => list(&mut app_state)?, + None => { + // For the the notify event handler thread. + // Leaking is not a problem because the slice lives until the end of the program. + let exercise_paths = app_state + .exercises() + .iter() + .map(|exercise| exercise.path) + .collect::>() + .leak(); + + loop { + match watch(&mut app_state, exercise_paths)? { + WatchExit::Shutdown => break, + // It is much easier to exit the watch mode, launch the list mode and then restart + // the watch mode instead of trying to pause the watch threads and correct the + // watch state. + WatchExit::List => list(&mut app_state)?, + } } - }, + } // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { @@ -90,10 +100,10 @@ fn main() -> Result<()> { } Some(Subcommands::Reset { name }) => { app_state.set_current_exercise_by_name(&name)?; - app_state.set_pending(app_state.current_exercise_ind())?; let exercise = app_state.current_exercise(); exercise.reset()?; println!("The exercise {exercise} has been reset!"); + app_state.set_pending(app_state.current_exercise_ind())?; } Some(Subcommands::Hint { name }) => { app_state.set_current_exercise_by_name(&name)?; diff --git a/src/run.rs b/src/run.rs index 4748549245..9c504b5345 100644 --- a/src/run.rs +++ b/src/run.rs @@ -17,7 +17,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { if !output.status.success() { app_state.set_pending(app_state.current_exercise_ind())?; - bail!("Ran {exercise} with errors"); + bail!("Ran {} with errors", app_state.current_exercise()); } stdout.write_fmt(format_args!( diff --git a/src/watch.rs b/src/watch.rs index beb69b3d9d..58e829f34a 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -11,14 +11,14 @@ use std::{ time::Duration, }; -mod debounce_event; +mod notify_event; mod state; mod terminal_event; use crate::app_state::{AppState, ExercisesProgress}; use self::{ - debounce_event::DebounceEventHandler, + notify_event::DebounceEventHandler, state::WatchState, terminal_event::{terminal_event_handler, InputEvent}, }; @@ -40,13 +40,16 @@ pub enum WatchExit { List, } -pub fn watch(app_state: &mut AppState) -> Result { +pub fn watch( + app_state: &mut AppState, + exercise_paths: &'static [&'static Path], +) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), DebounceEventHandler { tx: tx.clone(), - exercises: app_state.exercises(), + exercise_paths, }, )?; debouncer @@ -85,10 +88,10 @@ pub fn watch(app_state: &mut AppState) -> Result { watch_state.render()?; } WatchEvent::NotifyErr(e) => { - return Err(Error::from(e).context("Exercise file watcher failed")) + return Err(Error::from(e).context("Exercise file watcher failed")); } WatchEvent::TerminalEventErr(e) => { - return Err(Error::from(e).context("Terminal event listener failed")) + return Err(Error::from(e).context("Terminal event listener failed")); } } } diff --git a/src/watch/debounce_event.rs b/src/watch/notify_event.rs similarity index 84% rename from src/watch/debounce_event.rs rename to src/watch/notify_event.rs index 1dc92cb49b..0c8d669226 100644 --- a/src/watch/debounce_event.rs +++ b/src/watch/notify_event.rs @@ -1,13 +1,11 @@ use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; -use std::sync::mpsc::Sender; - -use crate::exercise::Exercise; +use std::{path::Path, sync::mpsc::Sender}; use super::WatchEvent; pub struct DebounceEventHandler { pub tx: Sender, - pub exercises: &'static [Exercise], + pub exercise_paths: &'static [&'static Path], } impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { @@ -23,9 +21,9 @@ impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { return None; } - self.exercises + self.exercise_paths .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) + .position(|path| event.path.ends_with(path)) }) .min() else { From bee62c89de09fdd9823cba81e07f0f8528fe8ef9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 02:41:19 +0200 Subject: [PATCH 0707/1432] Add terminal links --- src/app_state.rs | 2 +- src/app_state/state_file.rs | 8 +++----- src/embedded.rs | 7 ++++++- src/exercise.rs | 34 +++++++++++++++++++++++++++++++--- src/info_file.rs | 10 ++++------ src/list/state.rs | 2 +- src/run.rs | 12 +++++++++--- src/watch.rs | 2 +- src/watch/notify_event.rs | 4 ++-- src/watch/state.rs | 6 +----- 10 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 1a051b9766..98c6384260 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -38,7 +38,7 @@ impl AppState { // Leaking to be able to borrow in the watch mode `Table`. // Leaking is not a problem because the `AppState` instance lives until // the end of the program. - let path = Box::leak(exercise_info.path().into_boxed_path()); + let path = exercise_info.path().leak(); exercise_info.name.shrink_to_fit(); let name = exercise_info.name.leak(); diff --git a/src/app_state/state_file.rs b/src/app_state/state_file.rs index 364a1fa382..4e4a0e1565 100644 --- a/src/app_state/state_file.rs +++ b/src/app_state/state_file.rs @@ -59,7 +59,7 @@ pub fn write(app_state: &AppState) -> Result<()> { exercises: ExercisesStateSerializer(&app_state.exercises), }; - let mut buf = Vec::with_capacity(1024); + let mut buf = Vec::with_capacity(4096); serde_json::ser::to_writer(&mut buf, &content).context("Failed to serialize the state")?; fs::write(STATE_FILE_NAME, buf) .with_context(|| format!("Failed to write the state file `{STATE_FILE_NAME}`"))?; @@ -69,8 +69,6 @@ pub fn write(app_state: &AppState) -> Result<()> { #[cfg(test)] mod tests { - use std::path::Path; - use crate::info_file::Mode; use super::*; @@ -81,14 +79,14 @@ mod tests { let exercises = [ Exercise { name: "1", - path: Path::new("exercises/1.rs"), + path: "exercises/1.rs", mode: Mode::Run, hint: String::new(), done: true, }, Exercise { name: "2", - path: Path::new("exercises/2.rs"), + path: "exercises/2.rs", mode: Mode::Test, hint: String::new(), done: false, diff --git a/src/embedded.rs b/src/embedded.rs index 1e2d677037..866b12b861 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -91,7 +91,12 @@ impl EmbeddedFiles { Ok(()) } - pub fn write_exercise_to_disk(&self, path: &Path, strategy: WriteStrategy) -> io::Result<()> { + pub fn write_exercise_to_disk

(&self, path: P, strategy: WriteStrategy) -> io::Result<()> + where + P: AsRef, + { + let path = path.as_ref(); + if let Some(file) = self .exercises_dir .files diff --git a/src/exercise.rs b/src/exercise.rs index c5ece5f5f7..2ec8d979d3 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,7 +1,8 @@ use anyhow::{Context, Result}; +use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, - path::Path, + fs, process::{Command, Output}, }; @@ -10,11 +11,32 @@ use crate::{ info_file::Mode, }; +pub struct TerminalFileLink<'a> { + path: &'a str, +} + +impl<'a> Display for TerminalFileLink<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Ok(Some(canonical_path)) = fs::canonicalize(self.path) + .as_deref() + .map(|path| path.to_str()) + { + write!( + f, + "\x1b]8;;file://{}\x1b\\{}\x1b]8;;\x1b\\", + canonical_path, self.path, + ) + } else { + write!(f, "{}", self.path,) + } + } +} + pub struct Exercise { // Exercise's unique name pub name: &'static str, // Exercise's path - pub path: &'static Path, + pub path: &'static str, // The mode of the exercise pub mode: Mode, // The hint text associated with the exercise @@ -60,10 +82,16 @@ impl Exercise { .write_exercise_to_disk(self.path, WriteStrategy::Overwrite) .with_context(|| format!("Failed to reset the exercise {self}")) } + + pub fn terminal_link(&self) -> StyledContent> { + style(TerminalFileLink { path: self.path }) + .underlined() + .blue() + } } impl Display for Exercise { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(&self.path.display(), f) + self.path.fmt(f) } } diff --git a/src/info_file.rs b/src/info_file.rs index dc97b9262a..2a45e02de7 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; -use std::{fs, path::PathBuf}; +use std::fs; // The mode of the exercise. #[derive(Deserialize, Copy, Clone)] @@ -28,14 +28,12 @@ pub struct ExerciseInfo { } impl ExerciseInfo { - pub fn path(&self) -> PathBuf { - let path = if let Some(dir) = &self.dir { + pub fn path(&self) -> String { + if let Some(dir) = &self.dir { format!("exercises/{dir}/{}.rs", self.name) } else { format!("exercises/{}.rs", self.name) - }; - - PathBuf::from(path) + } } } diff --git a/src/list/state.rs b/src/list/state.rs index 38391a4940..2a1fef18f9 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -63,7 +63,7 @@ impl<'a> UiState<'a> { next, exercise_state, Span::raw(exercise.name), - Span::raw(exercise.path.to_string_lossy()), + Span::raw(exercise.path), ])) }); diff --git a/src/run.rs b/src/run.rs index 9c504b5345..863b584efb 100644 --- a/src/run.rs +++ b/src/run.rs @@ -17,18 +17,24 @@ pub fn run(app_state: &mut AppState) -> Result<()> { if !output.status.success() { app_state.set_pending(app_state.current_exercise_ind())?; - bail!("Ran {} with errors", app_state.current_exercise()); + bail!( + "Ran {} with errors", + app_state.current_exercise().terminal_link(), + ); } stdout.write_fmt(format_args!( "{}{}\n", "βœ“ Successfully ran ".green(), - exercise.path.to_string_lossy().green(), + exercise.path.green(), ))?; match app_state.done_current_exercise(&mut stdout)? { ExercisesProgress::AllDone => (), - ExercisesProgress::Pending => println!("Next exercise: {}", app_state.current_exercise()), + ExercisesProgress::Pending => println!( + "Next exercise: {}", + app_state.current_exercise().terminal_link(), + ), } Ok(()) diff --git a/src/watch.rs b/src/watch.rs index 58e829f34a..bab64ae1cf 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -42,7 +42,7 @@ pub enum WatchExit { pub fn watch( app_state: &mut AppState, - exercise_paths: &'static [&'static Path], + exercise_paths: &'static [&'static str], ) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index 0c8d669226..fb9a8c05cd 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -1,11 +1,11 @@ use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; -use std::{path::Path, sync::mpsc::Sender}; +use std::sync::mpsc::Sender; use super::WatchEvent; pub struct DebounceEventHandler { pub tx: Sender, - pub exercise_paths: &'static [&'static Path], + pub exercise_paths: &'static [&'static str], } impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { diff --git a/src/watch/state.rs b/src/watch/state.rs index 6a97637b9e..1a79573fb5 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -136,11 +136,7 @@ When you are done experimenting, enter `n` or `next` to go to the next exercise )?; self.writer.write_fmt(format_args!( "{progress_bar}Current exercise: {}\n", - self.app_state - .current_exercise() - .path - .to_string_lossy() - .bold(), + self.app_state.current_exercise().terminal_link(), ))?; self.show_prompt()?; From 9831cbb13975cd0f5ee4c295156102e3573ede1a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 03:13:33 +0200 Subject: [PATCH 0708/1432] Fix tests --- gen-dev-cargo-toml/src/main.rs | 24 +++++++++++--------- tests/dev_cargo_bins.rs | 39 +++++++++++++++++++-------------- tests/fixture/failure/info.toml | 4 +--- tests/fixture/state/info.toml | 7 ++---- tests/fixture/success/info.toml | 4 +--- 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs index 9a7c1bbda8..792fe5f0bc 100644 --- a/gen-dev-cargo-toml/src/main.rs +++ b/gen-dev-cargo-toml/src/main.rs @@ -10,18 +10,18 @@ use std::{ }; #[derive(Deserialize)] -struct Exercise { +struct ExerciseInfo { name: String, - path: String, + dir: Option, } #[derive(Deserialize)] -struct InfoToml { - exercises: Vec, +struct InfoFile { + exercises: Vec, } fn main() -> Result<()> { - let exercises = toml_edit::de::from_str::( + let exercise_infos = toml_edit::de::from_str::( &fs::read_to_string("info.toml").context("Failed to read `info.toml`")?, ) .context("Failed to deserialize `info.toml`")? @@ -36,12 +36,16 @@ fn main() -> Result<()> { bin = [\n", ); - for exercise in exercises { + for exercise_info in exercise_infos { buf.extend_from_slice(b" { name = \""); - buf.extend_from_slice(exercise.name.as_bytes()); - buf.extend_from_slice(b"\", path = \"../"); - buf.extend_from_slice(exercise.path.as_bytes()); - buf.extend_from_slice(b"\" },\n"); + 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.extend_from_slice(b"/"); + } + buf.extend_from_slice(exercise_info.name.as_bytes()); + buf.extend_from_slice(b".rs\" },\n"); } buf.extend_from_slice( diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs index c3faea92cc..81f48b1e1a 100644 --- a/tests/dev_cargo_bins.rs +++ b/tests/dev_cargo_bins.rs @@ -5,34 +5,39 @@ use serde::Deserialize; use std::fs; #[derive(Deserialize)] -struct Exercise { +struct ExerciseInfo { name: String, - path: String, + dir: Option, } #[derive(Deserialize)] -struct InfoToml { - exercises: Vec, +struct InfoFile { + exercises: Vec, } #[test] fn dev_cargo_bins() { - let content = fs::read_to_string("dev/Cargo.toml").unwrap(); + let cargo_toml = fs::read_to_string("dev/Cargo.toml").unwrap(); - let exercises = toml_edit::de::from_str::(&fs::read_to_string("info.toml").unwrap()) - .unwrap() - .exercises; + let exercise_infos = + toml_edit::de::from_str::(&fs::read_to_string("info.toml").unwrap()) + .unwrap() + .exercises; let mut start_ind = 0; - for exercise in exercises { - let name_start = start_ind + content[start_ind..].find('"').unwrap() + 1; - let name_end = name_start + content[name_start..].find('"').unwrap(); - assert_eq!(exercise.name, &content[name_start..name_end]); - - // +3 to skip `../` at the begeinning of the path. - let path_start = name_end + content[name_end + 1..].find('"').unwrap() + 5; - let path_end = path_start + content[path_start..].find('"').unwrap(); - assert_eq!(exercise.path, &content[path_start..path_end]); + 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 9474ee3f21..94ec6ead61 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -1,11 +1,9 @@ [[exercises]] name = "compFailure" -path = "exercises/compFailure.rs" -mode = "compile" +mode = "run" hint = "" [[exercises]] name = "testFailure" -path = "exercises/testFailure.rs" mode = "test" hint = "Hello!" diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index 8de5d604f9..e5c4d8f407 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -1,17 +1,14 @@ [[exercises]] name = "pending_exercise" -path = "exercises/pending_exercise.rs" -mode = "compile" +mode = "run" hint = """""" [[exercises]] name = "pending_test_exercise" -path = "exercises/pending_test_exercise.rs" mode = "test" hint = """""" [[exercises]] name = "finished_exercise" -path = "exercises/finished_exercise.rs" -mode = "compile" +mode = "run" hint = """""" diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index 17ed8c6229..674ba26434 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -1,11 +1,9 @@ [[exercises]] name = "compSuccess" -path = "exercises/compSuccess.rs" -mode = "compile" +mode = "run" hint = """""" [[exercises]] name = "testSuccess" -path = "exercises/testSuccess.rs" mode = "test" hint = """""" From 9dcc4b7df5f539b10117e97870a9f1cb01ca040d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 05:13:27 +0200 Subject: [PATCH 0709/1432] Simplify the state file --- .gitignore | 2 +- Cargo.lock | 12 ---- Cargo.toml | 1 - src/app_state.rs | 128 +++++++++++++++++++++++------------- src/app_state/state_file.rs | 110 ------------------------------- src/init.rs | 2 +- 6 files changed, 86 insertions(+), 169 deletions(-) delete mode 100644 src/app_state/state_file.rs diff --git a/.gitignore b/.gitignore index c9172e01a2..80f9092abb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ target/ /dev/Cargo.lock # State file -.rustlings-state.json +.rustlings-state.txt # oranda public/ diff --git a/Cargo.lock b/Cargo.lock index dbf1923e7d..6bc68f0d25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,7 +690,6 @@ dependencies = [ "ratatui", "rustlings-macros", "serde", - "serde_json", "toml_edit", "which", ] @@ -749,17 +748,6 @@ dependencies = [ "syn 2.0.58", ] -[[package]] -name = "serde_json" -version = "1.0.115" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_spanned" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index 14ae9a1439..07865abcce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,6 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } -serde_json = "1.0.115" serde.workspace = true toml_edit.workspace = true which = "6.0.1" diff --git a/src/app_state.rs b/src/app_state.rs index 98c6384260..9a378de6bd 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -4,15 +4,14 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; -use std::io::{StdoutLock, Write}; - -mod state_file; +use std::{ + fs::{self, File}, + io::{Read, StdoutLock, Write}, +}; use crate::{exercise::Exercise, info_file::InfoFile, FENISH_LINE}; -use self::state_file::{write, StateFileDeser}; - -const STATE_FILE_NAME: &str = ".rustlings-state.json"; +const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; #[must_use] @@ -27,11 +26,51 @@ pub struct AppState { n_done: u16, welcome_message: String, final_message: String, + file_buf: Vec, } impl AppState { + fn update_from_file(&mut self) { + self.file_buf.clear(); + self.n_done = 0; + + if File::open(STATE_FILE_NAME) + .and_then(|mut file| file.read_to_end(&mut self.file_buf)) + .is_ok() + { + let mut lines = self.file_buf.split(|c| *c == b'\n'); + let Some(current_exercise_name) = lines.next() else { + return; + }; + + if lines.next().is_none() { + return; + } + + let mut done_exercises = hashbrown::HashSet::with_capacity(self.exercises.len()); + + for done_exerise_name in lines { + if done_exerise_name.is_empty() { + break; + } + done_exercises.insert(done_exerise_name); + } + + for (ind, exercise) in self.exercises.iter_mut().enumerate() { + if done_exercises.contains(exercise.name.as_bytes()) { + exercise.done = true; + self.n_done += 1; + } + + if exercise.name.as_bytes() == current_exercise_name { + self.current_exercise_ind = ind; + } + } + } + } + pub fn new(info_file: InfoFile) -> Self { - let mut exercises = info_file + let exercises = info_file .exercises .into_iter() .map(|mut exercise_info| { @@ -55,42 +94,18 @@ impl AppState { }) .collect::>(); - let (current_exercise_ind, n_done) = StateFileDeser::read().map_or((0, 0), |state_file| { - let mut state_file_exercises = - hashbrown::HashMap::with_capacity(state_file.exercises.len()); - - for (ind, exercise_state) in state_file.exercises.into_iter().enumerate() { - state_file_exercises.insert( - exercise_state.name, - (ind == state_file.current_exercise_ind, exercise_state.done), - ); - } - - let mut current_exercise_ind = 0; - let mut n_done = 0; - for (ind, exercise) in exercises.iter_mut().enumerate() { - if let Some((current, done)) = state_file_exercises.get(exercise.name) { - if *done { - exercise.done = true; - n_done += 1; - } - - if *current { - current_exercise_ind = ind; - } - } - } - - (current_exercise_ind, n_done) - }); - - Self { - current_exercise_ind, + let mut slf = Self { + current_exercise_ind: 0, exercises, - n_done, + n_done: 0, welcome_message: info_file.welcome_message.unwrap_or_default(), final_message: info_file.final_message.unwrap_or_default(), - } + file_buf: Vec::with_capacity(2048), + }; + + slf.update_from_file(); + + slf } #[inline] @@ -120,7 +135,7 @@ impl AppState { self.current_exercise_ind = ind; - write(self) + self.write() } pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> { @@ -132,7 +147,7 @@ impl AppState { .position(|exercise| exercise.name == name) .with_context(|| format!("No exercise found for '{name}'!"))?; - write(self) + self.write() } pub fn set_pending(&mut self, ind: usize) -> Result<()> { @@ -141,7 +156,7 @@ impl AppState { if exercise.done { exercise.done = false; self.n_done -= 1; - write(self)?; + self.write()?; } Ok(()) @@ -193,7 +208,7 @@ impl AppState { self.exercises[exercise_ind].done = false; self.n_done -= 1; - write(self)?; + self.write()?; return Ok(ExercisesProgress::Pending); } @@ -213,6 +228,31 @@ impl AppState { Ok(ExercisesProgress::Pending) } + + // Write the state file. + // The file's format is very simple: + // - The first line is the name of the current exercise. + // - The second line is an empty line. + // - All remaining lines are the names of done exercises. + fn write(&mut self) -> Result<()> { + self.file_buf.clear(); + + self.file_buf + .extend_from_slice(self.current_exercise().name.as_bytes()); + self.file_buf.extend_from_slice(b"\n\n"); + + for exercise in &self.exercises { + if exercise.done { + self.file_buf.extend_from_slice(exercise.name.as_bytes()); + self.file_buf.extend_from_slice(b"\n"); + } + } + + fs::write(STATE_FILE_NAME, &self.file_buf) + .with_context(|| format!("Failed to write the state file {STATE_FILE_NAME}"))?; + + Ok(()) + } } const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" diff --git a/src/app_state/state_file.rs b/src/app_state/state_file.rs deleted file mode 100644 index 4e4a0e1565..0000000000 --- a/src/app_state/state_file.rs +++ /dev/null @@ -1,110 +0,0 @@ -use anyhow::{Context, Result}; -use serde::{Deserialize, Serialize}; -use std::fs; - -use crate::exercise::Exercise; - -use super::{AppState, STATE_FILE_NAME}; - -#[derive(Deserialize)] -pub struct ExerciseStateDeser { - pub name: String, - pub done: bool, -} - -#[derive(Serialize)] -struct ExerciseStateSer<'a> { - name: &'a str, - done: bool, -} - -struct ExercisesStateSerializer<'a>(&'a [Exercise]); - -impl<'a> Serialize for ExercisesStateSerializer<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let iter = self.0.iter().map(|exercise| ExerciseStateSer { - name: exercise.name, - done: exercise.done, - }); - - serializer.collect_seq(iter) - } -} - -#[derive(Deserialize)] -pub struct StateFileDeser { - pub current_exercise_ind: usize, - pub exercises: Vec, -} - -#[derive(Serialize)] -struct StateFileSer<'a> { - current_exercise_ind: usize, - exercises: ExercisesStateSerializer<'a>, -} - -impl StateFileDeser { - pub fn read() -> Option { - let file_content = fs::read(STATE_FILE_NAME).ok()?; - serde_json::de::from_slice(&file_content).ok() - } -} - -pub fn write(app_state: &AppState) -> Result<()> { - let content = StateFileSer { - current_exercise_ind: app_state.current_exercise_ind, - exercises: ExercisesStateSerializer(&app_state.exercises), - }; - - let mut buf = Vec::with_capacity(4096); - serde_json::ser::to_writer(&mut buf, &content).context("Failed to serialize the state")?; - fs::write(STATE_FILE_NAME, buf) - .with_context(|| format!("Failed to write the state file `{STATE_FILE_NAME}`"))?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use crate::info_file::Mode; - - use super::*; - - #[test] - fn ser_deser_sync() { - let current_exercise_ind = 1; - let exercises = [ - Exercise { - name: "1", - path: "exercises/1.rs", - mode: Mode::Run, - hint: String::new(), - done: true, - }, - Exercise { - name: "2", - path: "exercises/2.rs", - mode: Mode::Test, - hint: String::new(), - done: false, - }, - ]; - - let ser = StateFileSer { - current_exercise_ind, - exercises: ExercisesStateSerializer(&exercises), - }; - let deser: StateFileDeser = - serde_json::de::from_slice(&serde_json::ser::to_vec(&ser).unwrap()).unwrap(); - - assert_eq!(deser.current_exercise_ind, current_exercise_ind); - assert!(deser - .exercises - .iter() - .zip(exercises) - .all(|(deser, ser)| deser.name == ser.name && deser.done == ser.done)); - } -} diff --git a/src/init.rs b/src/init.rs index 2badf3767e..4ee503a2b4 100644 --- a/src/init.rs +++ b/src/init.rs @@ -89,7 +89,7 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { } const GITIGNORE: &[u8] = b"/target -/.rustlings-state.json +/.rustlings-state.txt "; const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; From 1c90575b9fe0f0fb32006e000aefff10d8a4a39c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 05:13:50 +0200 Subject: [PATCH 0710/1432] Update deps --- Cargo.lock | 59 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6bc68f0d25..5cfebe603b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,9 +261,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "equivalent" @@ -1000,7 +1000,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1020,17 +1020,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1041,9 +1042,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1053,9 +1054,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1065,9 +1066,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1077,9 +1084,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1089,9 +1096,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1101,9 +1108,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -1113,9 +1120,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" From 3da860927d131eacc288764672ed8799a6a8cfca Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 14:53:32 +0200 Subject: [PATCH 0711/1432] Use push instead of extend_from_slice on chars --- gen-dev-cargo-toml/src/main.rs | 2 +- src/app_state.rs | 2 +- src/init.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs index 792fe5f0bc..43b4ebd7a5 100644 --- a/gen-dev-cargo-toml/src/main.rs +++ b/gen-dev-cargo-toml/src/main.rs @@ -42,7 +42,7 @@ bin = [\n", buf.extend_from_slice(b"\", path = \"../exercises/"); if let Some(dir) = &exercise_info.dir { buf.extend_from_slice(dir.as_bytes()); - buf.extend_from_slice(b"/"); + buf.push(b'/'); } buf.extend_from_slice(exercise_info.name.as_bytes()); buf.extend_from_slice(b".rs\" },\n"); diff --git a/src/app_state.rs b/src/app_state.rs index 9a378de6bd..31cb2cbf46 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -244,7 +244,7 @@ impl AppState { for exercise in &self.exercises { if exercise.done { self.file_buf.extend_from_slice(exercise.name.as_bytes()); - self.file_buf.extend_from_slice(b"\n"); + self.file_buf.push(b'\n'); } } diff --git a/src/init.rs b/src/init.rs index 4ee503a2b4..459519decd 100644 --- a/src/init.rs +++ b/src/init.rs @@ -17,7 +17,7 @@ fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { cargo_toml.extend_from_slice(b"\", path = \"exercises/"); if let Some(dir) = &exercise_info.dir { cargo_toml.extend_from_slice(dir.as_bytes()); - cargo_toml.extend_from_slice(b"/"); + cargo_toml.push(b'/'); } cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); cargo_toml.extend_from_slice(b".rs\" },\n"); From 8aef915ee732af1480cd7b93818f7d71c3ba178c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 16:03:49 +0200 Subject: [PATCH 0712/1432] Show the welcome message --- src/app_state.rs | 81 +++++++++++++++++++++++++++--------------------- src/main.rs | 32 +++++++++++++++++-- 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 31cb2cbf46..fb4b92e79d 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -9,7 +9,7 @@ use std::{ io::{Read, StdoutLock, Write}, }; -use crate::{exercise::Exercise, info_file::InfoFile, FENISH_LINE}; +use crate::{exercise::Exercise, info_file::ExerciseInfo, FENISH_LINE}; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -20,58 +20,69 @@ pub enum ExercisesProgress { Pending, } +pub enum StateFileStatus { + Read, + NotRead, +} + pub struct AppState { current_exercise_ind: usize, exercises: Vec, n_done: u16, - welcome_message: String, final_message: String, file_buf: Vec, } impl AppState { - fn update_from_file(&mut self) { + fn update_from_file(&mut self) -> StateFileStatus { self.file_buf.clear(); self.n_done = 0; if File::open(STATE_FILE_NAME) .and_then(|mut file| file.read_to_end(&mut self.file_buf)) - .is_ok() + .is_err() { - let mut lines = self.file_buf.split(|c| *c == b'\n'); - let Some(current_exercise_name) = lines.next() else { - return; - }; + return StateFileStatus::NotRead; + } - if lines.next().is_none() { - return; - } + // See `Self::write` for more information about the file format. + let mut lines = self.file_buf.split(|c| *c == b'\n'); + let Some(current_exercise_name) = lines.next() else { + return StateFileStatus::NotRead; + }; + + if current_exercise_name.is_empty() || lines.next().is_none() { + return StateFileStatus::NotRead; + } - let mut done_exercises = hashbrown::HashSet::with_capacity(self.exercises.len()); + let mut done_exercises = hashbrown::HashSet::with_capacity(self.exercises.len()); - for done_exerise_name in lines { - if done_exerise_name.is_empty() { - break; - } - done_exercises.insert(done_exerise_name); + for done_exerise_name in lines { + if done_exerise_name.is_empty() { + break; } + done_exercises.insert(done_exerise_name); + } - for (ind, exercise) in self.exercises.iter_mut().enumerate() { - if done_exercises.contains(exercise.name.as_bytes()) { - exercise.done = true; - self.n_done += 1; - } + for (ind, exercise) in self.exercises.iter_mut().enumerate() { + if done_exercises.contains(exercise.name.as_bytes()) { + exercise.done = true; + self.n_done += 1; + } - if exercise.name.as_bytes() == current_exercise_name { - self.current_exercise_ind = ind; - } + if exercise.name.as_bytes() == current_exercise_name { + self.current_exercise_ind = ind; } } + + StateFileStatus::Read } - pub fn new(info_file: InfoFile) -> Self { - let exercises = info_file - .exercises + pub fn new( + exercise_infos: Vec, + final_message: String, + ) -> (Self, StateFileStatus) { + let exercises = exercise_infos .into_iter() .map(|mut exercise_info| { // Leaking to be able to borrow in the watch mode `Table`. @@ -98,14 +109,13 @@ impl AppState { current_exercise_ind: 0, exercises, n_done: 0, - welcome_message: info_file.welcome_message.unwrap_or_default(), - final_message: info_file.final_message.unwrap_or_default(), + final_message, file_buf: Vec::with_capacity(2048), }; - slf.update_from_file(); + let state_file_status = slf.update_from_file(); - slf + (slf, state_file_status) } #[inline] @@ -231,7 +241,8 @@ impl AppState { // Write the state file. // The file's format is very simple: - // - The first line is the name of the current exercise. + // - The first line is the name of the current exercise. It must end with `\n` even if there + // are no done exercises. // - The second line is an empty line. // - All remaining lines are the names of done exercises. fn write(&mut self) -> Result<()> { @@ -239,12 +250,12 @@ impl AppState { self.file_buf .extend_from_slice(self.current_exercise().name.as_bytes()); - self.file_buf.extend_from_slice(b"\n\n"); + self.file_buf.push(b'\n'); for exercise in &self.exercises { if exercise.done { - self.file_buf.extend_from_slice(exercise.name.as_bytes()); self.file_buf.push(b'\n'); + self.file_buf.extend_from_slice(exercise.name.as_bytes()); } } diff --git a/src/main.rs b/src/main.rs index a96e323098..aeb9432110 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,15 @@ use anyhow::{Context, Result}; +use app_state::StateFileStatus; use clap::{Parser, Subcommand}; -use std::{path::Path, process::exit}; +use crossterm::{ + terminal::{Clear, ClearType}, + ExecutableCommand, +}; +use std::{ + io::{self, BufRead, Write}, + path::Path, + process::exit, +}; mod app_state; mod embedded; @@ -67,7 +76,26 @@ fn main() -> Result<()> { exit(1); } - let mut app_state = AppState::new(info_file); + let (mut app_state, state_file_status) = AppState::new( + info_file.exercises, + info_file.final_message.unwrap_or_default(), + ); + + if let Some(welcome_message) = info_file.welcome_message { + match state_file_status { + StateFileStatus::NotRead => { + let mut stdout = io::stdout().lock(); + stdout.execute(Clear(ClearType::All))?; + + let welcome_message = welcome_message.trim(); + write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; + stdout.flush()?; + + io::stdin().lock().read_until(b'\n', &mut Vec::new())?; + } + StateFileStatus::Read => (), + } + } match args.command { None => { From 070a780d7f7ca4ef03ab29898ec553933994bfab Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 16:04:05 +0200 Subject: [PATCH 0713/1432] Trim the final message --- src/app_state.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index fb4b92e79d..432a9a2746 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -228,8 +228,12 @@ impl AppState { writer.execute(Clear(ClearType::All))?; writer.write_all(FENISH_LINE.as_bytes())?; - writer.write_all(self.final_message.as_bytes())?; - writer.write_all(b"\n")?; + + let final_message = self.final_message.trim(); + if !final_message.is_empty() { + writer.write_all(self.final_message.as_bytes())?; + writer.write_all(b"\n")?; + } return Ok(ExercisesProgress::AllDone); }; From bd10b154fe558af693e9f8f57dbb3e43f0bd0ec8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 16:07:17 +0200 Subject: [PATCH 0714/1432] Clear the terminal after showing the welcome message --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index aeb9432110..6796921526 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,6 +92,8 @@ fn main() -> Result<()> { stdout.flush()?; io::stdin().lock().read_until(b'\n', &mut Vec::new())?; + + stdout.execute(Clear(ClearType::All))?; } StateFileStatus::Read => (), } From 1cbabc3d28a29a01caeffba969ed640e00e5f0be Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 17:10:53 +0200 Subject: [PATCH 0715/1432] Add the manual-run option --- src/main.rs | 28 +++++++++++++------- src/watch.rs | 53 ++++++++++++++++++++++++++----------- src/watch/state.rs | 8 +++++- src/watch/terminal_event.rs | 4 ++- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6796921526..28a426b7ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,10 @@ use self::{ struct Args { #[command(subcommand)] command: Option, + /// Manually run the current exercise using `r` or `run` in the watch mode. + /// Only use this if Rustlings fails to detect exercise file changes. + #[arg(long)] + manual_run: bool, } #[derive(Subcommand)] @@ -101,17 +105,23 @@ fn main() -> Result<()> { match args.command { None => { - // For the the notify event handler thread. - // Leaking is not a problem because the slice lives until the end of the program. - let exercise_paths = app_state - .exercises() - .iter() - .map(|exercise| exercise.path) - .collect::>() - .leak(); + let notify_exercise_paths: Option<&'static [&'static str]> = if args.manual_run { + None + } else { + // For the the notify event handler thread. + // Leaking is not a problem because the slice lives until the end of the program. + Some( + app_state + .exercises() + .iter() + .map(|exercise| exercise.path) + .collect::>() + .leak(), + ) + }; loop { - match watch(&mut app_state, exercise_paths)? { + match 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 diff --git a/src/watch.rs b/src/watch.rs index bab64ae1cf..d20e552ef0 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -42,25 +42,38 @@ pub enum WatchExit { pub fn watch( app_state: &mut AppState, - exercise_paths: &'static [&'static str], + notify_exercise_paths: Option<&'static [&'static str]>, ) -> Result { let (tx, rx) = channel(); - let mut debouncer = new_debouncer( - Duration::from_secs(1), - DebounceEventHandler { - tx: tx.clone(), - exercise_paths, - }, - )?; - debouncer - .watcher() - .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - - let mut watch_state = WatchState::new(app_state); + + let mut manual_run = false; + // Prevent dropping the guard until the end of the function. + // Otherwise, the file watcher exits. + let _debouncer_guard = if let Some(exercise_paths) = notify_exercise_paths { + let mut debouncer = new_debouncer( + Duration::from_secs(1), + DebounceEventHandler { + tx: tx.clone(), + exercise_paths, + }, + ) + .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; + debouncer + .watcher() + .watch(Path::new("exercises"), RecursiveMode::Recursive) + .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; + + Some(debouncer) + } else { + manual_run = true; + None + }; + + let mut watch_state = WatchState::new(app_state, manual_run); watch_state.run_current_exercise()?; - thread::spawn(move || terminal_event_handler(tx)); + thread::spawn(move || terminal_event_handler(tx, manual_run)); while let Ok(event) = rx.recv() { match event { @@ -78,6 +91,7 @@ pub fn watch( watch_state.into_writer().write_all(QUIT_MSG)?; break; } + WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise()?, WatchEvent::Input(InputEvent::Unrecognized(cmd)) => { watch_state.handle_invalid_cmd(&cmd)?; } @@ -88,7 +102,8 @@ pub fn watch( watch_state.render()?; } WatchEvent::NotifyErr(e) => { - return Err(Error::from(e).context("Exercise file watcher failed")); + watch_state.into_writer().write_all(NOTIFY_ERR.as_bytes())?; + return Err(Error::from(e)); } WatchEvent::TerminalEventErr(e) => { return Err(Error::from(e).context("Terminal event listener failed")); @@ -103,3 +118,11 @@ const QUIT_MSG: &[u8] = b" We hope you're enjoying learning Rust! If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. "; + +const NOTIFY_ERR: &str = " +The automatic detection of exercise file changes failed :( +Please try running `rustlings` again. + +If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher. +You need to manually trigger running the current exercise using `r` or `run` then. +"; diff --git a/src/watch/state.rs b/src/watch/state.rs index 1a79573fb5..c0f6c532b6 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -18,10 +18,11 @@ pub struct WatchState<'a> { stderr: Option>, show_hint: bool, show_done: bool, + manual_run: bool, } impl<'a> WatchState<'a> { - pub fn new(app_state: &'a mut AppState) -> Self { + pub fn new(app_state: &'a mut AppState, manual_run: bool) -> Self { let writer = io::stdout().lock(); Self { @@ -31,6 +32,7 @@ impl<'a> WatchState<'a> { stderr: None, show_hint: false, show_done: false, + manual_run, } } @@ -78,6 +80,10 @@ impl<'a> WatchState<'a> { fn show_prompt(&mut self) -> io::Result<()> { self.writer.write_all(b"\n")?; + if self.manual_run { + self.writer.write_fmt(format_args!("{}un/", 'r'.bold()))?; + } + if self.show_done { self.writer.write_fmt(format_args!("{}ext/", 'n'.bold()))?; } diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 7f7ebe06f3..6d790b7ced 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -4,6 +4,7 @@ use std::sync::mpsc::Sender; use super::WatchEvent; pub enum InputEvent { + Run, Next, Hint, List, @@ -11,7 +12,7 @@ pub enum InputEvent { Unrecognized(String), } -pub fn terminal_event_handler(tx: Sender) { +pub fn terminal_event_handler(tx: Sender, manual_run: bool) { let mut input = String::with_capacity(8); let last_input_event = loop { @@ -43,6 +44,7 @@ pub fn terminal_event_handler(tx: Sender) { "h" | "hint" => InputEvent::Hint, "l" | "list" => break InputEvent::List, "q" | "quit" => break InputEvent::Quit, + "r" | "run" if manual_run => InputEvent::Run, _ => InputEvent::Unrecognized(input.clone()), }; From 7526c6b1f92626df6ab8b4853535b73711bfada4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 17:11:27 +0200 Subject: [PATCH 0716/1432] Update POST_INIT_MSG --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 28a426b7ed..ed5becf550 100644 --- a/src/main.rs +++ b/src/main.rs @@ -174,7 +174,7 @@ const POST_INIT_MSG: &str = " Done initialization! Run `cd rustlings` to go into the generated directory. -Then run `rustlings` for further instructions on getting started."; +Then run `rustlings` to get started."; const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | From c613b70363c60c6f4305d09c7394c96cdc6b69e4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 17:28:01 +0200 Subject: [PATCH 0717/1432] Print the trimmed final message --- src/app_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_state.rs b/src/app_state.rs index 432a9a2746..54c02d690b 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -231,7 +231,7 @@ impl AppState { let final_message = self.final_message.trim(); if !final_message.is_empty() { - writer.write_all(self.final_message.as_bytes())?; + writer.write_all(final_message.as_bytes())?; writer.write_all(b"\n")?; } From 15ca847c37c170590abe6caa53dba5606d956341 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 15 Apr 2024 02:11:27 +0200 Subject: [PATCH 0718/1432] Implement third-party exercises trust handling --- Cargo.lock | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/init.rs | 4 ++- src/main.rs | 53 ++++++++++++++++++++++++++-------- src/trust.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 12 deletions(-) create mode 100644 src/trust.rs diff --git a/Cargo.lock b/Cargo.lock index 5cfebe603b..671a03ff0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,6 +253,27 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -320,6 +341,17 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -428,6 +460,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -528,6 +570,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "parking_lot" version = "0.12.1" @@ -634,6 +682,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.4" @@ -684,6 +743,7 @@ dependencies = [ "assert_cmd", "clap", "crossterm", + "dirs", "hashbrown", "notify-debouncer-mini", "predicates", @@ -865,6 +925,26 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "toml_datetime" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index 07865abcce..6cf9ef94d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ edition.workspace = true anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" +dirs = "5.0.1" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" diff --git a/src/init.rs b/src/init.rs index 459519decd..d051fc4212 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,7 +6,7 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; +use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo, trust::trust_current_dir}; fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { let mut cargo_toml = Vec::with_capacity(1 << 13); @@ -85,6 +85,8 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?; + trust_current_dir()?; + Ok(()) } diff --git a/src/main.rs b/src/main.rs index ed5becf550..7b63f70f49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use anyhow::{Context, Result}; -use app_state::StateFileStatus; use clap::{Parser, Subcommand}; use crossterm::{ terminal::{Clear, ClearType}, @@ -19,14 +18,16 @@ mod init; mod list; mod progress_bar; mod run; +mod trust; mod watch; use self::{ - app_state::AppState, + app_state::{AppState, StateFileStatus}, info_file::InfoFile, init::init, list::list, run::run, + trust::{current_dir_is_trusted, trust_current_dir}, watch::{watch, WatchExit}, }; @@ -61,6 +62,11 @@ enum Subcommands { /// The name of the exercise name: String, }, + /// Trust the current directory with its exercises. + /// + /// You only need to run this if you want to work on third-party exercises or after you moved + /// the official exercises that were initialized with `rustlings init`. + Trust, } fn main() -> Result<()> { @@ -72,14 +78,26 @@ fn main() -> Result<()> { if matches!(args.command, Some(Subcommands::Init)) { init(&info_file.exercises).context("Initialization failed")?; - 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); } + if matches!(args.command, Some(Subcommands::Trust)) { + trust_current_dir()?; + println!("{POST_TRUST_MSG}"); + return Ok(()); + } + + if !current_dir_is_trusted()? { + println!("{NOT_TRUSTED_MSG}"); + exit(1); + } + let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), @@ -130,8 +148,6 @@ fn main() -> Result<()> { } } } - // `Init` is handled above. - Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; @@ -149,6 +165,8 @@ fn main() -> Result<()> { app_state.set_current_exercise_by_name(&name)?; println!("{}", app_state.current_exercise().hint); } + // `Init` and `Trust` are handled above. + Some(Subcommands::Init | Subcommands::Trust) => (), } 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 POST_INIT_MSG: &str = "Done initialization! + +Run `cd rustlings` to go into the generated directory. +Then run `rustlings` to get started."; + const PRE_INIT_MSG: &str = r" - welcome to... + Welcome to... _ _ _ _ __ _ _ ___| |_| (_)_ __ __ _ ___ | '__| | | / __| __| | | '_ \ / _` / __| @@ -170,11 +193,19 @@ 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! +const POST_TRUST_MSG: &str = "You now trust the exercises in the current directory. +Run `rustlings` to start working on them."; -Run `cd rustlings` to go into the generated directory. -Then run `rustlings` to get started."; +const NOT_TRUSTED_MSG: &str = "It looks like you are trying to work on third-party exercises. +Rustlings supports third-party exercises. But because Rustlings runs the code inside an exercise, +we need to warn you about the possibility of malicious code. +We recommend that you read all the exercise files in the `exercises` directory and check the +dependencies in the `Cargo.toml` file. +If everything looks fine and you want to trust this directory, run `rustlings trust`. + +If you you are trying to work on the official exercises that were generated using `rustlings init`, +then you probably moved the directory containing them. In that case, you can run `rustlings trust` +without a problem."; const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | diff --git a/src/trust.rs b/src/trust.rs new file mode 100644 index 0000000000..7e36f7372d --- /dev/null +++ b/src/trust.rs @@ -0,0 +1,72 @@ +use anyhow::{Context, Error, Result}; +use std::{ + env, + fs::{self, OpenOptions}, + io::{ErrorKind, Write}, +}; + +const DATA_DIR_NAME: &str = "rustlings"; +const TRUSTED_DIRS_FILE_NAME: &str = "trusted-dirs.txt"; + +pub fn trust_current_dir() -> Result<()> { + let mut path = dirs::data_dir().context("Failed to determine the data directory")?; + path.push(DATA_DIR_NAME); + if !path.is_dir() { + fs::create_dir(&path) + .with_context(|| format!("Failed to create the directory {}", path.display()))?; + } + + path.push(TRUSTED_DIRS_FILE_NAME); + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(&path) + .with_context(|| { + format!( + "Failed to create/open the file {} in write mode", + path.display(), + ) + })?; + + let dir = env::current_dir().context("Failed to get the current directory path")?; + let dir = dir.to_string_lossy(); + let mut line = Vec::with_capacity(dir.as_bytes().len() + 1); + line.extend_from_slice(dir.as_bytes()); + line.push(b'\n'); + + file.write_all(&line) + .with_context(|| format!("Failed to append to the file {}", path.display())) +} + +pub fn current_dir_is_trusted() -> Result { + let mut path = dirs::data_dir().context("Failed to determine the data directory")?; + path.push(DATA_DIR_NAME); + path.push(TRUSTED_DIRS_FILE_NAME); + + let content = match fs::read(&path) { + Ok(v) => v, + Err(e) => match e.kind() { + ErrorKind::NotFound => return Ok(false), + _ => { + return Err( + Error::from(e).context(format!("Failed to read the file {}", path.display())) + ) + } + }, + }; + + let current_dir = env::current_dir().context("Failed to get the current directory path")?; + let current_dir = current_dir.to_string_lossy(); + + for line in content.split(|c| *c == b'\n') { + if line.is_empty() { + break; + } + + if line == current_dir.as_bytes() { + return Ok(true); + } + } + + Ok(false) +} From 6f04570dd080f3aedf2fdf4fac1e627abe3a5b27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 15 Apr 2024 03:36:12 +0200 Subject: [PATCH 0719/1432] Revert "Implement third-party exercises trust handling" This reverts commit 15ca847c37c170590abe6caa53dba5606d956341. See https://rust-lang.zulipchat.com/#narrow/stream/334454-rustlings/topic/Proposal.3A.20Third-party.20exercises/near/433183449 --- Cargo.lock | 80 ---------------------------------------------------- Cargo.toml | 1 - src/init.rs | 4 +-- src/main.rs | 39 +++---------------------- src/trust.rs | 72 ---------------------------------------------- 5 files changed, 5 insertions(+), 191 deletions(-) delete mode 100644 src/trust.rs diff --git a/Cargo.lock b/Cargo.lock index 671a03ff0f..5cfebe603b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,27 +253,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -341,17 +320,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "getrandom" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hashbrown" version = "0.14.3" @@ -460,16 +428,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -570,12 +528,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "parking_lot" version = "0.12.1" @@ -682,17 +634,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - [[package]] name = "regex" version = "1.10.4" @@ -743,7 +684,6 @@ dependencies = [ "assert_cmd", "clap", "crossterm", - "dirs", "hashbrown", "notify-debouncer-mini", "predicates", @@ -925,26 +865,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" -[[package]] -name = "thiserror" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.58", -] - [[package]] name = "toml_datetime" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index 6cf9ef94d8..07865abcce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ edition.workspace = true anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" -dirs = "5.0.1" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" diff --git a/src/init.rs b/src/init.rs index d051fc4212..459519decd 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,7 +6,7 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo, trust::trust_current_dir}; +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); @@ -85,8 +85,6 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?; - trust_current_dir()?; - Ok(()) } diff --git a/src/main.rs b/src/main.rs index 7b63f70f49..541783dc1b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use app_state::StateFileStatus; use clap::{Parser, Subcommand}; use crossterm::{ terminal::{Clear, ClearType}, @@ -18,16 +19,14 @@ mod init; mod list; mod progress_bar; mod run; -mod trust; mod watch; use self::{ - app_state::{AppState, StateFileStatus}, + app_state::AppState, info_file::InfoFile, init::init, list::list, run::run, - trust::{current_dir_is_trusted, trust_current_dir}, watch::{watch, WatchExit}, }; @@ -62,11 +61,6 @@ enum Subcommands { /// The name of the exercise name: String, }, - /// Trust the current directory with its exercises. - /// - /// You only need to run this if you want to work on third-party exercises or after you moved - /// the official exercises that were initialized with `rustlings init`. - Trust, } fn main() -> Result<()> { @@ -87,17 +81,6 @@ fn main() -> Result<()> { exit(1); } - if matches!(args.command, Some(Subcommands::Trust)) { - trust_current_dir()?; - println!("{POST_TRUST_MSG}"); - return Ok(()); - } - - if !current_dir_is_trusted()? { - println!("{NOT_TRUSTED_MSG}"); - exit(1); - } - let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), @@ -148,6 +131,8 @@ fn main() -> Result<()> { } } } + // `Init` is handled above. + Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; @@ -165,8 +150,6 @@ fn main() -> Result<()> { app_state.set_current_exercise_by_name(&name)?; println!("{}", app_state.current_exercise().hint); } - // `Init` and `Trust` are handled above. - Some(Subcommands::Init | Subcommands::Trust) => (), } Ok(()) @@ -193,20 +176,6 @@ 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_TRUST_MSG: &str = "You now trust the exercises in the current directory. -Run `rustlings` to start working on them."; - -const NOT_TRUSTED_MSG: &str = "It looks like you are trying to work on third-party exercises. -Rustlings supports third-party exercises. But because Rustlings runs the code inside an exercise, -we need to warn you about the possibility of malicious code. -We recommend that you read all the exercise files in the `exercises` directory and check the -dependencies in the `Cargo.toml` file. -If everything looks fine and you want to trust this directory, run `rustlings trust`. - -If you you are trying to work on the official exercises that were generated using `rustlings init`, -then you probably moved the directory containing them. In that case, you can run `rustlings trust` -without a problem."; - const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | +-------------------------- ------------------------+ diff --git a/src/trust.rs b/src/trust.rs deleted file mode 100644 index 7e36f7372d..0000000000 --- a/src/trust.rs +++ /dev/null @@ -1,72 +0,0 @@ -use anyhow::{Context, Error, Result}; -use std::{ - env, - fs::{self, OpenOptions}, - io::{ErrorKind, Write}, -}; - -const DATA_DIR_NAME: &str = "rustlings"; -const TRUSTED_DIRS_FILE_NAME: &str = "trusted-dirs.txt"; - -pub fn trust_current_dir() -> Result<()> { - let mut path = dirs::data_dir().context("Failed to determine the data directory")?; - path.push(DATA_DIR_NAME); - if !path.is_dir() { - fs::create_dir(&path) - .with_context(|| format!("Failed to create the directory {}", path.display()))?; - } - - path.push(TRUSTED_DIRS_FILE_NAME); - let mut file = OpenOptions::new() - .create(true) - .append(true) - .open(&path) - .with_context(|| { - format!( - "Failed to create/open the file {} in write mode", - path.display(), - ) - })?; - - let dir = env::current_dir().context("Failed to get the current directory path")?; - let dir = dir.to_string_lossy(); - let mut line = Vec::with_capacity(dir.as_bytes().len() + 1); - line.extend_from_slice(dir.as_bytes()); - line.push(b'\n'); - - file.write_all(&line) - .with_context(|| format!("Failed to append to the file {}", path.display())) -} - -pub fn current_dir_is_trusted() -> Result { - let mut path = dirs::data_dir().context("Failed to determine the data directory")?; - path.push(DATA_DIR_NAME); - path.push(TRUSTED_DIRS_FILE_NAME); - - let content = match fs::read(&path) { - Ok(v) => v, - Err(e) => match e.kind() { - ErrorKind::NotFound => return Ok(false), - _ => { - return Err( - Error::from(e).context(format!("Failed to read the file {}", path.display())) - ) - } - }, - }; - - let current_dir = env::current_dir().context("Failed to get the current directory path")?; - let current_dir = current_dir.to_string_lossy(); - - for line in content.split(|c| *c == b'\n') { - if line.is_empty() { - break; - } - - if line == current_dir.as_bytes() { - return Ok(true); - } - } - - Ok(false) -} From f5eaa578b9479d6a0b9acab5ebf155fcd50b9b27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 15 Apr 2024 23:35:30 +0200 Subject: [PATCH 0720/1432] Update deps --- Cargo.lock | 37 +++++++++++++------------------------ Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5cfebe603b..defdacd4b0 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]] @@ -589,9 +589,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" dependencies = [ "unicode-ident", ] @@ -607,9 +607,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", @@ -745,7 +745,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -795,12 +795,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 +834,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 +1145,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 07865abcce..efc1441994 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ 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" +ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros" } serde.workspace = true toml_edit.workspace = true From 7ebc260924f5db0099568589f2be621c9ea43721 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 15 Apr 2024 23:54:57 +0200 Subject: [PATCH 0721/1432] Scetch the dev subcommand --- src/dev.rs | 20 ++++++++++++++++++++ src/dev/check.rs | 5 +++++ src/dev/init.rs | 5 +++++ src/main.rs | 21 +++++++++------------ 4 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 src/dev.rs create mode 100644 src/dev/check.rs create mode 100644 src/dev/init.rs diff --git a/src/dev.rs b/src/dev.rs new file mode 100644 index 0000000000..40382a8713 --- /dev/null +++ b/src/dev.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use clap::Subcommand; + +mod check; +mod init; + +#[derive(Subcommand)] +pub enum DevCommands { + Init, + Check, +} + +impl DevCommands { + pub fn run(self) -> Result<()> { + match self { + DevCommands::Init => init::init(), + DevCommands::Check => check::check(), + } + } +} diff --git a/src/dev/check.rs b/src/dev/check.rs new file mode 100644 index 0000000000..46d3ffec48 --- /dev/null +++ b/src/dev/check.rs @@ -0,0 +1,5 @@ +use anyhow::Result; + +pub fn check() -> Result<()> { + todo!() +} diff --git a/src/dev/init.rs b/src/dev/init.rs new file mode 100644 index 0000000000..01cfd9f0cb --- /dev/null +++ b/src/dev/init.rs @@ -0,0 +1,5 @@ +use anyhow::Result; + +pub fn init() -> Result<()> { + todo!() +} diff --git a/src/main.rs b/src/main.rs index 541783dc1b..e72dbdcce2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use std::{ }; mod app_state; +mod dev; mod embedded; mod exercise; mod info_file; @@ -21,14 +22,7 @@ mod progress_bar; mod run; mod watch; -use self::{ - app_state::AppState, - info_file::InfoFile, - init::init, - list::list, - run::run, - watch::{watch, WatchExit}, -}; +use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] @@ -61,6 +55,8 @@ enum Subcommands { /// The name of the exercise name: String, }, + #[command(subcommand)] + Dev(DevCommands), } fn main() -> Result<()> { @@ -71,7 +67,7 @@ fn main() -> Result<()> { let info_file = InfoFile::parse()?; if matches!(args.command, Some(Subcommands::Init)) { - init(&info_file.exercises).context("Initialization failed")?; + init::init(&info_file.exercises).context("Initialization failed")?; println!("{POST_INIT_MSG}"); return Ok(()); } @@ -122,12 +118,12 @@ 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)?, } } } @@ -137,7 +133,7 @@ fn main() -> Result<()> { 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)?; @@ -150,6 +146,7 @@ fn main() -> Result<()> { app_state.set_current_exercise_by_name(&name)?; println!("{}", app_state.current_exercise().hint); } + Some(Subcommands::Dev(dev_command)) => dev_command.run()?, } Ok(()) From 92777c0a4498625a44c0e6eeced97633dacc78d1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 01:22:54 +0200 Subject: [PATCH 0722/1432] Add the format version --- info.toml | 2 ++ src/info_file.rs | 1 + src/main.rs | 13 ++++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/info.toml b/info.toml index fa90ad7044..27071a5cb6 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/src/info_file.rs b/src/info_file.rs index 2a45e02de7..18e77b9f4f 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, pub final_message: Option, pub exercises: Vec, diff --git a/src/main.rs b/src/main.rs index e72dbdcce2..7d4d8a5c9b 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::{ @@ -24,6 +24,8 @@ mod watch; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; +const CURRENT_FORMAT_VERSION: u8 = 1; + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -66,6 +68,10 @@ fn main() -> Result<()> { let info_file = InfoFile::parse()?; + if info_file.format_version > CURRENT_FORMAT_VERSION { + bail!(FORMAT_VERSION_HIGHER_ERR); + } + if matches!(args.command, Some(Subcommands::Init)) { init::init(&info_file.exercises).context("Initialization failed")?; println!("{POST_INIT_MSG}"); @@ -156,6 +162,11 @@ 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 POST_INIT_MSG: &str = "Done initialization! Run `cd rustlings` to go into the generated directory. From 25e7696565349014c5e2662ddba43dc20391e272 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:08:45 +0200 Subject: [PATCH 0723/1432] Done `dev init` --- src/dev.rs | 7 +++- src/dev/init.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++- src/init.rs | 63 +++++++++++------------------- 3 files changed, 125 insertions(+), 45 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 40382a8713..e09996f88c 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use clap::Subcommand; mod check; @@ -13,8 +13,11 @@ pub enum DevCommands { impl DevCommands { pub fn run(self) -> Result<()> { match self { - DevCommands::Init => init::init(), + DevCommands::Init => init::init().context(INIT_ERR), DevCommands::Check => check::check(), } } } + +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/init.rs b/src/dev/init.rs index 01cfd9f0cb..d382136244 100644 --- a/src/dev/init.rs +++ b/src/dev/init.rs @@ -1,5 +1,101 @@ -use anyhow::Result; +use std::fs::{self, create_dir}; + +use anyhow::{Context, Result}; + +use crate::CURRENT_FORMAT_VERSION; pub fn init() -> Result<()> { - todo!() + 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( + "rustligns/Cargo.toml", + format!("{CARGO_TOML_COMMENT}{}", crate::init::CARGO_TOML_PACKAGE), + ) + .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 `rustligns/.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 = "???" + +# A mutli-line hint to be shown to users on request. +hint = """???""" +"#; + +const CARGO_TOML_COMMENT: &str = + "# You shouldn't edit this file manually! It is updated by `rustlings dev check` + +"; + +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/init.rs b/src/init.rs index 459519decd..3202017170 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,14 +1,14 @@ 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<()> { +fn cargo_toml(exercise_infos: &[ExerciseInfo]) -> Vec { let mut cargo_toml = Vec::with_capacity(1 << 13); cargo_toml.extend_from_slice(b"bin = [\n"); for exercise_info in exercise_infos { @@ -23,39 +23,10 @@ fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { cargo_toml.extend_from_slice(b".rs\" },\n"); } - cargo_toml.extend_from_slice( - br#"] + cargo_toml.extend_from_slice(b"]\n\n"); + cargo_toml.extend_from_slice(CARGO_TOML_PACKAGE.as_bytes()); -[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)?; - - Ok(()) + cargo_toml } pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { @@ -78,21 +49,31 @@ 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(exercise_infos)) .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`")?; Ok(()) } -const GITIGNORE: &[u8] = b"/target -/.rustlings-state.txt +pub const CARGO_TOML_PACKAGE: &str = r#"[package] +name = "rustlings" +edition = "2021" +publish = false +"#; + +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 From df448c069cae593d5eec37aa5b07c8103ae0f9b9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:15:14 +0200 Subject: [PATCH 0724/1432] Fix running dev commands --- src/init.rs | 7 +++++++ src/main.rs | 20 ++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/init.rs b/src/init.rs index 3202017170..7648202494 100644 --- a/src/init.rs +++ b/src/init.rs @@ -59,6 +59,8 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { fs::write(".vscode/extensions.json", VS_CODE_EXTENSIONS_JSON) .context("Failed to create the file `rustlings/.vscode/extensions.json`")?; + println!("{POST_INIT_MSG}"); + Ok(()) } @@ -87,3 +89,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/main.rs b/src/main.rs index 7d4d8a5c9b..5188ee12b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,10 +72,12 @@ fn main() -> Result<()> { bail!(FORMAT_VERSION_HIGHER_ERR); } - if matches!(args.command, Some(Subcommands::Init)) { - init::init(&info_file.exercises).context("Initialization failed")?; - println!("{POST_INIT_MSG}"); - return Ok(()); + match args.command { + Some(Subcommands::Init) => { + return init::init(&info_file.exercises).context("Initialization failed"); + } + Some(Subcommands::Dev(dev_command)) => return dev_command.run(), + _ => (), } if !Path::new("exercises").is_dir() { @@ -133,8 +135,6 @@ fn main() -> Result<()> { } } } - // `Init` is handled above. - Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; @@ -152,7 +152,8 @@ fn main() -> Result<()> { app_state.set_current_exercise_by_name(&name)?; println!("{}", app_state.current_exercise().hint); } - Some(Subcommands::Dev(dev_command)) => dev_command.run()?, + // Handled in an earlier match. + Some(Subcommands::Init | Subcommands::Dev(_)) => (), } Ok(()) @@ -167,11 +168,6 @@ const FORMAT_VERSION_HIGHER_ERR: &str = It is possible that you have an outdated version of Rustlings. Try to install the latest Rustlings version first."; -const POST_INIT_MSG: &str = "Done initialization! - -Run `cd rustlings` to go into the generated directory. -Then run `rustlings` to get started."; - const PRE_INIT_MSG: &str = r" Welcome to... _ _ _ From c07cf5bffe43402ced908dc315e5b8ee3e52bdcc Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:18:06 +0200 Subject: [PATCH 0725/1432] Fix typo --- src/dev/init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dev/init.rs b/src/dev/init.rs index d382136244..73c2c2037e 100644 --- a/src/dev/init.rs +++ b/src/dev/init.rs @@ -20,7 +20,7 @@ pub fn init() -> Result<()> { .context("Failed to create the file `rustlings/info.toml`")?; fs::write( - "rustligns/Cargo.toml", + "rustlings/Cargo.toml", format!("{CARGO_TOML_COMMENT}{}", crate::init::CARGO_TOML_PACKAGE), ) .context("Failed to create the file `rustlings/Cargo.toml`")?; @@ -32,7 +32,7 @@ pub fn init() -> Result<()> { .context("Failed to create the file `rustlings/README.md`")?; create_dir("rustlings/.vscode") - .context("Failed to create the directory `rustligns/.vscode`")?; + .context("Failed to create the directory `rustlings/.vscode`")?; fs::write( "rustlings/.vscode/extensions.json", crate::init::VS_CODE_EXTENSIONS_JSON, From d1ebbaa6f610ec492422806beb34e0dc7e4fc466 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:18:22 +0200 Subject: [PATCH 0726/1432] Add format_version to test info.toml files --- tests/fixture/failure/info.toml | 2 ++ tests/fixture/state/info.toml | 2 ++ tests/fixture/success/info.toml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index 94ec6ead61..ef99a07ae4 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 e5c4d8f407..eec24ea198 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 674ba26434..88650ecbaf 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" From aa813fbce1305bb1beac88bff47f4279948cb3ac Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:30:28 +0200 Subject: [PATCH 0727/1432] Update Cargo.toml on `dev check` --- src/dev.rs | 6 ++++-- src/dev/check.rs | 17 ++++++++++++++--- src/init.rs | 2 +- src/main.rs | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index e09996f88c..7905f3879c 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,6 +1,8 @@ use anyhow::{Context, Result}; use clap::Subcommand; +use crate::info_file::InfoFile; + mod check; mod init; @@ -11,10 +13,10 @@ pub enum DevCommands { } impl DevCommands { - pub fn run(self) -> Result<()> { + pub fn run(self, info_file: InfoFile) -> Result<()> { match self { DevCommands::Init => init::init().context(INIT_ERR), - DevCommands::Check => check::check(), + DevCommands::Check => check::check(info_file), } } } diff --git a/src/dev/check.rs b/src/dev/check.rs index 46d3ffec48..9ae066bf16 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,5 +1,16 @@ -use anyhow::Result; +use std::fs; -pub fn check() -> Result<()> { - todo!() +use anyhow::{Context, Result}; + +use crate::{info_file::InfoFile, init::cargo_toml}; + +pub fn check(info_file: InfoFile) -> Result<()> { + // TODO: Add checks + + fs::write("Cargo.toml", cargo_toml(&info_file.exercises)) + .context("Failed to update the file `Cargo.toml`")?; + + println!("Everything looks fine!"); + + Ok(()) } diff --git a/src/init.rs b/src/init.rs index 7648202494..5fa44d40a7 100644 --- a/src/init.rs +++ b/src/init.rs @@ -8,7 +8,7 @@ use std::{ use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; -fn cargo_toml(exercise_infos: &[ExerciseInfo]) -> Vec { +pub fn cargo_toml(exercise_infos: &[ExerciseInfo]) -> Vec { let mut cargo_toml = Vec::with_capacity(1 << 13); cargo_toml.extend_from_slice(b"bin = [\n"); for exercise_info in exercise_infos { diff --git a/src/main.rs b/src/main.rs index 5188ee12b7..8b3f28f9e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,7 +76,7 @@ fn main() -> Result<()> { Some(Subcommands::Init) => { return init::init(&info_file.exercises).context("Initialization failed"); } - Some(Subcommands::Dev(dev_command)) => return dev_command.run(), + Some(Subcommands::Dev(dev_command)) => return dev_command.run(info_file), _ => (), } From 6566c5904f9463607eac15fd0072b551fac589dd Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:35:23 +0200 Subject: [PATCH 0728/1432] Tell about updating Cargo.toml --- src/dev/check.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 9ae066bf16..1ee717b5be 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -9,8 +9,9 @@ pub fn check(info_file: InfoFile) -> Result<()> { fs::write("Cargo.toml", cargo_toml(&info_file.exercises)) .context("Failed to update the file `Cargo.toml`")?; + println!("Updated `Cargo.toml`"); - println!("Everything looks fine!"); + println!("\nEverything looks fine!"); Ok(()) } From 87db9129bc361b88e206d1b27cb9056d2c8b00f1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:37:58 +0200 Subject: [PATCH 0729/1432] Add the mode field --- src/dev/init.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dev/init.rs b/src/dev/init.rs index 73c2c2037e..7dd3a3e1e8 100644 --- a/src/dev/init.rs +++ b/src/dev/init.rs @@ -71,6 +71,11 @@ name = "???" # 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 mutli-line hint to be shown to users on request. hint = """???""" "#; From 86d716cf8a59092ba2078f2b0d80f95e155f2d64 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:43:34 +0200 Subject: [PATCH 0730/1432] Add comment about keeping dependencies --- src/dev/check.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dev/check.rs b/src/dev/check.rs index 1ee717b5be..5910a75ad5 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -7,6 +7,7 @@ use crate::{info_file::InfoFile, init::cargo_toml}; pub fn check(info_file: InfoFile) -> Result<()> { // TODO: Add checks + // TODO: Keep dependencies! fs::write("Cargo.toml", cargo_toml(&info_file.exercises)) .context("Failed to update the file `Cargo.toml`")?; println!("Updated `Cargo.toml`"); From 4d9eb35ad7f6625da7e2a83afbb5c0bcee6ca96a Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:46:04 +0200 Subject: [PATCH 0731/1432] Prepare for publishing the first alpha version --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efc1441994..fd82478e25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ ] [workspace.package] -version = "6.0.0" +version = "6.0.0-alpha.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", @@ -40,7 +40,7 @@ crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros" } +rustlings-macros = "6.0.0-alpha.0" serde.workspace = true toml_edit.workspace = true which = "6.0.1" From 932f6b53a911cd549195be306fcbd65f5770f9d5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:47:09 +0200 Subject: [PATCH 0732/1432] Add myself to the list of authors :) --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index fd82478e25..81e91ad477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ version = "6.0.0-alpha.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", + "Mo ", ] license = "MIT" edition = "2021" From f9be652b3b84dfa16265ec5804c267c66d381da5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:56:08 +0200 Subject: [PATCH 0733/1432] Ready to publish --- Cargo.lock | 4 ++-- Cargo.toml | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index defdacd4b0..65d221623d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -678,7 +678,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0" +version = "6.0.0-alpha.0" dependencies = [ "anyhow", "assert_cmd", @@ -696,7 +696,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0" +version = "6.0.0-alpha.0" dependencies = [ "quote", ] diff --git a/Cargo.toml b/Cargo.toml index 81e91ad477..06fbbf70b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,13 @@ version.workspace = true authors.workspace = true license.workspace = true edition.workspace = true +include = [ + "/exercises/", + "/info.toml", + "/LICENSE", + "/README.md", + "/src/", +] [dependencies] anyhow.workspace = true @@ -41,7 +48,7 @@ crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" -rustlings-macros = "6.0.0-alpha.0" +rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" From 0ac5aa7af2dbff6f9d5e7cc712f409a86ea4cad0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 04:00:42 +0200 Subject: [PATCH 0734/1432] Fix typo --- src/dev/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/init.rs b/src/dev/init.rs index 7dd3a3e1e8..09935220d5 100644 --- a/src/dev/init.rs +++ b/src/dev/init.rs @@ -76,7 +76,7 @@ name = "???" # The mode "run" only checks if the exercise compiles and runs it. mode = "test" -# A mutli-line hint to be shown to users on request. +# A multi-line hint to be shown to users on request. hint = """???""" "#; From d322bcfcec8bd39a66fb5e6c0390e648e060b67c Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 04:04:45 +0200 Subject: [PATCH 0735/1432] Add description --- rustlings-macros/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 79279f578b..095deaa3a1 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 From 67a15ef27f73f83d4a29b955e970846fe42026fc Mon Sep 17 00:00:00 2001 From: liv Date: Tue, 16 Apr 2024 15:23:08 +0200 Subject: [PATCH 0736/1432] fix: remove bad hint in functions4 Technically it's correct, but playing around with this will very quickly throw you into having to handle `Option`s and futzing around with `try_into`. Not really something we want to throw upon the user here. Closes #1948. --- info.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/info.toml b/info.toml index 36629b38c9..5690701d55 100644 --- a/info.toml +++ b/info.toml @@ -142,10 +142,7 @@ mode = "compile" hint = """ The error message points to the function `sale_price` and says it expects a type after the `->`. This is where the function's return type should be -- take a -look at the `is_even` function for an example! - -Also: Did you figure out that, technically, `u32` would be the more fitting type -for the inputs of the functions here, since the original prices shouldn't be negative? If so, kudos!""" +look at the `is_even` function for an example!""" [[exercises]] name = "functions5" From 30636e7cf345757f95235744ff81376ae81c9aa2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 21:46:07 +0200 Subject: [PATCH 0737/1432] Use colors inside the test --- src/exercise.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 2ec8d979d3..8bdf399f5a 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -69,7 +69,17 @@ impl Exercise { pub fn run(&self) -> Result { 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"], From 9a13bccd6305a83635993b9e2f80422a35566ee3 Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Wed, 17 Apr 2024 01:35:29 -0700 Subject: [PATCH 0738/1432] chore: changes from formatting on save --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 226d413726..515dc50e12 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rustlings Tests on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] env: CARGO_TERM_COLOR: always From 4eec81a1131a3159bce2131266a8d62d98212741 Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Wed, 17 Apr 2024 01:35:53 -0700 Subject: [PATCH 0739/1432] ci: add `clippy` job to `rust.yml` workflow --- .github/workflows/rust.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 515dc50e12..689d05e1d8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,6 +10,11 @@ env: CARGO_TERM_COLOR: always jobs: + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: cargo clippy -- --deny warnings fmt: runs-on: ubuntu-latest steps: From 501b973c258a3c2e3a463d58c16402302184380f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 15:55:50 +0200 Subject: [PATCH 0740/1432] Add "dev update" --- Cargo.lock | 9 ---- Cargo.toml | 14 ++---- dev/Cargo.toml | 4 +- gen-dev-cargo-toml/Cargo.toml | 10 ----- gen-dev-cargo-toml/src/main.rs | 68 ---------------------------- src/dev.rs | 9 ++-- src/dev/check.rs | 81 +++++++++++++++++++++++++++++++--- src/dev/init.rs | 23 +++++----- src/dev/update.rs | 53 ++++++++++++++++++++++ src/exercise.rs | 5 +-- src/init.rs | 41 +++++------------ src/main.rs | 27 ++++++++---- tests/dev_cargo_bins.rs | 44 ------------------ 13 files changed, 182 insertions(+), 206 deletions(-) delete mode 100644 gen-dev-cargo-toml/Cargo.toml delete mode 100644 gen-dev-cargo-toml/src/main.rs create mode 100644 src/dev/update.rs delete mode 100644 tests/dev_cargo_bins.rs diff --git a/Cargo.lock b/Cargo.lock index 65d221623d..4c95ca855d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,15 +311,6 @@ dependencies = [ "libc", ] -[[package]] -name = "gen-dev-cargo-toml" -version = "0.0.0" -dependencies = [ - "anyhow", - "serde", - "toml_edit", -] - [[package]] name = "hashbrown" version = "0.14.3" diff --git a/Cargo.toml b/Cargo.toml index 06fbbf70b9..cde4182343 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,6 @@ exclude = [ "tests/fixture/success", "dev", ] -members = [ - "gen-dev-cargo-toml", -] [workspace.package] version = "6.0.0-alpha.0" @@ -20,11 +17,6 @@ authors = [ 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!" @@ -42,15 +34,15 @@ include = [ ] [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.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } -serde.workspace = true -toml_edit.workspace = true +serde = { version = "1.0.197", 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 1d230ebb58..e66973e061 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/gen-dev-cargo-toml/Cargo.toml b/gen-dev-cargo-toml/Cargo.toml deleted file mode 100644 index 8922ae8c39..0000000000 --- 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 43b4ebd7a5..0000000000 --- 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, -} - -#[derive(Deserialize)] -struct InfoFile { - exercises: Vec, -} - -fn main() -> Result<()> { - let exercise_infos = toml_edit::de::from_str::( - &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/src/dev.rs b/src/dev.rs index 7905f3879c..feca99e5dc 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,22 +1,23 @@ use anyhow::{Context, Result}; use clap::Subcommand; -use crate::info_file::InfoFile; - mod check; mod init; +mod update; #[derive(Subcommand)] pub enum DevCommands { Init, Check, + Update, } impl DevCommands { - pub fn run(self, info_file: InfoFile) -> Result<()> { + pub fn run(self) -> Result<()> { match self { DevCommands::Init => init::init().context(INIT_ERR), - DevCommands::Check => check::check(info_file), + DevCommands::Check => check::check(), + DevCommands::Update => update::update(), } } } diff --git a/src/dev/check.rs b/src/dev/check.rs index 5910a75ad5..bc8e4592e9 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,16 +1,83 @@ +use anyhow::{bail, Context, Result}; use std::fs; -use anyhow::{Context, Result}; +use crate::{ + info_file::{ExerciseInfo, InfoFile}, + DEVELOPING_OFFIFICAL_RUSTLINGS, +}; -use crate::{info_file::InfoFile, init::cargo_toml}; +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, + 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 = ¤t_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()?; -pub fn check(info_file: InfoFile) -> Result<()> { // TODO: Add checks - // TODO: Keep dependencies! - fs::write("Cargo.toml", cargo_toml(&info_file.exercises)) - .context("Failed to update the file `Cargo.toml`")?; - println!("Updated `Cargo.toml`"); + if DEVELOPING_OFFIFICAL_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, ¤t_cargo_toml, b"").context( + "The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it", + )?; + } println!("\nEverything looks fine!"); diff --git a/src/dev/init.rs b/src/dev/init.rs index 09935220d5..3ce5055c5c 100644 --- a/src/dev/init.rs +++ b/src/dev/init.rs @@ -1,6 +1,5 @@ -use std::fs::{self, create_dir}; - use anyhow::{Context, Result}; +use std::fs::{self, create_dir}; use crate::CURRENT_FORMAT_VERSION; @@ -19,11 +18,8 @@ pub fn init() -> Result<()> { ) .context("Failed to create the file `rustlings/info.toml`")?; - fs::write( - "rustlings/Cargo.toml", - format!("{CARGO_TOML_COMMENT}{}", crate::init::CARGO_TOML_PACKAGE), - ) - .context("Failed to create the file `rustlings/Cargo.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`")?; @@ -80,10 +76,17 @@ mode = "test" hint = """???""" "#; -const CARGO_TOML_COMMENT: &str = - "# You shouldn't edit this file manually! It is updated by `rustlings dev check` +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 πŸ¦€ diff --git a/src/dev/update.rs b/src/dev/update.rs new file mode 100644 index 0000000000..981934d6cd --- /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_OFFIFICAL_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_OFFIFICAL_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, ¤t_cargo_toml, "Cargo.toml", b"") + .context("Failed to update the file `Cargo.toml`")?; + + println!("Updated `Cargo.toml`"); + } + + Ok(()) +} diff --git a/src/exercise.rs b/src/exercise.rs index 8bdf399f5a..c4df9999f0 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -9,6 +9,7 @@ use std::{ use crate::{ embedded::{WriteStrategy, EMBEDDED_FILES}, info_file::Mode, + DEVELOPING_OFFIFICAL_RUSTLINGS, }; pub struct TerminalFileLink<'a> { @@ -50,9 +51,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_OFFIFICAL_RUSTLINGS { cmd.arg("--manifest-path").arg("dev/Cargo.toml"); } diff --git a/src/init.rs b/src/init.rs index 5fa44d40a7..52315e240e 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,30 +6,19 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; - -pub fn cargo_toml(exercise_infos: &[ExerciseInfo]) -> Vec { - 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"); +use crate::embedded::EMBEDDED_FILES; + +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 +}; - cargo_toml.extend_from_slice(b"]\n\n"); - cargo_toml.extend_from_slice(CARGO_TOML_PACKAGE.as_bytes()); - - cargo_toml -} - -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); } @@ -49,7 +38,7 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { .init_exercises_dir() .context("Failed to initialize the `rustlings/exercises` directory")?; - fs::write("Cargo.toml", cargo_toml(exercise_infos)) + fs::write("Cargo.toml", CARGO_TOML) .context("Failed to create the file `rustlings/Cargo.toml`")?; fs::write(".gitignore", GITIGNORE) @@ -64,12 +53,6 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { Ok(()) } -pub const CARGO_TOML_PACKAGE: &str = r#"[package] -name = "rustlings" -edition = "2021" -publish = false -"#; - pub const GITIGNORE: &[u8] = b"Cargo.lock .rustlings-state.txt target diff --git a/src/main.rs b/src/main.rs index 8b3f28f9e8..ea5f7c922d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,17 @@ mod watch; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; const CURRENT_FORMAT_VERSION: u8 = 1; +const DEVELOPING_OFFIFICAL_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 #[derive(Parser)] @@ -66,17 +77,11 @@ fn main() -> Result<()> { which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; - let info_file = InfoFile::parse()?; - - if info_file.format_version > CURRENT_FORMAT_VERSION { - bail!(FORMAT_VERSION_HIGHER_ERR); - } - match args.command { Some(Subcommands::Init) => { - return init::init(&info_file.exercises).context("Initialization failed"); + return init::init().context("Initialization failed"); } - Some(Subcommands::Dev(dev_command)) => return dev_command.run(info_file), + Some(Subcommands::Dev(dev_command)) => return dev_command.run(), _ => (), } @@ -85,6 +90,12 @@ fn main() -> Result<()> { 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(), diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs deleted file mode 100644 index 81f48b1e1a..0000000000 --- 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, -} - -#[derive(Deserialize)] -struct InfoFile { - exercises: Vec, -} - -#[test] -fn dev_cargo_bins() { - let cargo_toml = fs::read_to_string("dev/Cargo.toml").unwrap(); - - let exercise_infos = - toml_edit::de::from_str::(&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; - } -} From a2506f154b929b74e7d5c286cee6eabb008aa1fd Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 15:56:24 +0200 Subject: [PATCH 0741/1432] Update serde --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c95ca855d..b194de618e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,9 +580,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -721,18 +721,18 @@ 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", diff --git a/Cargo.toml b/Cargo.toml index cde4182343..5e3be11313 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } -serde = { version = "1.0.197", features = ["derive"] } +serde = { version = "1.0.198", features = ["derive"] } toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } which = "6.0.1" From 7f433ae28f0e79c62f53b74a14042f916cb13650 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 16:09:25 +0200 Subject: [PATCH 0742/1432] Check the format version in `dev check` --- src/dev/check.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index bc8e4592e9..daf5bdb262 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,9 +1,9 @@ use anyhow::{bail, Context, Result}; -use std::fs; +use std::{cmp::Ordering, fs}; use crate::{ info_file::{ExerciseInfo, InfoFile}, - DEVELOPING_OFFIFICAL_RUSTLINGS, + CURRENT_FORMAT_VERSION, DEVELOPING_OFFIFICAL_RUSTLINGS, }; pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { @@ -62,7 +62,11 @@ fn check_cargo_toml( pub fn check() -> Result<()> { let info_file = InfoFile::parse()?; - // TODO: Add checks + 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 => (), + } if DEVELOPING_OFFIFICAL_RUSTLINGS { check_cargo_toml( From 7005d8a400ce2a61f05bae1f71e144e0a25a9bf0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 16:11:44 +0200 Subject: [PATCH 0743/1432] Fix typo --- src/dev/check.rs | 4 ++-- src/dev/update.rs | 4 ++-- src/exercise.rs | 4 ++-- src/main.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index daf5bdb262..9002eaff80 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -3,7 +3,7 @@ use std::{cmp::Ordering, fs}; use crate::{ info_file::{ExerciseInfo, InfoFile}, - CURRENT_FORMAT_VERSION, DEVELOPING_OFFIFICAL_RUSTLINGS, + CURRENT_FORMAT_VERSION, DEVELOPING_OFFICIAL_RUSTLINGS, }; pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { @@ -68,7 +68,7 @@ pub fn check() -> Result<()> { Ordering::Equal => (), } - if DEVELOPING_OFFIFICAL_RUSTLINGS { + if DEVELOPING_OFFICIAL_RUSTLINGS { check_cargo_toml( &info_file.exercises, include_str!("../../dev/Cargo.toml"), diff --git a/src/dev/update.rs b/src/dev/update.rs index 981934d6cd..65dcf76823 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -4,7 +4,7 @@ use anyhow::{Context, Result}; use crate::{ info_file::{ExerciseInfo, InfoFile}, - DEVELOPING_OFFIFICAL_RUSTLINGS, + DEVELOPING_OFFICIAL_RUSTLINGS, }; use super::check::{append_bins, bins_start_end_ind}; @@ -30,7 +30,7 @@ fn update_cargo_toml( pub fn update() -> Result<()> { let info_file = InfoFile::parse()?; - if DEVELOPING_OFFIFICAL_RUSTLINGS { + if DEVELOPING_OFFICIAL_RUSTLINGS { update_cargo_toml( &info_file.exercises, include_str!("../../dev/Cargo.toml"), diff --git a/src/exercise.rs b/src/exercise.rs index c4df9999f0..60a65bb65d 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -9,7 +9,7 @@ use std::{ use crate::{ embedded::{WriteStrategy, EMBEDDED_FILES}, info_file::Mode, - DEVELOPING_OFFIFICAL_RUSTLINGS, + DEVELOPING_OFFICIAL_RUSTLINGS, }; pub struct TerminalFileLink<'a> { @@ -51,7 +51,7 @@ impl Exercise { cmd.arg(command); // A hack to make `cargo run` work when developing Rustlings. - if DEVELOPING_OFFIFICAL_RUSTLINGS { + if DEVELOPING_OFFICIAL_RUSTLINGS { cmd.arg("--manifest-path").arg("dev/Cargo.toml"); } diff --git a/src/main.rs b/src/main.rs index ea5f7c922d..fa5542af25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ mod watch; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; const CURRENT_FORMAT_VERSION: u8 = 1; -const DEVELOPING_OFFIFICAL_RUSTLINGS: bool = { +const DEVELOPING_OFFICIAL_RUSTLINGS: bool = { #[allow(unused_assignments, unused_mut)] let mut debug_profile = false; From 28ec0f864a5d041e15c08049a600c1f0e4fefd2e Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 18:19:08 +0200 Subject: [PATCH 0744/1432] Check the info file --- src/dev/check.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 6 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 9002eaff80..ac2c603d46 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,11 +1,128 @@ use anyhow::{bail, Context, Result}; -use std::{cmp::Ordering, fs}; +use std::{ + cmp::Ordering, + fs::{self, read_dir}, + path::PathBuf, +}; use crate::{ info_file::{ExerciseInfo, InfoFile}, CURRENT_FORMAT_VERSION, DEVELOPING_OFFICIAL_RUSTLINGS, }; +fn forbidden_char(input: &str) -> Option { + input.chars().find(|c| *c != '_' && !c.is_alphanumeric()) +} + +fn check_info_file_exercises(info_file: &InfoFile) -> Result> { + let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); + let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); + for exercise_info in &info_file.exercises { + 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 let Some(c) = forbidden_char(dir) { + bail!("Char `{c}` in the exercise dir `{dir}` is not allowed"); + } + } + + if !names.insert(exercise_info.name.as_str()) { + bail!( + "The exercise name {} is duplicated. Exercise names must all be unique", + exercise_info.name, + ); + } + + paths.insert(PathBuf::from(exercise_info.path())); + } + + Ok(paths) +} + +fn check_exercise_dir_files( + info_file: &InfoFile, + info_file_paths: hashbrown::HashSet, +) -> Result> { + let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); + 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) { + bail!("`{}` is expected to be an exercise file corresponding to some exercise in `info.toml`", path.display()); + } + + let file_name = file_name.to_string_lossy(); + names.insert(file_name[..file_name.len() - 3].to_string()); + 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", path.display()); + } + + let file_name = path.file_name().unwrap(); + if file_name == "README.md" { + continue; + } + + if !info_file_paths.contains(&path) { + bail!("`{}` is expected to be an exercise file corresponding to some exercise in `info.toml`", path.display()); + } + + let file_name = file_name.to_string_lossy(); + names.insert(file_name[..file_name.len() - 3].to_string()); + } + } + + Ok(names) +} + +fn check_info_file(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)?; + let names_in_exercises_dir = check_exercise_dir_files(info_file, info_file_paths)?; + + // Now, we know that every file has an exercise in `info.toml`. + // But we need to check that every exercise in `info.toml` has a file. + if names_in_exercises_dir.len() != info_file.exercises.len() { + for exercise_info in &info_file.exercises { + if !names_in_exercises_dir.contains(&exercise_info.name) { + bail!( + "No exercise file found for the exercise `{}`", + exercise_info.name, + ); + } + } + } + + Ok(()) +} + pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { let start_ind = cargo_toml .find("bin = [") @@ -62,11 +179,7 @@ fn check_cargo_toml( pub fn check() -> Result<()> { let info_file = InfoFile::parse()?; - 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 => (), - } + check_info_file(&info_file)?; if DEVELOPING_OFFICIAL_RUSTLINGS { check_cargo_toml( From b9167e9299bfe7c644cdbc2f6e933873f06b1c3f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 18:19:15 +0200 Subject: [PATCH 0745/1432] Remove redundant checks --- rustlings-macros/src/lib.rs | 11 ++--------- src/info_file.rs | 8 -------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d8da666e1a..d95a93a63c 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/info_file.rs b/src/info_file.rs index 18e77b9f4f..879609e3a9 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -64,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) } } From d42a6e741564313460fceb055d0aebe599cbe232 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 18:59:40 +0200 Subject: [PATCH 0746/1432] Print the path of the missing file --- src/dev/check.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index ac2c603d46..2c48f0e108 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -17,6 +17,7 @@ fn forbidden_char(input: &str) -> Option { fn check_info_file_exercises(info_file: &InfoFile) -> Result> { let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); + for exercise_info in &info_file.exercises { if let Some(c) = forbidden_char(&exercise_info.name) { bail!( @@ -49,6 +50,7 @@ fn check_exercise_dir_files( info_file_paths: hashbrown::HashSet, ) -> Result> { let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); + for entry in read_dir("exercises").context("Failed to open the `exercises` directory")? { let entry = entry.context("Failed to read the `exercises` directory")?; @@ -89,8 +91,11 @@ fn check_exercise_dir_files( bail!("`{}` is expected to be an exercise file corresponding to some exercise in `info.toml`", path.display()); } + // The file name must be valid Unicode with the `.rs` extension + // because it is part of the info file paths. let file_name = file_name.to_string_lossy(); - names.insert(file_name[..file_name.len() - 3].to_string()); + let file_name_without_rs_extension = file_name[..file_name.len() - 3].to_string(); + names.insert(file_name_without_rs_extension); } } @@ -112,10 +117,7 @@ fn check_info_file(info_file: &InfoFile) -> Result<()> { if names_in_exercises_dir.len() != info_file.exercises.len() { for exercise_info in &info_file.exercises { if !names_in_exercises_dir.contains(&exercise_info.name) { - bail!( - "No exercise file found for the exercise `{}`", - exercise_info.name, - ); + bail!("The file `{}` is missing", exercise_info.path()); } } } @@ -178,7 +180,6 @@ fn check_cargo_toml( pub fn check() -> Result<()> { let info_file = InfoFile::parse()?; - check_info_file(&info_file)?; if DEVELOPING_OFFICIAL_RUSTLINGS { From d6bb27ec2060863c38794b7c2511ca7399e29172 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 19:12:10 +0200 Subject: [PATCH 0747/1432] Check for empty field values --- src/dev/check.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dev/check.rs b/src/dev/check.rs index 2c48f0e108..3cb5345a2a 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -19,6 +19,9 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result Result Date: Wed, 17 Apr 2024 19:16:48 +0200 Subject: [PATCH 0748/1432] Trim before checking if the hint is empty --- src/dev/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 3cb5345a2a..4688e044b4 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -38,7 +38,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result Date: Wed, 17 Apr 2024 22:46:21 +0200 Subject: [PATCH 0749/1432] Require a main function in all exercises --- exercises/03_if/if1.rs | 4 + exercises/03_if/if2.rs | 4 + exercises/03_if/if3.rs | 4 + .../04_primitive_types/primitive_types4.rs | 4 + .../04_primitive_types/primitive_types6.rs | 4 + exercises/05_vecs/vecs1.rs | 4 + exercises/05_vecs/vecs2.rs | 4 + exercises/07_structs/structs1.rs | 4 + exercises/07_structs/structs2.rs | 4 + exercises/07_structs/structs3.rs | 4 + exercises/08_enums/enums3.rs | 4 + exercises/09_strings/strings3.rs | 4 + exercises/11_hashmaps/hashmaps1.rs | 4 + exercises/11_hashmaps/hashmaps2.rs | 6 +- exercises/11_hashmaps/hashmaps3.rs | 10 ++- exercises/12_options/options1.rs | 4 + exercises/12_options/options2.rs | 4 + exercises/13_error_handling/errors1.rs | 4 + exercises/13_error_handling/errors2.rs | 4 + exercises/13_error_handling/errors4.rs | 4 + exercises/13_error_handling/errors6.rs | 4 + exercises/14_generics/generics2.rs | 4 + exercises/15_traits/traits2.rs | 4 + exercises/15_traits/traits3.rs | 4 + exercises/15_traits/traits4.rs | 4 + exercises/17_tests/tests1.rs | 4 + exercises/17_tests/tests2.rs | 4 + exercises/17_tests/tests3.rs | 4 + exercises/17_tests/tests4.rs | 8 +- exercises/18_iterators/iterators2.rs | 4 + exercises/18_iterators/iterators3.rs | 4 + exercises/18_iterators/iterators4.rs | 4 + exercises/18_iterators/iterators5.rs | 4 + exercises/19_smart_pointers/cow1.rs | 4 + exercises/23_conversions/as_ref_mut.rs | 4 + exercises/quiz1.rs | 4 + exercises/quiz2.rs | 4 + exercises/quiz3.rs | 10 ++- src/dev/check.rs | 73 ++++++++++--------- 39 files changed, 199 insertions(+), 44 deletions(-) diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs index a1df66bbcd..dbd0d28582 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -10,6 +10,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 7b9c05f6a4..a1ed5c8b31 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -13,6 +13,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 caba172bb9..0b44c5ab47 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -27,6 +27,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_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index 8ed0a82a32..f99d889550 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -5,6 +5,10 @@ // Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand // for a hint. +fn main() { + // You can optionally experiment here. +} + #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs index 5f82f10fc5..48e84d34c0 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -6,6 +6,10 @@ // Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand // for a hint. +fn main() { + // You can optionally experiment here. +} + #[test] fn indexing_tuple() { let numbers = (1, 2, 3); diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index c64acbbd80..5f44cb2409 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -14,6 +14,10 @@ fn array_and_vec() -> ([i32; 4], Vec) { (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 d64d3d1640..1b16f0b43e 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -26,6 +26,10 @@ fn vec_map(v: &Vec) -> Vec { }).collect() } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs index 2978121440..cd8b81c981 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -14,6 +14,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 a7a2deca11..7e61e7525b 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -28,6 +28,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 9835b811ac..bd562a12ea 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -38,6 +38,10 @@ impl Package { } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index 580a553ee7..56c04fe6f6 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -45,6 +45,10 @@ impl State { } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index dedc081fa7..d53f654e31 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -18,6 +18,10 @@ fn replace_me(input: &str) -> String { ??? } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index 5a52f611d4..51146dfcba 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -24,6 +24,10 @@ fn fruit_basket() -> HashMap { 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 273064395a..47983f6b4b 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -41,6 +41,10 @@ fn fruit_basket(basket: &mut HashMap) { } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -79,7 +83,7 @@ mod tests { let count = basket.values().sum::(); 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 775a401483..33229096f6 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -5,9 +5,9 @@ // 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! @@ -42,6 +42,10 @@ fn build_scores_table(results: String) -> HashMap { 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 ba4b1cda9d..aecb123aef 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -14,6 +14,10 @@ fn maybe_icecream(time_of_day: u16) -> Option { ??? } +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 73f707e33f..d183d1d578 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -3,6 +3,10 @@ // Execute `rustlings hint options2` or use the `hint` watch subcommand for a // hint. +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { #[test] diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index 9767f2c889..7991c42060 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -9,6 +9,10 @@ // 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 { if name.is_empty() { // Empty names aren't allowed. diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 88d1bf436f..051516b9c0 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -29,6 +29,10 @@ pub fn total_cost(item_quantity: &str) -> Result { 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/errors4.rs b/exercises/13_error_handling/errors4.rs index 0e5c08bfe6..9449417093 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -19,6 +19,10 @@ impl PositiveNonzeroInteger { } } +fn main() { + // You can optionally experiment here. +} + #[test] fn test_creation() { assert!(PositiveNonzeroInteger::new(10).is_ok()); diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index de73a9a5fe..363a3b91b7 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -54,6 +54,10 @@ impl PositiveNonzeroInteger { } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod test { use super::*; diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index d50ed17445..068468ba35 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -16,6 +16,10 @@ impl Wrapper { } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index 9a2bc07a9e..18ebcb0899 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -14,6 +14,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 357f1d7f7a..8412afa216 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -23,6 +23,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 7242c4834d..18db0d6743 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -25,6 +25,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/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index bde21083f7..d32ace1e53 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -10,6 +10,10 @@ // Execute `rustlings hint tests1` or use the `hint` watch subcommand for a // hint. +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { #[test] diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index aea5c0e4c3..501c44bcb8 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -6,6 +6,10 @@ // Execute `rustlings hint tests2` or use the `hint` watch subcommand for a // hint. +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { #[test] diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index d815e0587d..a2093cf27a 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -11,6 +11,10 @@ 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 0972a5b4f6..a50323cfdf 100644 --- a/exercises/17_tests/tests4.rs +++ b/exercises/17_tests/tests4.rs @@ -7,7 +7,7 @@ struct Rectangle { width: i32, - height: i32 + height: i32, } impl Rectangle { @@ -16,10 +16,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/iterators2.rs b/exercises/18_iterators/iterators2.rs index 4ca7742e94..0ebd69a193 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -33,6 +33,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 f7da049c8d..3f5923cda2 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -43,6 +43,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 af3958c820..8fc87927ad 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -15,6 +15,10 @@ pub fn factorial(num: u64) -> u64 { // Execute `rustlings hint iterators4` for hints. } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index ceec536913..2604004a76 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -55,6 +55,10 @@ fn count_collection_iterator(collection: &[HashMap], value: Pr todo!(); } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index b24591b7f1..51e5fdb4e4 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -25,6 +25,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/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs index cd2c93beab..6fb7c2fced 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -26,6 +26,10 @@ fn num_sq(arg: &mut T) { ??? } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index b9e71f5925..55bc61f690 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -16,6 +16,10 @@ // Put your function here! // fn calculate_price_of_apples { +fn main() { + // You can optionally experiment here. +} + // Don't modify this function! #[test] fn verify_test() { diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 8ace3fe082..1d73ab9338 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -40,6 +40,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 24f708293b..780e130d78 100644 --- a/exercises/quiz3.rs +++ b/exercises/quiz3.rs @@ -24,11 +24,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/src/dev/check.rs b/src/dev/check.rs index 4688e044b4..d2e5fe144e 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,8 +1,9 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Error, Result}; use std::{ cmp::Ordering, - fs::{self, read_dir}, - path::PathBuf, + fs::{self, read_dir, OpenOptions}, + io::Read, + path::{Path, PathBuf}, }; use crate::{ @@ -18,6 +19,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result Result Result, -) -> Result> { - let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); +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) -> Result<()> { for entry in read_dir("exercises").context("Failed to open the `exercises` directory")? { let entry = entry.context("Failed to read the `exercises` directory")?; @@ -72,11 +91,9 @@ fn check_exercise_dir_files( } if !info_file_paths.contains(&path) { - bail!("`{}` is expected to be an exercise file corresponding to some exercise in `info.toml`", path.display()); + return Err(unexpected_file(&path)); } - let file_name = file_name.to_string_lossy(); - names.insert(file_name[..file_name.len() - 3].to_string()); continue; } @@ -89,7 +106,7 @@ fn check_exercise_dir_files( let path = entry.path(); if !entry.file_type().unwrap().is_file() { - bail!("Found {} but expected only files", path.display()); + bail!("Found `{}` but expected only files. Only one level of exercise nesting is allowed", path.display()); } let file_name = path.file_name().unwrap(); @@ -98,21 +115,15 @@ fn check_exercise_dir_files( } if !info_file_paths.contains(&path) { - bail!("`{}` is expected to be an exercise file corresponding to some exercise in `info.toml`", path.display()); + return Err(unexpected_file(&path)); } - - // The file name must be valid Unicode with the `.rs` extension - // because it is part of the info file paths. - let file_name = file_name.to_string_lossy(); - let file_name_without_rs_extension = file_name[..file_name.len() - 3].to_string(); - names.insert(file_name_without_rs_extension); } } - Ok(names) + Ok(()) } -fn check_info_file(info_file: &InfoFile) -> Result<()> { +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"), @@ -120,17 +131,7 @@ fn check_info_file(info_file: &InfoFile) -> Result<()> { } let info_file_paths = check_info_file_exercises(info_file)?; - let names_in_exercises_dir = check_exercise_dir_files(info_file, info_file_paths)?; - - // Now, we know that every file has an exercise in `info.toml`. - // But we need to check that every exercise in `info.toml` has a file. - if names_in_exercises_dir.len() != info_file.exercises.len() { - for exercise_info in &info_file.exercises { - if !names_in_exercises_dir.contains(&exercise_info.name) { - bail!("The file `{}` is missing", exercise_info.path()); - } - } - } + check_exercise_dir_files(info_file_paths)?; Ok(()) } @@ -190,7 +191,7 @@ fn check_cargo_toml( pub fn check() -> Result<()> { let info_file = InfoFile::parse()?; - check_info_file(&info_file)?; + check_exercises(&info_file)?; if DEVELOPING_OFFICIAL_RUSTLINGS { check_cargo_toml( From 2f810a4da67233716ad93e00afff6e8b260f4807 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 23:34:27 +0200 Subject: [PATCH 0750/1432] Clean up and unify exercises --- exercises/00_intro/intro1.rs | 5 - exercises/00_intro/intro2.rs | 5 - exercises/01_variables/variables1.rs | 5 - exercises/01_variables/variables2.rs | 5 - exercises/01_variables/variables3.rs | 5 - exercises/01_variables/variables4.rs | 5 - exercises/01_variables/variables5.rs | 5 - exercises/01_variables/variables6.rs | 5 - exercises/02_functions/functions1.rs | 5 - exercises/02_functions/functions2.rs | 5 - exercises/02_functions/functions3.rs | 5 - exercises/02_functions/functions4.rs | 5 - exercises/02_functions/functions5.rs | 5 - exercises/03_if/if1.rs | 4 - exercises/03_if/if2.rs | 4 - exercises/03_if/if3.rs | 4 - .../04_primitive_types/primitive_types1.rs | 5 +- .../04_primitive_types/primitive_types2.rs | 5 - .../04_primitive_types/primitive_types3.rs | 5 - .../04_primitive_types/primitive_types4.rs | 20 ++-- .../04_primitive_types/primitive_types5.rs | 5 - .../04_primitive_types/primitive_types6.rs | 24 ++-- exercises/05_vecs/vecs1.rs | 4 - exercises/05_vecs/vecs2.rs | 4 - .../06_move_semantics/move_semantics1.rs | 30 ++--- .../06_move_semantics/move_semantics2.rs | 34 +++--- .../06_move_semantics/move_semantics3.rs | 30 ++--- .../06_move_semantics/move_semantics4.rs | 34 +++--- .../06_move_semantics/move_semantics5.rs | 30 ++--- .../06_move_semantics/move_semantics6.rs | 5 - exercises/07_structs/structs1.rs | 5 - exercises/07_structs/structs2.rs | 5 - exercises/07_structs/structs3.rs | 5 - exercises/08_enums/enums1.rs | 4 - exercises/08_enums/enums2.rs | 5 - exercises/08_enums/enums3.rs | 5 - exercises/09_strings/strings1.rs | 5 - exercises/09_strings/strings2.rs | 5 - exercises/09_strings/strings3.rs | 5 - exercises/09_strings/strings4.rs | 6 +- exercises/10_modules/modules1.rs | 5 - exercises/10_modules/modules2.rs | 5 - exercises/10_modules/modules3.rs | 5 - exercises/11_hashmaps/hashmaps1.rs | 5 - exercises/11_hashmaps/hashmaps2.rs | 5 - exercises/11_hashmaps/hashmaps3.rs | 5 - exercises/12_options/options1.rs | 5 - exercises/12_options/options2.rs | 5 - exercises/12_options/options3.rs | 5 - exercises/13_error_handling/errors1.rs | 5 - exercises/13_error_handling/errors2.rs | 5 - exercises/13_error_handling/errors3.rs | 5 - exercises/13_error_handling/errors4.rs | 26 ++--- exercises/13_error_handling/errors5.rs | 5 - exercises/13_error_handling/errors6.rs | 5 - exercises/14_generics/generics1.rs | 5 - exercises/14_generics/generics2.rs | 5 - exercises/15_traits/traits1.rs | 5 - exercises/15_traits/traits2.rs | 4 - exercises/15_traits/traits3.rs | 5 - exercises/15_traits/traits4.rs | 5 - exercises/15_traits/traits5.rs | 5 - exercises/16_lifetimes/lifetimes1.rs | 5 - exercises/16_lifetimes/lifetimes2.rs | 5 - exercises/16_lifetimes/lifetimes3.rs | 10 +- exercises/17_tests/tests1.rs | 5 - exercises/17_tests/tests2.rs | 5 - exercises/17_tests/tests3.rs | 5 - exercises/17_tests/tests4.rs | 5 - exercises/18_iterators/iterators1.rs | 32 ++--- exercises/18_iterators/iterators2.rs | 5 - exercises/18_iterators/iterators3.rs | 5 - exercises/18_iterators/iterators4.rs | 6 - exercises/18_iterators/iterators5.rs | 5 - exercises/19_smart_pointers/arc1.rs | 4 - exercises/19_smart_pointers/box1.rs | 4 - exercises/19_smart_pointers/cow1.rs | 4 - exercises/19_smart_pointers/rc1.rs | 109 +++++++++--------- exercises/20_threads/threads1.rs | 5 - exercises/20_threads/threads2.rs | 5 - exercises/20_threads/threads3.rs | 38 +++--- exercises/21_macros/macros1.rs | 5 - exercises/21_macros/macros2.rs | 5 - exercises/21_macros/macros3.rs | 5 - exercises/21_macros/macros4.rs | 5 - exercises/22_clippy/clippy1.rs | 5 - exercises/22_clippy/clippy2.rs | 5 - exercises/22_clippy/clippy3.rs | 3 - exercises/23_conversions/as_ref_mut.rs | 5 - exercises/23_conversions/from_into.rs | 6 - exercises/23_conversions/from_str.rs | 5 - exercises/23_conversions/try_from_into.rs | 5 - exercises/23_conversions/using_as.rs | 5 - exercises/quiz1.rs | 31 ++--- exercises/quiz2.rs | 4 - exercises/quiz3.rs | 4 - 96 files changed, 242 insertions(+), 610 deletions(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 170d19589a..7000039295 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 84e0d75c05..c7a3ab2a57 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 56408f3532..3035bfaef4 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 0f417e017a..ce2dd85177 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 421c6b1549..488385ba58 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 68f8f50b02..67be12716d 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 7014c56874..3a745411b7 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 9f47682519..4746331ba6 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 2365f91b28..4e3b1036e8 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 64dbd66546..84e09cda92 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 503712122e..66fb6d32e3 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 6b449edf21..06d3b1bbfd 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 0c963223d1..3bb5e52af8 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 dbd0d28582..52dee0b54a 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -1,7 +1,3 @@ -// if1.rs -// -// Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. - pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! // If both numbers are equal, any of them can be returned. diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index a1ed5c8b31..a06bba559b 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -1,9 +1,5 @@ -// if2.rs -// // Step 1: Make me compile! // Step 2: Get the bar_for_fuzz and default_to_baz tests passing! -// -// Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint. pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index 0b44c5ab47..1d9b7c25ff 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -1,7 +1,3 @@ -// if3.rs -// -// Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint. - pub fn animal_habitat(animal: &str) -> &'static str { let identifier = if animal == "crab" { 1 diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index f9169c8489..0002651d8b 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 1911b12a1f..29c74718ad 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 70a8cc20bc..5095fc4ae4 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 f99d889550..c583ae1388 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -1,19 +1,19 @@ -// primitive_types4.rs -// // Get a slice out of Array a where the ??? is so that the test passes. -// -// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand -// for a hint. fn main() { // You can optionally experiment here. } -#[test] -fn slice_out_of_array() { - let a = [1, 2, 3, 4, 5]; +#[cfg(test)] +mod tests { + use super::*; - let nice_slice = ??? + #[test] + fn slice_out_of_array() { + let a = [1, 2, 3, 4, 5]; - assert_eq!([2, 3, 4], nice_slice) + let nice_slice = ??? + + assert_eq!([2, 3, 4], nice_slice) + } } diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs index 5754a3d89f..f2216a55d1 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 48e84d34c0..83cec24b5d 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -1,21 +1,21 @@ -// primitive_types6.rs -// // Use a tuple index to access the second element of `numbers`. You can put the // expression for the second element where ??? is so that the test passes. -// -// Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand -// for a hint. fn main() { // You can optionally experiment here. } -#[test] -fn indexing_tuple() { - let numbers = (1, 2, 3); - // Replace below ??? with the tuple indexing syntax. - let second = ???; +#[cfg(test)] +mod tests { + use super::*; - assert_eq!(2, second, - "This is not the 2nd number in the tuple!") + #[test] + fn indexing_tuple() { + let numbers = (1, 2, 3); + // Replace below ??? with the tuple indexing syntax. + let second = ???; + + assert_eq!(2, second, + "This is not the 2nd number in the tuple!") + } } diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index 5f44cb2409..ddcad84b9f 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -1,11 +1,7 @@ -// vecs1.rs -// // Your task is to create a `Vec` which holds the exact same elements as in the // array `a`. // // Make me compile and pass the test! -// -// Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. fn array_and_vec() -> ([i32; 4], Vec) { let a = [10, 20, 30, 40]; // a plain array diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index 1b16f0b43e..e72209c443 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -1,11 +1,7 @@ -// vecs2.rs -// // A Vec of even numbers is given. Your task is to complete the loop so that // each number in the Vec is multiplied by 2. // // Make me pass the test! -// -// Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. fn vec_loop(mut v: Vec) -> Vec { for element in v.iter_mut() { diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs index c612ba937e..8c3fe3a7f8 100644 --- a/exercises/06_move_semantics/move_semantics1.rs +++ b/exercises/06_move_semantics/move_semantics1.rs @@ -1,21 +1,25 @@ -// move_semantics1.rs -// -// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand -// for a hint. +fn fill_vec(vec: Vec) -> Vec { + let vec = vec; -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; + vec.push(88); - let vec1 = fill_vec(vec0); + vec +} - assert_eq!(vec1, vec![22, 44, 66, 88]); +fn main() { + // You can optionally experiment here. } -fn fill_vec(vec: Vec) -> Vec { - let vec = vec; +#[cfg(test)] +mod tests { + use super::*; - vec.push(88); + #[test] + fn move_semantics1() { + let vec0 = vec![22, 44, 66]; - vec + let vec1 = fill_vec(vec0); + + assert_eq!(vec1, vec![22, 44, 66, 88]); + } } diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index 3457d111f1..d0879113c2 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -1,19 +1,4 @@ -// move_semantics2.rs -// // Make the test pass by finding a way to keep both Vecs separate! -// -// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand -// for a hint. - -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; - - let vec1 = fill_vec(vec0); - - assert_eq!(vec0, vec![22, 44, 66]); - assert_eq!(vec1, vec![22, 44, 66, 88]); -} fn fill_vec(vec: Vec) -> Vec { let mut vec = vec; @@ -22,3 +7,22 @@ fn fill_vec(vec: Vec) -> Vec { vec } + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics2() { + let vec0 = vec![22, 44, 66]; + + let vec1 = fill_vec(vec0); + + assert_eq!(vec0, vec![22, 44, 66]); + assert_eq!(vec1, vec![22, 44, 66, 88]); + } +} diff --git a/exercises/06_move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs index 9415eb159a..24e35971a2 100644 --- a/exercises/06_move_semantics/move_semantics3.rs +++ b/exercises/06_move_semantics/move_semantics3.rs @@ -1,22 +1,26 @@ -// move_semantics3.rs -// // Make me compile without adding new lines -- just changing existing lines! (no // lines with multiple semicolons necessary!) -// -// Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand -// for a hint. -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; +fn fill_vec(vec: Vec) -> Vec { + vec.push(88); - let vec1 = fill_vec(vec0); + vec +} - assert_eq!(vec1, vec![22, 44, 66, 88]); +fn main() { + // You can optionally experiment here. } -fn fill_vec(vec: Vec) -> Vec { - vec.push(88); +#[cfg(test)] +mod tests { + use super::*; - vec + #[test] + fn move_semantics3() { + let vec0 = vec![22, 44, 66]; + + let vec1 = fill_vec(vec0); + + assert_eq!(vec1, vec![22, 44, 66, 88]); + } } diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index 1509f5d23f..b6622244a0 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -1,20 +1,6 @@ -// move_semantics4.rs -// // Refactor this code so that instead of passing `vec0` into the `fill_vec` // function, the Vector gets created in the function itself and passed back to -// the main function. -// -// Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand -// for a hint. - -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; - - let vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); -} +// the test function. // `fill_vec()` no longer takes `vec: Vec` as argument - don't change this! fn fill_vec() -> Vec { @@ -25,3 +11,21 @@ fn fill_vec() -> Vec { vec } + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics4() { + let vec0 = vec![22, 44, 66]; + + let vec1 = fill_vec(vec0); + + assert_eq!(vec1, vec![22, 44, 66, 88]); + } +} diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index c84d2fea2c..b34560ab54 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 6059e61c52..2ad71db29a 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 cd8b81c981..62f1421933 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -1,9 +1,4 @@ -// structs1.rs -// // Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a -// hint. struct ColorClassicStruct { // TODO: Something goes here diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs index 7e61e7525b..451dbe7607 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -1,9 +1,4 @@ -// structs2.rs -// // Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a -// hint. #[derive(Debug)] struct Order { diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index bd562a12ea..10adb48772 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,11 +1,6 @@ -// structs3.rs -// // Structs contain data, but can also have logic. In this exercise we have // defined the Package struct and we want to test some logic attached to it. // Make the code compile and the tests pass! -// -// Execute `rustlings hint structs3` or use the `hint` watch subcommand for a -// hint. #[derive(Debug)] struct Package { diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index 330269ca44..d63de83f5a 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 f0e4e6d341..f3b803ff02 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 56c04fe6f6..edac3dfba1 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -1,9 +1,4 @@ -// enums3.rs -// // Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a -// hint. enum Message { // TODO: implement the message variant types based on their usage below diff --git a/exercises/09_strings/strings1.rs b/exercises/09_strings/strings1.rs index a1255a329b..de762ebfd6 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 ba76fe656a..4768278171 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 d53f654e31..f83a5310db 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -1,8 +1,3 @@ -// strings3.rs -// -// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a -// hint. - fn trim_me(input: &str) -> String { // TODO: Remove whitespace from both ends of a string! ??? diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index a034aa49bc..1f3d88b789 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 c750946c07..931a3e26cb 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 4d3106c4e6..5f8b0d518f 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 c211a7695f..eff24a9c5f 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 51146dfcba..e646ed71be 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -1,5 +1,3 @@ -// hashmaps1.rs -// // A basket of fruits in the form of a hash map needs to be defined. The key // represents the name of the fruit and the value represents how many of that // particular fruit is in the basket. You have to put at least three different @@ -7,9 +5,6 @@ // of all the fruits should be at least five. // // Make me compile and pass the tests! -// -// Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index 47983f6b4b..e6380d94f6 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -1,5 +1,3 @@ -// hashmaps2.rs -// // We're collecting different fruits to bake a delicious fruit cake. For this, // we have a basket, which we'll represent in the form of a hash map. The key // represents the name of each fruit we collect and the value represents how @@ -10,9 +8,6 @@ // to insert any more of these fruits! // // Make me pass the tests! -// -// Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 33229096f6..070c37095c 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -1,5 +1,3 @@ -// hashmaps3.rs -// // A list of scores (one per line) of a soccer match is given. Each line is of // the form : ",,," // Example: England,France,4,2 (England scored 4 goals, France 2). @@ -11,9 +9,6 @@ // complete it to pass the test. // // Make me pass the tests! -// -// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index aecb123aef..b7cf7b0b62 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -1,8 +1,3 @@ -// options1.rs -// -// Execute `rustlings hint options1` or use the `hint` watch subcommand for a -// hint. - // This function returns how much icecream there is left in the fridge. // If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it // all, so there'll be no more left :( diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index d183d1d578..01f84c5838 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -1,8 +1,3 @@ -// options2.rs -// -// Execute `rustlings hint options2` or use the `hint` watch subcommand for a -// hint. - fn main() { // You can optionally experiment here. } diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 7922ef92e0..5b70a79268 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 7991c42060..15a3716df0 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -1,13 +1,8 @@ -// errors1.rs -// // This function refuses to generate text to be printed on a nametag if you pass // it an empty string. It'd be nicer if it explained what the problem was, // instead of just sometimes returning `None`. Thankfully, Rust has a similar // construct to `Option` that can be used to express error conditions. Let's use // it! -// -// Execute `rustlings hint errors1` or use the `hint` watch subcommand for a -// hint. fn main() { // You can optionally experiment here. diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 051516b9c0..e39aa959dd 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -1,5 +1,3 @@ -// errors2.rs -// // Say we're writing a game where you can buy items with tokens. All items cost // 5 tokens, and whenever you purchase items there is a processing fee of 1 // token. A player of the game will type in how many items they want to buy, and @@ -15,9 +13,6 @@ // // There are at least two ways to implement this that are both correct-- but one // is a lot shorter! -// -// Execute `rustlings hint errors2` or use the `hint` watch subcommand for a -// hint. use std::num::ParseIntError; diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index 56bb31b1b0..5661f17baf 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 9449417093..993d42a17b 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -1,8 +1,3 @@ -// errors4.rs -// -// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a -// hint. - #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); @@ -23,12 +18,17 @@ fn main() { // You can optionally experiment here. } -#[test] -fn test_creation() { - assert!(PositiveNonzeroInteger::new(10).is_ok()); - assert_eq!( - Err(CreationError::Negative), - PositiveNonzeroInteger::new(-10) - ); - assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creation() { + assert!(PositiveNonzeroInteger::new(10).is_ok()); + assert_eq!( + Err(CreationError::Negative), + PositiveNonzeroInteger::new(-10) + ); + assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); + } } diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 0bcb4b8ca5..719256262a 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 363a3b91b7..8b08e08677 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -1,13 +1,8 @@ -// errors6.rs -// // Using catch-all error types like `Box` isn't recommended // for library code, where callers might want to make decisions based on the // error content, instead of printing it out or propagating it further. Here, we // define a custom error type to make it possible for callers to decide what to // do next when our function returns an error. -// -// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a -// hint. use std::num::ParseIntError; diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs index 545fd95c01..c023e644a8 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 068468ba35..cbb9b5f982 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -1,10 +1,5 @@ -// generics2.rs -// // This powerful wrapper provides the ability to store a positive integer value. // Rewrite it using generics so that it supports wrapping ANY type. -// -// Execute `rustlings hint generics2` or use the `hint` watch subcommand for a -// hint. struct Wrapper { value: u32, diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs index c51d3b88b7..b17c9c6ebb 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 18ebcb0899..170779b256 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -1,12 +1,8 @@ -// traits2.rs -// // Your task is to implement the trait `AppendBar` for a vector of strings. To // implement this trait, consider for a moment what it means to 'append "Bar"' // to a vector of strings. // // No boiler plate code this time, you can do this! -// -// Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. trait AppendBar { fn append_bar(self) -> Self; diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 8412afa216..9a2365ae46 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -1,12 +1,7 @@ -// traits3.rs -// // Your task is to implement the Licensed trait for both structures and have // them return the same information without writing the same function twice. // // Consider what you can add to the Licensed trait. -// -// Execute `rustlings hint traits3` or use the `hint` watch subcommand for a -// hint. pub trait Licensed { fn licensing_info(&self) -> String; diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index 18db0d6743..7af30b5752 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -1,11 +1,6 @@ -// traits4.rs -// // Your task is to replace the '??' sections so the code compiles. // // Don't change any line other than the marked one. -// -// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a -// hint. pub trait Licensed { fn licensing_info(&self) -> String { diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index f258d32738..9a45bb7612 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 4f544b41c3..d34f3abd3e 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 33b5565fba..6e329e6d90 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 de6005ecef..9b631cab42 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 d32ace1e53..854a135849 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -1,14 +1,9 @@ -// tests1.rs -// // Tests are important to ensure that your code does what you think it should // do. Tests can be run on this file with the following command: rustlings run // tests1 // // This test has a problem with it -- make the test compile! Make the test pass! // Make the test fail! -// -// Execute `rustlings hint tests1` or use the `hint` watch subcommand for a -// hint. fn main() { // You can optionally experiment here. diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index 501c44bcb8..f0899e1b96 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -1,10 +1,5 @@ -// tests2.rs -// // This test has a problem with it -- make the test compile! Make the test pass! // Make the test fail! -// -// Execute `rustlings hint tests2` or use the `hint` watch subcommand for a -// hint. fn main() { // You can optionally experiment here. diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index a2093cf27a..3b4e199076 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -1,11 +1,6 @@ -// tests3.rs -// // This test isn't testing our function -- make it do that in such a way that // the test passes. Then write a second test that tests whether we get the // result we expect to get when we call `is_even(5)`. -// -// Execute `rustlings hint tests3` or use the `hint` watch subcommand for a -// hint. pub fn is_even(num: i32) -> bool { num % 2 == 0 diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs index a50323cfdf..35a9a3b50f 100644 --- a/exercises/17_tests/tests4.rs +++ b/exercises/17_tests/tests4.rs @@ -1,9 +1,4 @@ -// tests4.rs -// // Make sure that we're testing for the correct conditions! -// -// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a -// hint. struct Rectangle { width: i32, diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 7ec7da2caf..52b704d500 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 0ebd69a193..df1fa8387d 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -1,10 +1,5 @@ -// iterators2.rs -// // In this exercise, you'll learn some of the unique advantages that iterators // can offer. Follow the steps to complete the exercise. -// -// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a -// hint. // Step 1. // Complete the `capitalize_first` function. diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 3f5923cda2..9f106aa8a9 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,13 +1,8 @@ -// iterators3.rs -// // This is a bigger exercise than most of the others! You can do it! Here is // your mission, should you choose to accept it: // 1. Complete the divide function to get the first four tests to pass. // 2. Get the remaining tests to pass by completing the result_with_list and // list_of_results functions. -// -// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a -// hint. #[derive(Debug, PartialEq, Eq)] pub enum DivisionError { diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 8fc87927ad..60c7b8d123 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,8 +1,3 @@ -// iterators4.rs -// -// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a -// hint. - pub fn factorial(num: u64) -> u64 { // Complete this function to return the factorial of num // Do not use: @@ -12,7 +7,6 @@ pub fn factorial(num: u64) -> u64 { // - additional variables // For an extra challenge, don't use: // - recursion - // Execute `rustlings hint iterators4` for hints. } fn main() { diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index 2604004a76..4f052d5129 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -1,5 +1,3 @@ -// iterators5.rs -// // Let's define a simple model to track Rustlings exercise progress. Progress // will be modelled using a hash map. The name of the exercise is the key and // the progress is the value. Two counting functions were created to count the @@ -7,9 +5,6 @@ // functionality using iterators. Try not to use imperative loops (for, while). // Only the two iterator methods (count_iterator and count_collection_iterator) // need to be modified. -// -// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index 0647eea7dc..7b31fa82aa 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 2abc0249c2..226a117795 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 51e5fdb4e4..754c0bac92 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -1,5 +1,3 @@ -// cow1.rs -// // This exercise explores the Cow, or Clone-On-Write type. Cow is a // clone-on-write smart pointer. It can enclose and provide immutable access to // borrowed data, and clone the data lazily when mutation or ownership is @@ -9,8 +7,6 @@ // This exercise is meant to show you what to expect when passing data to Cow. // Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the // TODO markers. -// -// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint. use std::borrow::Cow; diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index e96e6255a6..19de3db277 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -1,5 +1,3 @@ -// rc1.rs -// // In this exercise, we want to express the concept of multiple owners via the // Rc type. This is a model of our solar system - there is a Sun type and // multiple Planets. The Planets take ownership of the sun, indicating that they @@ -7,8 +5,6 @@ // // Make this code compile by using the proper Rc primitives to express that the // sun has multiple owners. -// -// Execute `rustlings hint rc1` or use the `hint` watch subcommand for a hint. use std::rc::Rc; @@ -33,71 +29,80 @@ impl Planet { } } -#[test] fn main() { - let sun = Rc::new(Sun {}); - println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + // You can optionally experiment here. +} - let mercury = Planet::Mercury(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 2 references - mercury.details(); +#[cfg(test)] +mod tests { + use super::*; - let venus = Planet::Venus(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 3 references - venus.details(); + #[test] + fn rc1() { + let sun = Rc::new(Sun {}); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference - let earth = Planet::Earth(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 4 references - earth.details(); + let mercury = Planet::Mercury(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + mercury.details(); - let mars = Planet::Mars(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 5 references - mars.details(); + let venus = Planet::Venus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + venus.details(); - let jupiter = Planet::Jupiter(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 6 references - jupiter.details(); + let earth = Planet::Earth(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + earth.details(); - // TODO - let saturn = Planet::Saturn(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 7 references - saturn.details(); + let mars = Planet::Mars(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + mars.details(); - // TODO - let uranus = Planet::Uranus(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 8 references - uranus.details(); + let jupiter = Planet::Jupiter(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + jupiter.details(); - // TODO - let neptune = Planet::Neptune(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 9 references - neptune.details(); + // TODO + let saturn = Planet::Saturn(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + saturn.details(); - assert_eq!(Rc::strong_count(&sun), 9); + // TODO + let uranus = Planet::Uranus(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + uranus.details(); - drop(neptune); - println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + // TODO + let neptune = Planet::Neptune(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 9 references + neptune.details(); - drop(uranus); - println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + assert_eq!(Rc::strong_count(&sun), 9); - drop(saturn); - println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + drop(neptune); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references - drop(jupiter); - println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + drop(uranus); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references - drop(mars); - println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + drop(saturn); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + drop(jupiter); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + drop(mars); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references - assert_eq!(Rc::strong_count(&sun), 1); + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + assert_eq!(Rc::strong_count(&sun), 1); + } } diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index be1301d738..bf0b8e0d83 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 13cb840e36..2bdeba94e7 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 35b914ac27..13abc45024 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -1,8 +1,3 @@ -// threads3.rs -// -// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a -// hint. - use std::sync::mpsc; use std::sync::Arc; use std::thread; @@ -42,20 +37,29 @@ fn send_tx(q: Queue, tx: mpsc::Sender) -> () { }); } -#[test] fn main() { - let (tx, rx) = mpsc::channel(); - let queue = Queue::new(); - let queue_length = queue.length; + // You can optionally experiment here. +} - send_tx(queue, tx); +#[cfg(test)] +mod tests { + use super::*; - let mut total_received: u32 = 0; - for received in rx { - println!("Got: {}", received); - total_received += 1; - } + #[test] + fn threads3() { + let (tx, rx) = mpsc::channel(); + let queue = Queue::new(); + let queue_length = queue.length; + + send_tx(queue, tx); - println!("total numbers received: {}", total_received); - assert_eq!(total_received, queue_length) + let mut total_received: u32 = 0; + for received in rx { + println!("Got: {}", received); + total_received += 1; + } + + println!("total numbers received: {}", total_received); + assert_eq!(total_received, queue_length) + } } diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs index 65986db00c..1d415cb161 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 b7c37fd9ce..f16712be6d 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 92a19227c4..405c397aab 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 83a6e44f6a..03ece0808e 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 1e0f42e2d0..f1eaa83141 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 37ac089ea3..c7d400d1e5 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 6a6a36b5f7..fd829cf604 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 6fb7c2fced..c725dfded5 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -1,11 +1,6 @@ -// as_ref_mut.rs -// // AsRef and AsMut allow for cheap reference-to-reference conversions. Read more // about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and // https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. -// -// Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a -// hint. // Obtain the number of bytes (not characters) in the given argument. // TODO: Add the AsRef trait appropriately as a trait bound. diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index d2a1609e71..9df10da906 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -1,11 +1,6 @@ -// from_into.rs -// // The From trait is used for value-to-value conversions. If From is implemented // correctly for a type, the Into trait should work conversely. You can read // more about it at https://doc.rust-lang.org/std/convert/trait.From.html -// -// Execute `rustlings hint from_into` or use the `hint` watch subcommand for a -// hint. #[derive(Debug)] struct Person { @@ -24,7 +19,6 @@ impl Default for Person { } } - // Your task is to complete this implementation in order for the line `let p1 = // Person::from("Mark,20")` to compile. Please note that you'll need to parse the // age component into a `usize` with something like `"4".parse::()`. The diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index ed91ca5c0c..58270f0246 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 23166555a0..da45e5a47b 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 9f617ec58a..94b1bb31a3 100644 --- a/exercises/23_conversions/using_as.rs +++ b/exercises/23_conversions/using_as.rs @@ -1,14 +1,9 @@ -// using_as.rs -// // Type casting in Rust is done via the usage of the `as` operator. Please note // that the `as` operator is not only used when type casting. It also helps with // renaming imports. // // The goal is to make sure that the division does not fail to compile and // returns the proper type. -// -// Execute `rustlings hint using_as` or use the `hint` watch subcommand for a -// hint. fn average(values: &[f64]) -> f64 { let total = values.iter().sum::(); diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 55bc61f690..edb672ee20 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -1,5 +1,3 @@ -// quiz1.rs -// // This is a quiz for the following sections: // - Variables // - Functions @@ -10,8 +8,6 @@ // - If Mary buys more than 40 apples, each apple only costs 1 rustbuck! // Write a function that calculates the price of an order of apples given the // quantity bought. -// -// No hints this time ;) // Put your function here! // fn calculate_price_of_apples { @@ -20,16 +16,21 @@ fn main() { // You can optionally experiment here. } -// Don't modify this function! -#[test] -fn verify_test() { - let price1 = calculate_price_of_apples(35); - let price2 = calculate_price_of_apples(40); - let price3 = calculate_price_of_apples(41); - let price4 = calculate_price_of_apples(65); +#[cfg(test)] +mod tests { + use super::*; + + // Don't modify this test! + #[test] + fn verify_test() { + let price1 = calculate_price_of_apples(35); + let price2 = calculate_price_of_apples(40); + let price3 = calculate_price_of_apples(41); + let price4 = calculate_price_of_apples(65); - assert_eq!(70, price1); - assert_eq!(80, price2); - assert_eq!(41, price3); - assert_eq!(65, price4); + assert_eq!(70, price1); + assert_eq!(80, price2); + assert_eq!(41, price3); + assert_eq!(65, price4); + } } diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 1d73ab9338..0a29e78114 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -1,5 +1,3 @@ -// quiz2.rs -// // This is a quiz for the following sections: // - Strings // - Vecs @@ -17,8 +15,6 @@ // - The input is going to be a Vector of a 2-length tuple, // the first element is the string, the second one is the command. // - The output element is going to be a Vector of strings. -// -// No hints this time! pub enum Command { Uppercase, diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs index 780e130d78..f255cb5d5d 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, From 634e17a5abdd5b03740cfb5ab690e2b8762cf0c3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 23:37:31 +0200 Subject: [PATCH 0751/1432] Fix tests --- src/exercise.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 60a65bb65d..bed247eb92 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -3,6 +3,7 @@ use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, fs, + path::Path, process::{Command, Output}, }; @@ -51,7 +52,7 @@ impl Exercise { cmd.arg(command); // A hack to make `cargo run` work when developing Rustlings. - if DEVELOPING_OFFICIAL_RUSTLINGS { + if DEVELOPING_OFFICIAL_RUSTLINGS && Path::new("tests").exists() { cmd.arg("--manifest-path").arg("dev/Cargo.toml"); } From d64836f3170c443c6fb5f131930223831c6d724c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 01:49:32 +0200 Subject: [PATCH 0752/1432] Avoid an unneeded syscall --- src/embedded.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index 866b12b861..eae3099800 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) } } From 9f5be60b400f7af505770d9001731f592cb552bb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 11:20:51 +0200 Subject: [PATCH 0753/1432] Use git stash to reset third-party exercises --- src/exercise.rs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index bed247eb92..7f924f900e 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,10 +1,10 @@ -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, fs, path::Path, - process::{Command, Output}, + process::{Command, Output, Stdio}, }; use crate::{ @@ -88,9 +88,31 @@ 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}")) + if Path::new("info.toml").exists() { + let output = Command::new("git") + .arg("stash") + .arg("push") + .arg("--") + .arg(self.path) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .output() + .with_context(|| format!("Failed to run `git stash push -- {}`", self.path))?; + + if !output.status.success() { + bail!( + "`git stash push -- {}` didn't run successfully: {}", + self.path, + String::from_utf8_lossy(&output.stderr), + ); + } + } else { + EMBEDDED_FILES + .write_exercise_to_disk(self.path, WriteStrategy::Overwrite) + .with_context(|| format!("Failed to reset the exercise {self}"))?; + } + + Ok(()) } pub fn terminal_link(&self) -> StyledContent> { From 2e9b9a9f130c89e9b2856f17c24cdab841929b28 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 11:21:39 +0200 Subject: [PATCH 0754/1432] Move constant --- src/app_state.rs | 24 +++++++++++++++++++++++- src/main.rs | 22 ---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 54c02d690b..2ab7526892 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -9,7 +9,7 @@ use std::{ io::{Read, StdoutLock, Write}, }; -use crate::{exercise::Exercise, info_file::ExerciseInfo, FENISH_LINE}; +use crate::{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"; @@ -275,3 +275,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/main.rs b/src/main.rs index fa5542af25..3f3fbc1dcf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -190,25 +190,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 FENISH_LINE: &str = "+----------------------------------------------------+ -| You made it to the Fe-nish line! | -+-------------------------- ------------------------+ - \\/\x1b[31m - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ - β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ - β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ - β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ - β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ - β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m - -"; From 1eac00e89ae9e0ed6969f5036e9c8c43e4435e86 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 11:28:28 +0200 Subject: [PATCH 0755/1432] Disable init command during development --- src/dev.rs | 12 ++++++++++-- src/main.rs | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index feca99e5dc..1430f11e55 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,6 +1,8 @@ -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use clap::Subcommand; +use crate::DEVELOPING_OFFICIAL_RUSTLINGS; + mod check; mod init; mod update; @@ -15,7 +17,13 @@ pub enum DevCommands { impl DevCommands { pub fn run(self) -> Result<()> { match self { - DevCommands::Init => init::init().context(INIT_ERR), + 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(), } diff --git a/src/main.rs b/src/main.rs index 3f3fbc1dcf..d40f978c9a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -79,6 +79,10 @@ fn main() -> Result<()> { match args.command { Some(Subcommands::Init) => { + if DEVELOPING_OFFICIAL_RUSTLINGS { + bail!("Disabled while developing the official Rustlings"); + } + return init::init().context("Initialization failed"); } Some(Subcommands::Dev(dev_command)) => return dev_command.run(), From 2566f9aaf674458f6faff7e8aaf77bb108b7d56c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 11:31:08 +0200 Subject: [PATCH 0756/1432] Place mods under all imports --- src/list.rs | 4 ++-- src/main.rs | 4 ++-- src/watch.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/list.rs b/src/list.rs index 2bb813d8d4..790c02fe4e 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/main.rs b/src/main.rs index d40f978c9a..c8940af246 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,8 @@ use std::{ process::exit, }; +use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; + mod app_state; mod dev; mod embedded; @@ -22,8 +24,6 @@ mod progress_bar; mod run; mod watch; -use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; - const CURRENT_FORMAT_VERSION: u8 = 1; const DEVELOPING_OFFICIAL_RUSTLINGS: bool = { #[allow(unused_assignments, unused_mut)] diff --git a/src/watch.rs b/src/watch.rs index d20e552ef0..5ebe526fb9 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 }, From f04089b8bc724d41dcd26117c85f32328b5eb597 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 11:40:54 +0200 Subject: [PATCH 0757/1432] Only take a reference --- src/dev/check.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index d2e5fe144e..e95eb3c6f5 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -79,7 +79,7 @@ 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) -> Result<()> { +fn check_exercise_dir_files(info_file_paths: &hashbrown::HashSet) -> Result<()> { for entry in read_dir("exercises").context("Failed to open the `exercises` directory")? { let entry = entry.context("Failed to read the `exercises` directory")?; @@ -131,7 +131,7 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { } let info_file_paths = check_info_file_exercises(info_file)?; - check_exercise_dir_files(info_file_paths)?; + check_exercise_dir_files(&info_file_paths)?; Ok(()) } From 01e6732e4d920d9a1859e05fa28382e4307571af Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 12:41:17 +0200 Subject: [PATCH 0758/1432] Improve resetting --- src/app_state.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++- src/exercise.rs | 38 +++---------------------------- src/list/state.rs | 13 +++++------ src/main.rs | 6 ++--- 4 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 2ab7526892..9868781801 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}; +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, + 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 { if self.current_exercise_ind == self.exercises.len() - 1 { // The last exercise is done. diff --git a/src/exercise.rs b/src/exercise.rs index 7f924f900e..eb7b725ee7 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,17 +1,13 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, fs, path::Path, - process::{Command, Output, Stdio}, + process::{Command, Output}, }; -use crate::{ - embedded::{WriteStrategy, EMBEDDED_FILES}, - info_file::Mode, - DEVELOPING_OFFICIAL_RUSTLINGS, -}; +use crate::{info_file::Mode, DEVELOPING_OFFICIAL_RUSTLINGS}; pub struct TerminalFileLink<'a> { path: &'a str, @@ -87,34 +83,6 @@ impl Exercise { } } - pub fn reset(&self) -> Result<()> { - if Path::new("info.toml").exists() { - let output = Command::new("git") - .arg("stash") - .arg("push") - .arg("--") - .arg(self.path) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .output() - .with_context(|| format!("Failed to run `git stash push -- {}`", self.path))?; - - if !output.status.success() { - bail!( - "`git stash push -- {}` didn't run successfully: {}", - self.path, - String::from_utf8_lossy(&output.stderr), - ); - } - } else { - EMBEDDED_FILES - .write_exercise_to_disk(self.path, WriteStrategy::Overwrite) - .with_context(|| format!("Failed to reset the exercise {self}"))?; - } - - Ok(()) - } - pub fn terminal_link(&self) -> StyledContent> { style(TerminalFileLink { path: self.path }) .underlined() diff --git a/src/list/state.rs b/src/list/state.rs index 2a1fef18f9..0253bb97c2 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 c8940af246..04697379fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -158,10 +158,8 @@ fn main() -> Result<()> { } 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)?; From cc35a8431ff58502cdca08393e28578f6a4e9b53 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 13:07:07 +0200 Subject: [PATCH 0759/1432] Remove "All-Contributors" --- .all-contributorsrc | 2743 ------------------------------------------- AUTHORS.md | 397 ------- 2 files changed, 3140 deletions(-) delete mode 100644 .all-contributorsrc delete mode 100644 AUTHORS.md diff --git a/.all-contributorsrc b/.all-contributorsrc deleted file mode 100644 index 36952f7e7e..0000000000 --- a/.all-contributorsrc +++ /dev/null @@ -1,2743 +0,0 @@ -{ - "files": [ - "AUTHORS.md" - ], - "imageSize": 100, - "commit": false, - "contributors": [ - { - "login": "carols10cents", - "name": "Carol (Nichols || Goulding)", - "avatar_url": "https://avatars2.githubusercontent.com/u/193874?v=4", - "profile": "http://carol-nichols.com", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "QuietMisdreavus", - "name": "QuietMisdreavus", - "avatar_url": "https://avatars2.githubusercontent.com/u/5217170?v=4", - "profile": "https://twitter.com/QuietMisdreavus", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "robertlugg", - "name": "Robert M Lugg", - "avatar_url": "https://avatars0.githubusercontent.com/u/6054540?v=4", - "profile": "https://github.com/robertlugg", - "contributions": [ - "content" - ] - }, - { - "login": "hynek", - "name": "Hynek Schlawack", - "avatar_url": "https://avatars3.githubusercontent.com/u/41240?v=4", - "profile": "https://hynek.me/about/", - "contributions": [ - "code" - ] - }, - { - "login": "spacekookie", - "name": "Katharina Fey", - "avatar_url": "https://avatars0.githubusercontent.com/u/7669898?v=4", - "profile": "https://spacekookie.de", - "contributions": [ - "code" - ] - }, - { - "login": "lukabavdaz", - "name": "lukabavdaz", - "avatar_url": "https://avatars0.githubusercontent.com/u/9624558?v=4", - "profile": "https://github.com/lukabavdaz", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "evestera", - "name": "Erik Vesteraas", - "avatar_url": "https://avatars2.githubusercontent.com/u/4187449?v=4", - "profile": "http://vestera.as", - "contributions": [ - "code" - ] - }, - { - "login": "Delet0r", - "name": "delet0r", - "avatar_url": "https://avatars1.githubusercontent.com/u/23195618?v=4", - "profile": "https://github.com/Delet0r", - "contributions": [ - "code" - ] - }, - { - "login": "shaunbennett", - "name": "Shaun Bennett", - "avatar_url": "https://avatars1.githubusercontent.com/u/10522375?v=4", - "profile": "http://phinary.ca", - "contributions": [ - "code" - ] - }, - { - "login": "abagshaw", - "name": "Andrew Bagshaw", - "avatar_url": "https://avatars2.githubusercontent.com/u/8594541?v=4", - "profile": "https://github.com/abagshaw", - "contributions": [ - "code" - ] - }, - { - "login": "kisom", - "name": "Kyle Isom", - "avatar_url": "https://avatars2.githubusercontent.com/u/175578?v=4", - "profile": "https://ai6ua.net/", - "contributions": [ - "code" - ] - }, - { - "login": "ColinPitrat", - "name": "Colin Pitrat", - "avatar_url": "https://avatars3.githubusercontent.com/u/1541863?v=4", - "profile": "https://github.com/ColinPitrat", - "contributions": [ - "code" - ] - }, - { - "login": "zacanger", - "name": "Zac Anger", - "avatar_url": "https://avatars3.githubusercontent.com/u/12520493?v=4", - "profile": "https://zacanger.com", - "contributions": [ - "code" - ] - }, - { - "login": "mgeier", - "name": "Matthias Geier", - "avatar_url": "https://avatars1.githubusercontent.com/u/705404?v=4", - "profile": "https://github.com/mgeier", - "contributions": [ - "code" - ] - }, - { - "login": "cjpearce", - "name": "Chris Pearce", - "avatar_url": "https://avatars1.githubusercontent.com/u/3453268?v=4", - "profile": "https://github.com/cjpearce", - "contributions": [ - "code" - ] - }, - { - "login": "yvan-sraka", - "name": "Yvan Sraka", - "avatar_url": "https://avatars2.githubusercontent.com/u/705213?v=4", - "profile": "https://yvan-sraka.github.io", - "contributions": [ - "code" - ] - }, - { - "login": "dendi239", - "name": "Denys Smirnov", - "avatar_url": "https://avatars3.githubusercontent.com/u/16478650?v=4", - "profile": "https://github.com/dendi239", - "contributions": [ - "code" - ] - }, - { - "login": "eddyp", - "name": "eddyp", - "avatar_url": "https://avatars2.githubusercontent.com/u/123772?v=4", - "profile": "https://github.com/eddyp", - "contributions": [ - "code" - ] - }, - { - "login": "briankung", - "name": "Brian Kung", - "avatar_url": "https://avatars1.githubusercontent.com/u/2836167?v=4", - "profile": "http://about.me/BrianKung", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "miller-time", - "name": "Russell", - "avatar_url": "https://avatars3.githubusercontent.com/u/281039?v=4", - "profile": "https://rcousineau.gitlab.io", - "contributions": [ - "code" - ] - }, - { - "login": "danwilhelm", - "name": "Dan Wilhelm", - "avatar_url": "https://avatars3.githubusercontent.com/u/6137185?v=4", - "profile": "http://danwilhelm.com", - "contributions": [ - "doc" - ] - }, - { - "login": "Jesse-Cameron", - "name": "Jesse", - "avatar_url": "https://avatars3.githubusercontent.com/u/3723654?v=4", - "profile": "https://github.com/Jesse-Cameron", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "MrFroop", - "name": "Fredrik JambrΓ©n", - "avatar_url": "https://avatars3.githubusercontent.com/u/196700?v=4", - "profile": "https://github.com/MrFroop", - "contributions": [ - "code" - ] - }, - { - "login": "petemcfarlane", - "name": "Pete McFarlane", - "avatar_url": "https://avatars3.githubusercontent.com/u/3472717?v=4", - "profile": "https://github.com/petemcfarlane", - "contributions": [ - "content" - ] - }, - { - "login": "nkanderson", - "name": "nkanderson", - "avatar_url": "https://avatars0.githubusercontent.com/u/4128825?v=4", - "profile": "https://github.com/nkanderson", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "ajaxm", - "name": "Ajax M", - "avatar_url": "https://avatars0.githubusercontent.com/u/13360138?v=4", - "profile": "https://github.com/ajaxm", - "contributions": [ - "doc" - ] - }, - { - "login": "Dylnuge", - "name": "Dylan Nugent", - "avatar_url": "https://avatars2.githubusercontent.com/u/118624?v=4", - "profile": "https://dylnuge.com", - "contributions": [ - "content" - ] - }, - { - "login": "vyaslav", - "name": "vyaslav", - "avatar_url": "https://avatars0.githubusercontent.com/u/1385427?v=4", - "profile": "https://github.com/vyaslav", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "gdoenlen", - "name": "George", - "avatar_url": "https://avatars1.githubusercontent.com/u/17297466?v=4", - "profile": "https://join.sfxd.org", - "contributions": [ - "code" - ] - }, - { - "login": "nyxtom", - "name": "Thomas Holloway", - "avatar_url": "https://avatars2.githubusercontent.com/u/222763?v=4", - "profile": "https://github.com/nyxtom", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "workingjubilee", - "name": "Jubilee", - "avatar_url": "https://avatars1.githubusercontent.com/u/46493976?v=4", - "profile": "https://github.com/workingjubilee", - "contributions": [ - "code" - ] - }, - { - "login": "WofWca", - "name": "WofWca", - "avatar_url": "https://avatars1.githubusercontent.com/u/39462442?v=4", - "profile": "https://github.com/WofWca", - "contributions": [ - "code" - ] - }, - { - "login": "jrvidal", - "name": "Roberto Vidal", - "avatar_url": "https://avatars0.githubusercontent.com/u/1636604?v=4", - "profile": "https://github.com/jrvidal", - "contributions": [ - "code", - "doc", - "ideas", - "maintenance" - ] - }, - { - "login": "jensim", - "name": "Jens", - "avatar_url": "https://avatars0.githubusercontent.com/u/3663856?v=4", - "profile": "https://github.com/jensim", - "contributions": [ - "doc" - ] - }, - { - "login": "rahatarmanahmed", - "name": "Rahat Ahmed", - "avatar_url": "https://avatars3.githubusercontent.com/u/3174006?v=4", - "profile": "http://rahatah.me/d", - "contributions": [ - "doc" - ] - }, - { - "login": "AbdouSeck", - "name": "Abdou Seck", - "avatar_url": "https://avatars2.githubusercontent.com/u/6490055?v=4", - "profile": "https://github.com/AbdouSeck", - "contributions": [ - "code", - "content", - "review" - ] - }, - { - "login": "codehearts", - "name": "Katie", - "avatar_url": "https://avatars0.githubusercontent.com/u/2885412?v=4", - "profile": "https://codehearts.com", - "contributions": [ - "code" - ] - }, - { - "login": "Socratides", - "name": "Socrates", - "avatar_url": "https://avatars3.githubusercontent.com/u/27732983?v=4", - "profile": "https://github.com/Socratides", - "contributions": [ - "doc" - ] - }, - { - "login": "gnodarse", - "name": "gnodarse", - "avatar_url": "https://avatars3.githubusercontent.com/u/46761795?v=4", - "profile": "https://github.com/gnodarse", - "contributions": [ - "content" - ] - }, - { - "login": "harrisonmetz", - "name": "Harrison Metzger", - "avatar_url": "https://avatars1.githubusercontent.com/u/7883408?v=4", - "profile": "https://github.com/harrisonmetz", - "contributions": [ - "code" - ] - }, - { - "login": "TorbenJ", - "name": "Torben Jonas", - "avatar_url": "https://avatars2.githubusercontent.com/u/9077102?v=4", - "profile": "https://github.com/TorbenJ", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "pbx", - "name": "Paul Bissex", - "avatar_url": "https://avatars0.githubusercontent.com/u/641?v=4", - "profile": "http://paulbissex.com/", - "contributions": [ - "doc" - ] - }, - { - "login": "sjmann", - "name": "Steven Mann", - "avatar_url": "https://avatars0.githubusercontent.com/u/6589896?v=4", - "profile": "https://github.com/sjmann", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "Tarnadas", - "name": "Mario Reder", - "avatar_url": "https://avatars2.githubusercontent.com/u/5855071?v=4", - "profile": "https://smmdb.net/", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "sl4m", - "name": "skim", - "avatar_url": "https://avatars0.githubusercontent.com/u/47347?v=4", - "profile": "https://keybase.io/skim", - "contributions": [ - "code" - ] - }, - { - "login": "sanjaykdragon", - "name": "Sanjay K", - "avatar_url": "https://avatars1.githubusercontent.com/u/10261698?v=4", - "profile": "https://github.com/sanjaykdragon", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "crodjer", - "name": "Rohan Jain", - "avatar_url": "https://avatars1.githubusercontent.com/u/343499?v=4", - "profile": "http://www.rohanjain.in", - "contributions": [ - "code" - ] - }, - { - "login": "saidaspen", - "name": "Said Aspen", - "avatar_url": "https://avatars1.githubusercontent.com/u/7727687?v=4", - "profile": "https://www.saidaspen.se", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "uce", - "name": "Ufuk Celebi", - "avatar_url": "https://avatars3.githubusercontent.com/u/1756620?v=4", - "profile": "https://github.com/uce", - "contributions": [ - "code" - ] - }, - { - "login": "lebedevsergey", - "name": "lebedevsergey", - "avatar_url": "https://avatars2.githubusercontent.com/u/7325764?v=4", - "profile": "https://github.com/lebedevsergey", - "contributions": [ - "doc" - ] - }, - { - "login": "avrong", - "name": "Aleksei Trifonov", - "avatar_url": "https://avatars2.githubusercontent.com/u/6342851?v=4", - "profile": "https://github.com/avrong", - "contributions": [ - "content" - ] - }, - { - "login": "Darrenmeehan", - "name": "Darren Meehan", - "avatar_url": "https://avatars2.githubusercontent.com/u/411136?v=4", - "profile": "https://drn.ie", - "contributions": [ - "content" - ] - }, - { - "login": "jihchi", - "name": "Jihchi Lee", - "avatar_url": "https://avatars1.githubusercontent.com/u/87983?v=4", - "profile": "https://github.com/jihchi", - "contributions": [ - "content" - ] - }, - { - "login": "bertonha", - "name": "Christofer Bertonha", - "avatar_url": "https://avatars3.githubusercontent.com/u/1225902?v=4", - "profile": "https://github.com/bertonha", - "contributions": [ - "content" - ] - }, - { - "login": "apatniv", - "name": "Vivek Bharath Akupatni", - "avatar_url": "https://avatars2.githubusercontent.com/u/22565917?v=4", - "profile": "https://github.com/apatniv", - "contributions": [ - "code", - "test" - ] - }, - { - "login": "DiD92", - "name": "DΓ­dac SementΓ© FernΓ‘ndez", - "avatar_url": "https://avatars3.githubusercontent.com/u/6002416?v=4", - "profile": "https://github.com/DiD92", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "wrobstory", - "name": "Rob Story", - "avatar_url": "https://avatars3.githubusercontent.com/u/2601457?v=4", - "profile": "https://github.com/wrobstory", - "contributions": [ - "code" - ] - }, - { - "login": "siobhanjacobson", - "name": "Siobhan Jacobson", - "avatar_url": "https://avatars2.githubusercontent.com/u/28983835?v=4", - "profile": "https://github.com/siobhanjacobson", - "contributions": [ - "code" - ] - }, - { - "login": "EvanCarroll", - "name": "Evan Carroll", - "avatar_url": "https://avatars2.githubusercontent.com/u/19922?v=4", - "profile": "https://www.linkedin.com/in/evancarroll/", - "contributions": [ - "content" - ] - }, - { - "login": "jmahmood", - "name": "Jawaad Mahmood", - "avatar_url": "https://avatars3.githubusercontent.com/u/95606?v=4", - "profile": "http://www.jawaadmahmood.com", - "contributions": [ - "content" - ] - }, - { - "login": "GaurangTandon", - "name": "Gaurang Tandon", - "avatar_url": "https://avatars1.githubusercontent.com/u/6308683?v=4", - "profile": "https://github.com/GaurangTandon", - "contributions": [ - "content" - ] - }, - { - "login": "dev-cyprium", - "name": "Stefan Kupresak", - "avatar_url": "https://avatars1.githubusercontent.com/u/6002628?v=4", - "profile": "https://github.com/dev-cyprium", - "contributions": [ - "content" - ] - }, - { - "login": "greg-el", - "name": "Greg Leonard", - "avatar_url": "https://avatars3.githubusercontent.com/u/45019882?v=4", - "profile": "https://github.com/greg-el", - "contributions": [ - "content" - ] - }, - { - "login": "ryanpcmcquen", - "name": "Ryan McQuen", - "avatar_url": "https://avatars3.githubusercontent.com/u/772937?v=4", - "profile": "https://ryanpcmcquen.org", - "contributions": [ - "code" - ] - }, - { - "login": "AnnikaCodes", - "name": "Annika", - "avatar_url": "https://avatars3.githubusercontent.com/u/56906084?v=4", - "profile": "https://github.com/AnnikaCodes", - "contributions": [ - "review" - ] - }, - { - "login": "darnuria", - "name": "Axel Viala", - "avatar_url": "https://avatars1.githubusercontent.com/u/2827553?v=4", - "profile": "https://darnuria.eu", - "contributions": [ - "code" - ] - }, - { - "login": "sazid", - "name": "Mohammed Sazid Al Rashid", - "avatar_url": "https://avatars1.githubusercontent.com/u/2370167?v=4", - "profile": "https://sazid.github.io", - "contributions": [ - "content", - "code" - ] - }, - { - "login": "seeplusplus", - "name": "Caleb Webber", - "avatar_url": "https://avatars1.githubusercontent.com/u/17479099?v=4", - "profile": "https://codingthemsoftly.com", - "contributions": [ - "maintenance" - ] - }, - { - "login": "pcn", - "name": "Peter N", - "avatar_url": "https://avatars2.githubusercontent.com/u/1056756?v=4", - "profile": "https://github.com/pcn", - "contributions": [ - "maintenance" - ] - }, - { - "login": "seancad", - "name": "seancad", - "avatar_url": "https://avatars1.githubusercontent.com/u/47405611?v=4", - "profile": "https://github.com/seancad", - "contributions": [ - "maintenance" - ] - }, - { - "login": "wsh", - "name": "Will Hayworth", - "avatar_url": "https://avatars3.githubusercontent.com/u/181174?v=4", - "profile": "http://willhayworth.com", - "contributions": [ - "content" - ] - }, - { - "login": "chrizel", - "name": "Christian Zeller", - "avatar_url": "https://avatars3.githubusercontent.com/u/20802?v=4", - "profile": "https://github.com/chrizel", - "contributions": [ - "content" - ] - }, - { - "login": "jfchevrette", - "name": "Jean-Francois Chevrette", - "avatar_url": "https://avatars.githubusercontent.com/u/3001?v=4", - "profile": "https://github.com/jfchevrette", - "contributions": [ - "content", - "code" - ] - }, - { - "login": "jbaber", - "name": "John Baber-Lucero", - "avatar_url": "https://avatars.githubusercontent.com/u/1908117?v=4", - "profile": "https://github.com/jbaber", - "contributions": [ - "content" - ] - }, - { - "login": "tal-zvon", - "name": "Tal", - "avatar_url": "https://avatars.githubusercontent.com/u/3195851?v=4", - "profile": "https://github.com/tal-zvon", - "contributions": [ - "content" - ] - }, - { - "login": "apogeeoak", - "name": "apogeeoak", - "avatar_url": "https://avatars.githubusercontent.com/u/59737221?v=4", - "profile": "https://github.com/apogeeoak", - "contributions": [ - "content", - "code" - ] - }, - { - "login": "Crell", - "name": "Larry Garfield", - "avatar_url": "https://avatars.githubusercontent.com/u/254863?v=4", - "profile": "http://www.garfieldtech.com/", - "contributions": [ - "content" - ] - }, - { - "login": "circumspect", - "name": "circumspect", - "avatar_url": "https://avatars.githubusercontent.com/u/40770208?v=4", - "profile": "https://github.com/circumspect", - "contributions": [ - "content" - ] - }, - { - "login": "cjwyett", - "name": "Cyrus Wyett", - "avatar_url": "https://avatars.githubusercontent.com/u/34195737?v=4", - "profile": "https://github.com/cjwyett", - "contributions": [ - "content" - ] - }, - { - "login": "cadolphs", - "name": "cadolphs", - "avatar_url": "https://avatars.githubusercontent.com/u/13894820?v=4", - "profile": "https://github.com/cadolphs", - "contributions": [ - "code" - ] - }, - { - "login": "hpwxf", - "name": "Pascal H.", - "avatar_url": "https://avatars.githubusercontent.com/u/26146722?v=4", - "profile": "https://www.haveneer.com", - "contributions": [ - "content" - ] - }, - { - "login": "chapeupreto", - "name": "Rod Elias", - "avatar_url": "https://avatars.githubusercontent.com/u/834048?v=4", - "profile": "https://twitter.com/chapeupreto", - "contributions": [ - "content" - ] - }, - { - "login": "blerchy", - "name": "Matt Lebl", - "avatar_url": "https://avatars.githubusercontent.com/u/2555355?v=4", - "profile": "https://github.com/blerchy", - "contributions": [ - "code" - ] - }, - { - "login": "flakolefluk", - "name": "Ignacio Le Fluk", - "avatar_url": "https://avatars.githubusercontent.com/u/11986564?v=4", - "profile": "http://flakolefluk.dev", - "contributions": [ - "content" - ] - }, - { - "login": "tlyu", - "name": "Taylor Yu", - "avatar_url": "https://avatars.githubusercontent.com/u/431873?v=4", - "profile": "https://github.com/tlyu", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "Zerotask", - "name": "Patrick Hintermayer", - "avatar_url": "https://avatars.githubusercontent.com/u/20150243?v=4", - "profile": "https://zerotask.github.io", - "contributions": [ - "code" - ] - }, - { - "login": "arthas168", - "name": "Pete Pavlovski", - "avatar_url": "https://avatars.githubusercontent.com/u/32264020?v=4", - "profile": "https://petkopavlovski.com/", - "contributions": [ - "content" - ] - }, - { - "login": "k12ish", - "name": "k12ish", - "avatar_url": "https://avatars.githubusercontent.com/u/45272873?v=4", - "profile": "https://github.com/k12ish", - "contributions": [ - "content" - ] - }, - { - "login": "hongshaoyang", - "name": "Shao Yang Hong", - "avatar_url": "https://avatars.githubusercontent.com/u/19281800?v=4", - "profile": "https://github.com/hongshaoyang", - "contributions": [ - "content" - ] - }, - { - "login": "bmacer", - "name": "Brandon Macer", - "avatar_url": "https://avatars.githubusercontent.com/u/13931806?v=4", - "profile": "https://github.com/bmacer", - "contributions": [ - "content" - ] - }, - { - "login": "stoiandan", - "name": "Stoian Dan", - "avatar_url": "https://avatars.githubusercontent.com/u/10388612?v=4", - "profile": "https://github.com/stoiandan", - "contributions": [ - "content" - ] - }, - { - "login": "PiDelport", - "name": "Pi Delport", - "avatar_url": "https://avatars.githubusercontent.com/u/630271?v=4", - "profile": "https://about.me/pjdelport", - "contributions": [ - "content" - ] - }, - { - "login": "sateeshkumarb", - "name": "Sateesh ", - "avatar_url": "https://avatars.githubusercontent.com/u/429263?v=4", - "profile": "https://github.com/sateeshkumarb", - "contributions": [ - "code", - "content" - ] - }, - { - "login": "kayuapi", - "name": "ZC", - "avatar_url": "https://avatars.githubusercontent.com/u/10304328?v=4", - "profile": "https://github.com/kayuapi", - "contributions": [ - "content" - ] - }, - { - "login": "hyperparabolic", - "name": "hyperparabolic", - "avatar_url": "https://avatars.githubusercontent.com/u/12348474?v=4", - "profile": "https://github.com/hyperparabolic", - "contributions": [ - "code" - ] - }, - { - "login": "kolbma", - "name": "arlecchino", - "avatar_url": "https://avatars.githubusercontent.com/u/5228369?v=4", - "profile": "https://www.net4visions.at", - "contributions": [ - "doc" - ] - }, - { - "login": "jazzplato", - "name": "Richthofen", - "avatar_url": "https://avatars.githubusercontent.com/u/7576730?v=4", - "profile": "https://richthofen.io/", - "contributions": [ - "code" - ] - }, - { - "login": "cseltol", - "name": "Ivan Nerazumov", - "avatar_url": "https://avatars.githubusercontent.com/u/64264529?v=4", - "profile": "https://github.com/cseltol", - "contributions": [ - "doc" - ] - }, - { - "login": "lauralindzey", - "name": "lauralindzey", - "avatar_url": "https://avatars.githubusercontent.com/u/65185744?v=4", - "profile": "https://github.com/lauralindzey", - "contributions": [ - "doc" - ] - }, - { - "login": "sinharaksh1t", - "name": "Rakshit Sinha", - "avatar_url": "https://avatars.githubusercontent.com/u/28585848?v=4", - "profile": "https://github.com/sinharaksh1t", - "contributions": [ - "content" - ] - }, - { - "login": "dbednar230", - "name": "Damian", - "avatar_url": "https://avatars.githubusercontent.com/u/54457902?v=4", - "profile": "https://github.com/dbednar230", - "contributions": [ - "content" - ] - }, - { - "login": "benarmstead", - "name": "Ben Armstead", - "avatar_url": "https://avatars.githubusercontent.com/u/70973680?v=4", - "profile": "https://benarmstead.co.uk", - "contributions": [ - "code" - ] - }, - { - "login": "anuk909", - "name": "anuk909", - "avatar_url": "https://avatars.githubusercontent.com/u/34924662?v=4", - "profile": "https://github.com/anuk909", - "contributions": [ - "content", - "code" - ] - }, - { - "login": "granddaifuku", - "name": "granddaifuku", - "avatar_url": "https://avatars.githubusercontent.com/u/49578068?v=4", - "profile": "https://granddaifuku.com/", - "contributions": [ - "content" - ] - }, - { - "login": "Weilet", - "name": "Weilet", - "avatar_url": "https://avatars.githubusercontent.com/u/32561597?v=4", - "profile": "https://weilet.me", - "contributions": [ - "content" - ] - }, - { - "login": "Millione", - "name": "LIU JIE", - "avatar_url": "https://avatars.githubusercontent.com/u/38575932?v=4", - "profile": "https://github.com/Millione", - "contributions": [ - "content" - ] - }, - { - "login": "abusch", - "name": "Antoine BΓΌsch", - "avatar_url": "https://avatars.githubusercontent.com/u/506344?v=4", - "profile": "https://github.com/abusch", - "contributions": [ - "code" - ] - }, - { - "login": "frogtd", - "name": "frogtd", - "avatar_url": "https://avatars.githubusercontent.com/u/31412003?v=4", - "profile": "https://frogtd.com/", - "contributions": [ - "content" - ] - }, - { - "login": "EmisonLu", - "name": "Zhenghao Lu", - "avatar_url": "https://avatars.githubusercontent.com/u/54395432?v=4", - "profile": "https://github.com/EmisonLu", - "contributions": [ - "content" - ] - }, - { - "login": "fredr", - "name": "Fredrik Enestad", - "avatar_url": "https://avatars.githubusercontent.com/u/762956?v=4", - "profile": "https://soundtrackyourbrand.com", - "contributions": [ - "content" - ] - }, - { - "login": "xuesongbj", - "name": "xuesong", - "avatar_url": "https://avatars.githubusercontent.com/u/18476085?v=4", - "profile": "http://xuesong.pydevops.com", - "contributions": [ - "content" - ] - }, - { - "login": "MpdWalsh", - "name": "Michael Walsh", - "avatar_url": "https://avatars.githubusercontent.com/u/48160144?v=4", - "profile": "https://github.com/MpdWalsh", - "contributions": [ - "code" - ] - }, - { - "login": "alirezaghey", - "name": "alirezaghey", - "avatar_url": "https://avatars.githubusercontent.com/u/26653424?v=4", - "profile": "https://github.com/alirezaghey", - "contributions": [ - "content" - ] - }, - { - "login": "frvannes16", - "name": "Franklin van Nes", - "avatar_url": "https://avatars.githubusercontent.com/u/3188475?v=4", - "profile": "https://github.com/frvannes16", - "contributions": [ - "code" - ] - }, - { - "login": "nekonako", - "name": "nekonako", - "avatar_url": "https://avatars.githubusercontent.com/u/46141275?v=4", - "profile": "https://nekonako.github.io", - "contributions": [ - "code" - ] - }, - { - "login": "tan-zx", - "name": "ZX", - "avatar_url": "https://avatars.githubusercontent.com/u/67887489?v=4", - "profile": "https://github.com/tan-zx", - "contributions": [ - "content" - ] - }, - { - "login": "sundevilyang", - "name": "Yang Wen", - "avatar_url": "https://avatars.githubusercontent.com/u/1499214?v=4", - "profile": "https://github.com/sundevilyang", - "contributions": [ - "content" - ] - }, - { - "login": "highb", - "name": "Brandon High", - "avatar_url": "https://avatars.githubusercontent.com/u/759848?v=4", - "profile": "https://brandon-high.com", - "contributions": [ - "doc" - ] - }, - { - "login": "x-hgg-x", - "name": "x-hgg-x", - "avatar_url": "https://avatars.githubusercontent.com/u/39058530?v=4", - "profile": "https://github.com/x-hgg-x", - "contributions": [ - "code" - ] - }, - { - "login": "KisaragiEffective", - "name": "Kisaragi", - "avatar_url": "https://avatars.githubusercontent.com/u/48310258?v=4", - "profile": "http://kisaragieffective.github.io", - "contributions": [ - "doc" - ] - }, - { - "login": "Kallu-A", - "name": "Lucas Aries", - "avatar_url": "https://avatars.githubusercontent.com/u/73198738?v=4", - "profile": "https://github.com/Kallu-A", - "contributions": [ - "content" - ] - }, - { - "login": "ragreenburg", - "name": "ragreenburg", - "avatar_url": "https://avatars.githubusercontent.com/u/24358100?v=4", - "profile": "https://github.com/ragreenburg", - "contributions": [ - "content" - ] - }, - { - "login": "stevenfukase", - "name": "stevenfukase", - "avatar_url": "https://avatars.githubusercontent.com/u/66785624?v=4", - "profile": "https://github.com/stevenfukase", - "contributions": [ - "content" - ] - }, - { - "login": "J-S-Kim", - "name": "J-S-Kim", - "avatar_url": "https://avatars.githubusercontent.com/u/17569303?v=4", - "profile": "https://github.com/J-S-Kim", - "contributions": [ - "content" - ] - }, - { - "login": "Fointard", - "name": "Fointard", - "avatar_url": "https://avatars.githubusercontent.com/u/9333398?v=4", - "profile": "https://github.com/Fointard", - "contributions": [ - "content" - ] - }, - { - "login": "rytheo", - "name": "Ryan Lowe", - "avatar_url": "https://avatars.githubusercontent.com/u/22184325?v=4", - "profile": "https://github.com/rytheo", - "contributions": [ - "code" - ] - }, - { - "login": "cuishuang", - "name": "cui fliter", - "avatar_url": "https://avatars.githubusercontent.com/u/15921519?v=4", - "profile": "http://www.dashen.tech", - "contributions": [ - "content" - ] - }, - { - "login": "luskwater", - "name": "Ron Lusk", - "avatar_url": "https://avatars.githubusercontent.com/u/42529?v=4", - "profile": "https://github.com/luskwater", - "contributions": [ - "content" - ] - }, - { - "login": "liby", - "name": "Bryan Lee", - "avatar_url": "https://avatars.githubusercontent.com/u/38807139?v=4", - "profile": "http://liby.github.io/liby/", - "contributions": [ - "content" - ] - }, - { - "login": "nandajavarma", - "name": "Nandaja Varma", - "avatar_url": "https://avatars.githubusercontent.com/u/2624550?v=4", - "profile": "http://nandaja.space", - "contributions": [ - "doc" - ] - }, - { - "login": "merelymyself", - "name": "pwygab", - "avatar_url": "https://avatars.githubusercontent.com/u/88221256?v=4", - "profile": "https://github.com/merelymyself", - "contributions": [ - "code" - ] - }, - { - "login": "lucasgrvarela", - "name": "Lucas Grigolon Varela", - "avatar_url": "https://avatars.githubusercontent.com/u/37870368?v=4", - "profile": "http://linkedin.com/in/lucasgrvarela", - "contributions": [ - "content" - ] - }, - { - "login": "bufo24", - "name": "Bufo", - "avatar_url": "https://avatars.githubusercontent.com/u/32884105?v=4", - "profile": "https://github.com/bufo24", - "contributions": [ - "content" - ] - }, - { - "login": "jackos", - "name": "Jack Clayton", - "avatar_url": "https://avatars.githubusercontent.com/u/77730378?v=4", - "profile": "http://rustnote.com", - "contributions": [ - "code" - ] - }, - { - "login": "klkl0808", - "name": "Konstantin", - "avatar_url": "https://avatars.githubusercontent.com/u/24694249?v=4", - "profile": "https://github.com/klkl0808", - "contributions": [ - "content" - ] - }, - { - "login": "0pling", - "name": "0pling", - "avatar_url": "https://avatars.githubusercontent.com/u/104090344?v=4", - "profile": "https://github.com/0pling", - "contributions": [ - "content" - ] - }, - { - "login": "KatanaFluorescent", - "name": "KatanaFluorescent", - "avatar_url": "https://avatars.githubusercontent.com/u/60199077?v=4", - "profile": "https://github.com/KatanaFluorescent", - "contributions": [ - "code" - ] - }, - { - "login": "Drew-Morris", - "name": "Drew Morris", - "avatar_url": "https://avatars.githubusercontent.com/u/95818166?v=4", - "profile": "https://github.com/Drew-Morris", - "contributions": [ - "code" - ] - }, - { - "login": "camperdue42", - "name": "camperdue42", - "avatar_url": "https://avatars.githubusercontent.com/u/43047763?v=4", - "profile": "https://github.com/camperdue42", - "contributions": [ - "content" - ] - }, - { - "login": "YsuOS", - "name": "YsuOS", - "avatar_url": "https://avatars.githubusercontent.com/u/30138661?v=4", - "profile": "https://github.com/YsuOS", - "contributions": [ - "content" - ] - }, - { - "login": "icecream17", - "name": "Steven Nguyen", - "avatar_url": "https://avatars.githubusercontent.com/u/58114641?v=4", - "profile": "https://lichess.org/@/StevenEmily", - "contributions": [ - "content" - ] - }, - { - "login": "nacairns1", - "name": "nacairns1", - "avatar_url": "https://avatars.githubusercontent.com/u/94420090?v=4", - "profile": "https://noahcairns.dev", - "contributions": [ - "content" - ] - }, - { - "login": "pgjbz", - "name": "Paulo Gabriel Justino Bezerra", - "avatar_url": "https://avatars.githubusercontent.com/u/22059237?v=4", - "profile": "https://github.com/pgjbz", - "contributions": [ - "content" - ] - }, - { - "login": "jaystile", - "name": "Jason", - "avatar_url": "https://avatars.githubusercontent.com/u/46078028?v=4", - "profile": "https://github.com/jaystile", - "contributions": [ - "content" - ] - }, - { - "login": "exdx", - "name": "exdx", - "avatar_url": "https://avatars.githubusercontent.com/u/31546601?v=4", - "profile": "https://exdx.github.io", - "contributions": [ - "content" - ] - }, - { - "login": "Jzow", - "name": "James Zow", - "avatar_url": "https://avatars.githubusercontent.com/u/68860495?v=4", - "profile": "https://github.com/Jzow", - "contributions": [ - "content" - ] - }, - { - "login": "jayber", - "name": "James Bromley", - "avatar_url": "https://avatars.githubusercontent.com/u/2474334?v=4", - "profile": "https://jamesabromley.wordpress.com/", - "contributions": [ - "content" - ] - }, - { - "login": "swhiteCQC", - "name": "swhiteCQC", - "avatar_url": "https://avatars.githubusercontent.com/u/77438466?v=4", - "profile": "https://github.com/swhiteCQC", - "contributions": [ - "content" - ] - }, - { - "login": "neilpate", - "name": "Neil Pate", - "avatar_url": "https://avatars.githubusercontent.com/u/7802334?v=4", - "profile": "https://github.com/neilpate", - "contributions": [ - "content" - ] - }, - { - "login": "wojexe", - "name": "wojexe", - "avatar_url": "https://avatars.githubusercontent.com/u/21208490?v=4", - "profile": "https://wojexe.com", - "contributions": [ - "content" - ] - }, - { - "login": "Tostapunk", - "name": "Mattia Schiavon", - "avatar_url": "https://avatars.githubusercontent.com/u/25140297?v=4", - "profile": "https://github.com/Tostapunk", - "contributions": [ - "content" - ] - }, - { - "login": "PrettyWood", - "name": "Eric Jolibois", - "avatar_url": "https://avatars.githubusercontent.com/u/18406791?v=4", - "profile": "http://toucantoco.com", - "contributions": [ - "content" - ] - }, - { - "login": "EdwinChang24", - "name": "Edwin Chang", - "avatar_url": "https://avatars.githubusercontent.com/u/88263098?v=4", - "profile": "http://edwinchang.vercel.app", - "contributions": [ - "content" - ] - }, - { - "login": "saikatdas0790", - "name": "Saikat Das", - "avatar_url": "https://avatars.githubusercontent.com/u/7412443?v=4", - "profile": "https://saikat.dev/", - "contributions": [ - "content" - ] - }, - { - "login": "thatlittleboy", - "name": "Jeremy Goh", - "avatar_url": "https://avatars.githubusercontent.com/u/30731072?v=4", - "profile": "https://github.com/thatlittleboy", - "contributions": [ - "content" - ] - }, - { - "login": "Lioness100", - "name": "Lioness100", - "avatar_url": "https://avatars.githubusercontent.com/u/65814829?v=4", - "profile": "https://github.com/Lioness100", - "contributions": [ - "content" - ] - }, - { - "login": "tvkn", - "name": "Tristan Nicholls", - "avatar_url": "https://avatars.githubusercontent.com/u/79277926?v=4", - "profile": "https://github.com/tvkn", - "contributions": [ - "content" - ] - }, - { - "login": "clairew", - "name": "Claire", - "avatar_url": "https://avatars.githubusercontent.com/u/9344258?v=4", - "profile": "http://clairewang.net", - "contributions": [ - "content" - ] - }, - { - "login": "Mouwrice", - "name": "Maurice Van Wassenhove", - "avatar_url": "https://avatars.githubusercontent.com/u/56763273?v=4", - "profile": "https://github.com/Mouwrice", - "contributions": [ - "content" - ] - }, - { - "login": "johnmendel", - "name": "John Mendelewski", - "avatar_url": "https://avatars.githubusercontent.com/u/77524?v=4", - "profile": "http://jmthree.com", - "contributions": [ - "code" - ] - }, - { - "login": "brianfakhoury", - "name": "Brian Fakhoury", - "avatar_url": "https://avatars.githubusercontent.com/u/20828724?v=4", - "profile": "http://fakhoury.xyz", - "contributions": [ - "content" - ] - }, - { - "login": "markusboehme", - "name": "Markus Boehme", - "avatar_url": "https://avatars.githubusercontent.com/u/5074759?v=4", - "profile": "https://github.com/markusboehme", - "contributions": [ - "code" - ] - }, - { - "login": "nico-vromans", - "name": "Nico Vromans", - "avatar_url": "https://avatars.githubusercontent.com/u/48183857?v=4", - "profile": "https://github.com/nico-vromans", - "contributions": [ - "content" - ] - }, - { - "login": "vostok92", - "name": "vostok92", - "avatar_url": "https://avatars.githubusercontent.com/u/540339?v=4", - "profile": "https://github.com/vostok92", - "contributions": [ - "content" - ] - }, - { - "login": "magnusrodseth", - "name": "Magnus RΓΈdseth", - "avatar_url": "https://avatars.githubusercontent.com/u/59113973?v=4", - "profile": "http://magnusrodseth.vercel.app", - "contributions": [ - "content" - ] - }, - { - "login": "rubiesonthesky", - "name": "rubiesonthesky", - "avatar_url": "https://avatars.githubusercontent.com/u/2591240?v=4", - "profile": "https://github.com/rubiesonthesky", - "contributions": [ - "content" - ] - }, - { - "login": "GabrielBianconi", - "name": "Gabriel Bianconi", - "avatar_url": "https://avatars.githubusercontent.com/u/1275491?v=4", - "profile": "http://www.gabrielbianconi.com/", - "contributions": [ - "content" - ] - }, - { - "login": "Kodylow", - "name": "Kody Low", - "avatar_url": "https://avatars.githubusercontent.com/u/74332828?v=4", - "profile": "https://github.com/Kodylow", - "contributions": [ - "content" - ] - }, - { - "login": "rzrymiak", - "name": "rzrymiak", - "avatar_url": "https://avatars.githubusercontent.com/u/106121613?v=4", - "profile": "https://github.com/rzrymiak", - "contributions": [ - "content" - ] - }, - { - "login": "miguelraz", - "name": "Miguel Raz GuzmΓ‘n Macedo", - "avatar_url": "https://avatars.githubusercontent.com/u/13056181?v=4", - "profile": "https://github.com/miguelraz", - "contributions": [ - "content" - ] - }, - { - "login": "memark", - "name": "Magnus Markling", - "avatar_url": "https://avatars.githubusercontent.com/u/318504?v=4", - "profile": "https://github.com/memark", - "contributions": [ - "content" - ] - }, - { - "login": "gasparitiago", - "name": "Tiago De Gaspari", - "avatar_url": "https://avatars.githubusercontent.com/u/3237254?v=4", - "profile": "https://github.com/gasparitiago", - "contributions": [ - "content" - ] - }, - { - "login": "skaunov", - "name": "skaunov", - "avatar_url": "https://avatars.githubusercontent.com/u/65976143?v=4", - "profile": "https://github.com/skaunov", - "contributions": [ - "content" - ] - }, - { - "login": "cj81499", - "name": "Cal Jacobson", - "avatar_url": "https://avatars.githubusercontent.com/u/9152032?v=4", - "profile": "http://caljacobson.dev", - "contributions": [ - "content" - ] - }, - { - "login": "duchonic", - "name": "Duchoud Nicolas", - "avatar_url": "https://avatars.githubusercontent.com/u/34117620?v=4", - "profile": "https://github.com/duchonic", - "contributions": [ - "content" - ] - }, - { - "login": "gfaugere", - "name": "GaΓ«tan FaugΓ¨re", - "avatar_url": "https://avatars.githubusercontent.com/u/11901979?v=4", - "profile": "https://github.com/gfaugere", - "contributions": [ - "tool" - ] - }, - { - "login": "bhbuehler", - "name": "bhbuehler", - "avatar_url": "https://avatars.githubusercontent.com/u/25541343?v=4", - "profile": "https://github.com/bhbuehler", - "contributions": [ - "content" - ] - }, - { - "login": "nyurik", - "name": "Yuri Astrakhan", - "avatar_url": "https://avatars.githubusercontent.com/u/1641515?v=4", - "profile": "https://github.com/nyurik", - "contributions": [ - "code" - ] - }, - { - "login": "azzamsa", - "name": "azzamsa", - "avatar_url": "https://avatars.githubusercontent.com/u/17734314?v=4", - "profile": "http://azzamsa.com", - "contributions": [ - "code" - ] - }, - { - "login": "mvanschellebeeck", - "name": "mvanschellebeeck", - "avatar_url": "https://avatars.githubusercontent.com/u/17671052?v=4", - "profile": "https://github.com/mvanschellebeeck", - "contributions": [ - "content" - ] - }, - { - "login": "aaarkid", - "name": "Arkid", - "avatar_url": "https://avatars.githubusercontent.com/u/39987510?v=4", - "profile": "https://github.com/aaarkid", - "contributions": [ - "content" - ] - }, - { - "login": "tfpk", - "name": "Tom Kunc", - "avatar_url": "https://avatars.githubusercontent.com/u/10906982?v=4", - "profile": "http://tfpk.dev", - "contributions": [ - "content" - ] - }, - { - "login": "mfurak", - "name": "Marek FurΓ‘k", - "avatar_url": "https://avatars.githubusercontent.com/u/38523093?v=4", - "profile": "https://github.com/mfurak", - "contributions": [ - "content" - ] - }, - { - "login": "winterqt", - "name": "Winter", - "avatar_url": "https://avatars.githubusercontent.com/u/78392041?v=4", - "profile": "https://winter.cafe", - "contributions": [ - "code" - ] - }, - { - "login": "MoritzBoehme", - "name": "Moritz BΓΆhme", - "avatar_url": "https://avatars.githubusercontent.com/u/42215704?v=4", - "profile": "https://moritzboeh.me", - "contributions": [ - "code" - ] - }, - { - "login": "craymel", - "name": "craymel", - "avatar_url": "https://avatars.githubusercontent.com/u/71062756?v=4", - "profile": "https://github.com/craymel", - "contributions": [ - "content" - ] - }, - { - "login": "tkburis", - "name": "TK Buristrakul", - "avatar_url": "https://avatars.githubusercontent.com/u/20501289?v=4", - "profile": "https://github.com/tkburis", - "contributions": [ - "content" - ] - }, - { - "login": "HerschelW", - "name": "Kent Worthington", - "avatar_url": "https://avatars.githubusercontent.com/u/17935816?v=4", - "profile": "https://github.com/HerschelW", - "contributions": [ - "content" - ] - }, - { - "login": "seporterfield", - "name": "seporterfield", - "avatar_url": "https://avatars.githubusercontent.com/u/107010978?v=4", - "profile": "https://github.com/seporterfield", - "contributions": [ - "content" - ] - }, - { - "login": "dbarrosop", - "name": "David Barroso", - "avatar_url": "https://avatars.githubusercontent.com/u/6246622?v=4", - "profile": "https://www.linkedin.com/in/dbarrosop", - "contributions": [ - "infra" - ] - }, - { - "login": "tklauser", - "name": "Tobias Klauser", - "avatar_url": "https://avatars.githubusercontent.com/u/539708?v=4", - "profile": "https://distanz.ch", - "contributions": [ - "code" - ] - }, - { - "login": "0xMySt1c", - "name": "0xMySt1c", - "avatar_url": "https://avatars.githubusercontent.com/u/101825630?v=4", - "profile": "https://github.com/0xMySt1c", - "contributions": [ - "tool" - ] - }, - { - "login": "AxolotlTears", - "name": "Ten", - "avatar_url": "https://avatars.githubusercontent.com/u/87157047?v=4", - "profile": "https://github.com/AxolotlTears", - "contributions": [ - "code" - ] - }, - { - "login": "h4x5p4c3", - "name": "jones martin", - "avatar_url": "https://avatars.githubusercontent.com/u/66133688?v=4", - "profile": "http://h4x5p4c3.xyz", - "contributions": [ - "content" - ] - }, - { - "login": "cloppingemu", - "name": "cloppingemu", - "avatar_url": "https://avatars.githubusercontent.com/u/12227963?v=4", - "profile": "https://github.com/cloppingemu", - "contributions": [ - "code" - ] - }, - { - "login": "kevwan", - "name": "Kevin Wan", - "avatar_url": "https://avatars.githubusercontent.com/u/1918356?v=4", - "profile": "http://github.com/zeromicro/go-zero", - "contributions": [ - "content" - ] - }, - { - "login": "wjwrh", - "name": "Ruby", - "avatar_url": "https://avatars.githubusercontent.com/u/43495006?v=4", - "profile": "http://kurowasaruby.cn", - "contributions": [ - "code" - ] - }, - { - "login": "alexandergill", - "name": "Alexander Gill", - "avatar_url": "https://avatars.githubusercontent.com/u/7033716?v=4", - "profile": "https://github.com/alexandergill", - "contributions": [ - "code" - ] - }, - { - "login": "kawaiiPlat", - "name": "Jarrod Sanders", - "avatar_url": "https://avatars.githubusercontent.com/u/50600614?v=4", - "profile": "https://www.linkedin.com/in/jarrod-sanders/", - "contributions": [ - "content" - ] - }, - { - "login": "platformer", - "name": "Andrew Sen", - "avatar_url": "https://avatars.githubusercontent.com/u/40146328?v=4", - "profile": "https://github.com/platformer", - "contributions": [ - "content" - ] - }, - { - "login": "grzegorz-zur", - "name": "Grzegorz Ε»ur", - "avatar_url": "https://avatars.githubusercontent.com/u/5297583?v=4", - "profile": "https://grzegorz-zur.com/", - "contributions": [ - "content" - ] - }, - { - "login": "black-puppydog", - "name": "Daan Wynen", - "avatar_url": "https://avatars.githubusercontent.com/u/189241?v=4", - "profile": "https://github.com/black-puppydog", - "contributions": [ - "content" - ] - }, - { - "login": "Anush008", - "name": "Anush", - "avatar_url": "https://avatars.githubusercontent.com/u/46051506?v=4", - "profile": "https://github.com/Anush008", - "contributions": [ - "doc" - ] - }, - { - "login": "shgew", - "name": "Gleb Shevchenko", - "avatar_url": "https://avatars.githubusercontent.com/u/5584672?v=4", - "profile": "https://github.com/shgew", - "contributions": [ - "content" - ] - }, - { - "login": "mdmundo", - "name": "Edmundo Paulino", - "avatar_url": "https://avatars.githubusercontent.com/u/60408300?v=4", - "profile": "https://github.com/mdmundo", - "contributions": [ - "infra" - ] - }, - { - "login": "eroullit", - "name": "Emmanuel Roullit", - "avatar_url": "https://avatars.githubusercontent.com/u/301795?v=4", - "profile": "https://github.com/eroullit", - "contributions": [ - "infra" - ] - }, - { - "login": "nidhalmessaoudi", - "name": "Nidhal Messaoudi", - "avatar_url": "https://avatars.githubusercontent.com/u/63377412?v=4", - "profile": "https://nidhalmessaoudi.herokuapp.com", - "contributions": [ - "code" - ] - }, - { - "login": "MahdiBM", - "name": "Mahdi Bahrami", - "avatar_url": "https://avatars.githubusercontent.com/u/54685446?v=4", - "profile": "https://github.com/MahdiBM", - "contributions": [ - "tool" - ] - }, - { - "login": "Nagidal", - "name": "Nagidal", - "avatar_url": "https://avatars.githubusercontent.com/u/7075397?v=4", - "profile": "https://github.com/Nagidal", - "contributions": [ - "content" - ] - }, - { - "login": "adamhb123", - "name": "Adam Brewer", - "avatar_url": "https://avatars.githubusercontent.com/u/25161597?v=4", - "profile": "https://adabrew.com", - "contributions": [ - "content" - ] - }, - { - "login": "eugkhp", - "name": "Eugene", - "avatar_url": "https://avatars.githubusercontent.com/u/25910599?v=4", - "profile": "https://github.com/eugkhp", - "contributions": [ - "tool" - ] - }, - { - "login": "navicore", - "name": "Ed Sweeney", - "avatar_url": "https://avatars.githubusercontent.com/u/110999?v=4", - "profile": "https://social.linux.pizza/@navicore", - "contributions": [ - "content" - ] - }, - { - "login": "javihernant", - "name": "javihernant", - "avatar_url": "https://avatars.githubusercontent.com/u/73640929?v=4", - "profile": "https://github.com/javihernant", - "contributions": [ - "content" - ] - }, - { - "login": "VegardMatthey", - "name": "Vegard", - "avatar_url": "https://avatars.githubusercontent.com/u/59250656?v=4", - "profile": "https://github.com/VegardMatthey", - "contributions": [ - "content" - ] - }, - { - "login": "ryanwhitehouse", - "name": "Ryan Whitehouse", - "avatar_url": "https://avatars.githubusercontent.com/u/13400784?v=4", - "profile": "https://github.com/ryanwhitehouse", - "contributions": [ - "content" - ] - }, - { - "login": "guoard", - "name": "Ali Afsharzadeh", - "avatar_url": "https://avatars.githubusercontent.com/u/65511355?v=4", - "profile": "https://github.com/guoard", - "contributions": [ - "content" - ] - }, - { - "login": "keogami", - "name": "Keogami", - "avatar_url": "https://avatars.githubusercontent.com/u/41939011?v=4", - "profile": "http://keogami.ml", - "contributions": [ - "doc" - ] - }, - { - "login": "ahresse", - "name": "Alexandre Esse", - "avatar_url": "https://avatars.githubusercontent.com/u/28402488?v=4", - "profile": "https://github.com/ahresse", - "contributions": [ - "content" - ] - }, - { - "login": "sagarvora", - "name": "Sagar Vora", - "avatar_url": "https://avatars.githubusercontent.com/u/16315650?v=4", - "profile": "https://resilient.tech", - "contributions": [ - "content" - ] - }, - { - "login": "poneciak57", - "name": "Kacper Poneta", - "avatar_url": "https://avatars.githubusercontent.com/u/94321164?v=4", - "profile": "https://github.com/poneciak57", - "contributions": [ - "content" - ] - }, - { - "login": "ktheory", - "name": "Aaron Suggs", - "avatar_url": "https://avatars.githubusercontent.com/u/975?v=4", - "profile": "https://ktheory.com/", - "contributions": [ - "content" - ] - }, - { - "login": "alexwh", - "name": "Alex", - "avatar_url": "https://avatars.githubusercontent.com/u/1723612?v=4", - "profile": "https://github.com/alexwh", - "contributions": [ - "content" - ] - }, - { - "login": "stornquist", - "name": "Sebastian TΓΆrnquist", - "avatar_url": "https://avatars.githubusercontent.com/u/42915664?v=4", - "profile": "https://github.com/stornquist", - "contributions": [ - "content" - ] - }, - { - "login": "smlavine", - "name": "Sebastian LaVine", - "avatar_url": "https://avatars.githubusercontent.com/u/33563640?v=4", - "profile": "http://smlavine.com", - "contributions": [ - "code" - ] - }, - { - "login": "akgerber", - "name": "Alan Gerber", - "avatar_url": "https://avatars.githubusercontent.com/u/201313?v=4", - "profile": "http://www.alangerber.us", - "contributions": [ - "content" - ] - }, - { - "login": "esotuvaka", - "name": "Eric", - "avatar_url": "https://avatars.githubusercontent.com/u/104941850?v=4", - "profile": "http://esotuvaka.github.io", - "contributions": [ - "content" - ] - }, - { - "login": "az0977776", - "name": "Aaron Wang", - "avatar_url": "https://avatars.githubusercontent.com/u/9172038?v=4", - "profile": "https://github.com/az0977776", - "contributions": [ - "content" - ] - }, - { - "login": "nmay231", - "name": "Noah", - "avatar_url": "https://avatars.githubusercontent.com/u/35386821?v=4", - "profile": "https://github.com/nmay231", - "contributions": [ - "content" - ] - }, - { - "login": "rb5014", - "name": "rb5014", - "avatar_url": "https://avatars.githubusercontent.com/u/105397317?v=4", - "profile": "https://github.com/rb5014", - "contributions": [ - "content" - ] - }, - { - "login": "deedy5", - "name": "deedy5", - "avatar_url": "https://avatars.githubusercontent.com/u/65482418?v=4", - "profile": "https://github.com/deedy5", - "contributions": [ - "content" - ] - }, - { - "login": "lionel-rowe", - "name": "lionel-rowe", - "avatar_url": "https://avatars.githubusercontent.com/u/26078826?v=4", - "profile": "https://github.com/lionel-rowe", - "contributions": [ - "content" - ] - }, - { - "login": "Ben2917", - "name": "Ben", - "avatar_url": "https://avatars.githubusercontent.com/u/10279994?v=4", - "profile": "https://github.com/Ben2917", - "contributions": [ - "content" - ] - }, - { - "login": "b1ue64", - "name": "b1ue64", - "avatar_url": "https://avatars.githubusercontent.com/u/77976308?v=4", - "profile": "https://github.com/b1ue64", - "contributions": [ - "content" - ] - }, - { - "login": "lazywalker", - "name": "lazywalker", - "avatar_url": "https://avatars.githubusercontent.com/u/53956?v=4", - "profile": "https://github.com/lazywalker", - "contributions": [ - "content" - ] - }, - { - "login": "proofconstruction", - "name": "proofconstruction", - "avatar_url": "https://avatars.githubusercontent.com/u/74747193?v=4", - "profile": "https://github.com/proofconstruction", - "contributions": [ - "infra" - ] - }, - { - "login": "IVIURRAY", - "name": "IVIURRAY", - "avatar_url": "https://avatars.githubusercontent.com/u/16007179?v=4", - "profile": "https://www.youtube.com/channel/UCQCjA6qUutAtWqkCA4Z36CQ", - "contributions": [ - "content" - ] - }, - { - "login": "b-apperlo", - "name": "Bert Apperlo", - "avatar_url": "https://avatars.githubusercontent.com/u/91734527?v=4", - "profile": "https://github.com/b-apperlo", - "contributions": [ - "content" - ] - }, - { - "login": "FWDekker", - "name": "Florine W. Dekker", - "avatar_url": "https://avatars.githubusercontent.com/u/13442533?v=4", - "profile": "https://fwdekker.com/", - "contributions": [ - "content" - ] - }, - { - "login": "luhem7", - "name": "Mehul Gangavelli", - "avatar_url": "https://avatars.githubusercontent.com/u/4008215?v=4", - "profile": "https://github.com/luhem7", - "contributions": [ - "content" - ] - }, - { - "login": "Frosthage", - "name": "Mikael Frosthage", - "avatar_url": "https://avatars.githubusercontent.com/u/14823314?v=4", - "profile": "https://github.com/Frosthage", - "contributions": [ - "content" - ] - }, - { - "login": "robertefry", - "name": "Robert Fry", - "avatar_url": "https://avatars.githubusercontent.com/u/43712054?v=4", - "profile": "https://robertfry.xyz", - "contributions": [ - "content" - ] - }, - { - "login": "tajo48", - "name": "tajo48", - "avatar_url": "https://avatars.githubusercontent.com/u/55502906?v=4", - "profile": "https://github.com/tajo48", - "contributions": [ - "content" - ] - }, - { - "login": "novanish", - "name": "Anish", - "avatar_url": "https://avatars.githubusercontent.com/u/98446102?v=4", - "profile": "https://anishchhetri.com.np", - "contributions": [ - "content" - ] - }, - { - "login": "vnprc", - "name": "vnprc", - "avatar_url": "https://avatars.githubusercontent.com/u/9425366?v=4", - "profile": "https://github.com/vnprc", - "contributions": [ - "content" - ] - }, - { - "login": "jrcarl624", - "name": "Joshua Carlson", - "avatar_url": "https://avatars.githubusercontent.com/u/61999256?v=4", - "profile": "http://androecia.net", - "contributions": [ - "content" - ] - }, - { - "login": "johnDeSilencio", - "name": "Nicholas R. Smith", - "avatar_url": "https://avatars.githubusercontent.com/u/20136554?v=4", - "profile": "https://johndesilencio.me", - "contributions": [ - "code" - ] - }, - { - "login": "alexfertel", - "name": "Alexander GonzΓ‘lez", - "avatar_url": "https://avatars.githubusercontent.com/u/22298999?v=4", - "profile": "https://alexfertel.me", - "contributions": [ - "content" - ] - }, - { - "login": "softarn", - "name": "Marcus HΓΆjvall", - "avatar_url": "https://avatars.githubusercontent.com/u/517619?v=4", - "profile": "https://github.com/softarn", - "contributions": [ - "content" - ] - }, - { - "login": "barlevalon", - "name": "Alon Hearter", - "avatar_url": "https://avatars.githubusercontent.com/u/3397911?v=4", - "profile": "https://github.com/barlevalon", - "contributions": [ - "content" - ] - }, - { - "login": "shirts", - "name": "shirts", - "avatar_url": "https://avatars.githubusercontent.com/u/4952151?v=4", - "profile": "https://github.com/shirts", - "contributions": [ - "content" - ] - }, - { - "login": "eLVas", - "name": "Ivan Vasiunyk", - "avatar_url": "https://avatars.githubusercontent.com/u/6797156?v=4", - "profile": "https://github.com/eLVas", - "contributions": [ - "content" - ] - }, - { - "login": "mo8it", - "name": "Mo", - "avatar_url": "https://avatars.githubusercontent.com/u/76752051?v=4", - "profile": "https://mo8it.com", - "contributions": [ - "code" - ] - }, - { - "login": "x10an14", - "name": "x10an14", - "avatar_url": "https://avatars.githubusercontent.com/u/710608?v=4", - "profile": "https://github.com/x10an14", - "contributions": [ - "infra" - ] - }, - { - "login": "gabay", - "name": "Roi Gabay", - "avatar_url": "https://avatars.githubusercontent.com/u/5773610?v=4", - "profile": "https://github.com/gabay", - "contributions": [ - "content" - ] - }, - { - "login": "mkovaxx", - "name": "MΓ‘tΓ© KovΓ‘cs", - "avatar_url": "https://avatars.githubusercontent.com/u/481354?v=4", - "profile": "https://github.com/mkovaxx", - "contributions": [ - "content" - ] - }, - { - "login": "szabgab", - "name": "GΓ‘bor SzabΓ³", - "avatar_url": "https://avatars.githubusercontent.com/u/48833?v=4", - "profile": "https://szabgab.com/", - "contributions": [ - "content" - ] - }, - { - "login": "yamila-moreno", - "name": "Yamila Moreno", - "avatar_url": "https://avatars.githubusercontent.com/u/3340793?v=4", - "profile": "https://moduslaborandi.net", - "contributions": [ - "content" - ] - }, - { - "login": "willhack", - "name": "Will Hack", - "avatar_url": "https://avatars.githubusercontent.com/u/18036720?v=4", - "profile": "https://github.com/willhack", - "contributions": [ - "content" - ] - }, - { - "login": "bean5", - "name": "Michael", - "avatar_url": "https://avatars.githubusercontent.com/u/2052646?v=4", - "profile": "http://cancompute.tech", - "contributions": [ - "content" - ] - }, - { - "login": "pksadiq", - "name": "Mohammed Sadiq", - "avatar_url": "https://avatars.githubusercontent.com/u/1289514?v=4", - "profile": "https://www.sadiqpk.org", - "contributions": [ - "content" - ] - }, - { - "login": "Jak-Ch-ll", - "name": "Jakob", - "avatar_url": "https://avatars.githubusercontent.com/u/56225668?v=4", - "profile": "https://github.com/Jak-Ch-ll", - "contributions": [ - "content" - ] - }, - { - "login": "ob", - "name": "Oscar Bonilla", - "avatar_url": "https://avatars.githubusercontent.com/u/4950?v=4", - "profile": "http://oscarbonilla.com", - "contributions": [ - "content" - ] - }, - { - "login": "husjon", - "name": "Jon Erling Hustadnes", - "avatar_url": "https://avatars.githubusercontent.com/u/554229?v=4", - "profile": "https://github.com/husjon", - "contributions": [ - "content" - ] - }, - { - "login": "CobaltCause", - "name": "Charles Hall", - "avatar_url": "https://avatars.githubusercontent.com/u/7003738?v=4", - "profile": "https://github.com/CobaltCause", - "contributions": [ - "infra" - ] - }, - { - "login": "krmpotic", - "name": "Luka KrmpotiΔ‡", - "avatar_url": "https://avatars.githubusercontent.com/u/10350645?v=4", - "profile": "https://github.com/krmpotic", - "contributions": [ - "content" - ] - }, - { - "login": "jurglic", - "name": "Jurglic", - "avatar_url": "https://avatars.githubusercontent.com/u/112600?v=4", - "profile": "https://github.com/jurglic", - "contributions": [ - "content" - ] - }, - { - "login": "OfirLauber", - "name": "Ofir Lauber", - "avatar_url": "https://avatars.githubusercontent.com/u/5631030?v=4", - "profile": "https://github.com/OfirLauber", - "contributions": [ - "content" - ] - }, - { - "login": "offbyone", - "name": "Chris Rose", - "avatar_url": "https://avatars.githubusercontent.com/u/181693?v=4", - "profile": "https://github.com/offbyone", - "contributions": [ - "infra" - ] - }, - { - "login": "dieterplex", - "name": "d1t2", - "avatar_url": "https://avatars.githubusercontent.com/u/507502?v=4", - "profile": "https://github.com/dieterplex", - "contributions": [ - "infra" - ] - }, - { - "login": "docwilco", - "name": "docwilco", - "avatar_url": "https://avatars.githubusercontent.com/u/66911096?v=4", - "profile": "https://github.com/docwilco", - "contributions": [ - "code" - ] - }, - { - "login": "matthewjnield", - "name": "Matt Nield", - "avatar_url": "https://avatars.githubusercontent.com/u/64328730?v=4", - "profile": "https://www.linkedin.com/in/matthew-nield1/", - "contributions": [ - "content" - ] - }, - { - "login": "TheBearodactyl", - "name": "The Bearodactyl", - "avatar_url": "https://avatars.githubusercontent.com/u/114454115?v=4", - "profile": "https://github.com/TheBearodactyl", - "contributions": [ - "code" - ] - }, - { - "login": "markgreene74", - "name": "markgreene74", - "avatar_url": "https://avatars.githubusercontent.com/u/18945890?v=4", - "profile": "https://github.com/markgreene74", - "contributions": [ - "code" - ] - }, - { - "login": "VeeDeltaVee", - "name": "Versha Dhankar", - "avatar_url": "https://avatars.githubusercontent.com/u/45564258?v=4", - "profile": "https://github.com/VeeDeltaVee", - "contributions": [ - "doc" - ] - }, - { - "login": "0atman", - "name": "Tristram Oaten", - "avatar_url": "https://avatars.githubusercontent.com/u/114097?v=4", - "profile": "http://0atman.com", - "contributions": [ - "content" - ] - }, - { - "login": "danieltinazzi", - "name": "Daniel Tinazzi", - "avatar_url": "https://avatars.githubusercontent.com/u/11833533?v=4", - "profile": "https://github.com/danieltinazzi", - "contributions": [ - "content" - ] - }, - { - "login": "raymon-roos", - "name": "Raymon Roos", - "avatar_url": "https://avatars.githubusercontent.com/u/38888470?v=4", - "profile": "https://github.com/raymon-roos", - "contributions": [ - "content" - ] - }, - { - "login": "matthri", - "name": "Matthias", - "avatar_url": "https://avatars.githubusercontent.com/u/67913999?v=4", - "profile": "https://github.com/matthri", - "contributions": [ - "code" - ] - }, - { - "login": "neuschaefer", - "name": "J. NeuschΓ€fer", - "avatar_url": "https://avatars.githubusercontent.com/u/1021512?v=4", - "profile": "https://github.com/neuschaefer", - "contributions": [ - "code" - ] - }, - { - "login": "bastianpedersen", - "name": "Bastian Pedersen", - "avatar_url": "https://avatars.githubusercontent.com/u/58905488?v=4", - "profile": "https://scooterhacking.org", - "contributions": [ - "content" - ] - }, - { - "login": "gerases", - "name": "gerases", - "avatar_url": "https://avatars.githubusercontent.com/u/8953623?v=4", - "profile": "https://github.com/gerases", - "contributions": [ - "content" - ] - }, - { - "login": "AnonimAnonim2245", - "name": "Luca Plian", - "avatar_url": "https://avatars.githubusercontent.com/u/98339220?v=4", - "profile": "https://github.com/AnonimAnonim2245", - "contributions": [ - "code" - ] - }, - { - "login": "reifenrath-dev", - "name": "RenΓ© Reifenrath", - "avatar_url": "https://avatars.githubusercontent.com/u/18126097?v=4", - "profile": "https://reifenrath.dev/", - "contributions": [ - "content" - ] - }, - { - "login": "peterneave", - "name": "Peter Neave", - "avatar_url": "https://avatars.githubusercontent.com/u/7982708?v=4", - "profile": "https://github.com/peterneave", - "contributions": [ - "infra" - ] - }, - { - "login": "JanB1", - "name": "Jan", - "avatar_url": "https://avatars.githubusercontent.com/u/5552248?v=4", - "profile": "http://www.janb1.com", - "contributions": [ - "content" - ] - }, - { - "login": "kylev", - "name": "Kyle VanderBeek", - "avatar_url": "https://avatars.githubusercontent.com/u/46888?v=4", - "profile": "http://www.kylev.com/", - "contributions": [ - "infra" - ] - }, - { - "login": "pavedroad", - "name": "pavedroad", - "avatar_url": "https://avatars.githubusercontent.com/u/138004431?v=4", - "profile": "https://github.com/pavedroad", - "contributions": [ - "content" - ] - }, - { - "login": "hyphena", - "name": "luna", - "avatar_url": "https://avatars.githubusercontent.com/u/26529488?v=4", - "profile": "https://github.com/hyphena", - "contributions": [ - "content" - ] - }, - { - "login": "evanmiller2112", - "name": "Evan Miller", - "avatar_url": "https://avatars.githubusercontent.com/u/28488957?v=4", - "profile": "https://github.com/evanmiller2112", - "contributions": [ - "content" - ] - }, - { - "login": "luvchurchill", - "name": "luvchurchill", - "avatar_url": "https://avatars.githubusercontent.com/u/46406654?v=4", - "profile": "https://github.com/luvchurchill", - "contributions": [ - "code" - ] - }, - { - "login": "LeverImmy", - "name": "Ze-en Xiong", - "avatar_url": "https://avatars.githubusercontent.com/u/47663913?v=4", - "profile": "https://leverimmy.top/", - "contributions": [ - "content" - ] - }, - { - "login": "parnavh", - "name": "Parnav Harinathan", - "avatar_url": "https://avatars.githubusercontent.com/u/45985534?v=4", - "profile": "https://github.com/parnavh", - "contributions": [ - "content" - ] - }, - { - "login": "0Ahmed-0", - "name": "0Ahmed-0", - "avatar_url": "https://avatars.githubusercontent.com/u/111569638?v=4", - "profile": "https://github.com/0Ahmed-0", - "contributions": [ - "content" - ] - }, - { - "login": "guizo792", - "name": "guizo792", - "avatar_url": "https://avatars.githubusercontent.com/u/95940388?v=4", - "profile": "https://github.com/guizo792", - "contributions": [ - "content" - ] - }, - { - "login": "kazu728", - "name": "Kazuki Matsuo", - "avatar_url": "https://avatars.githubusercontent.com/u/34614358?v=4", - "profile": "https://github.com/kazu728", - "contributions": [ - "code" - ] - }, - { - "login": "paul-leydier", - "name": "Paul Leydier", - "avatar_url": "https://avatars.githubusercontent.com/u/75126792?v=4", - "profile": "https://github.com/paul-leydier", - "contributions": [ - "doc" - ] - }, - { - "login": "wznmickey", - "name": "wznmickey", - "avatar_url": "https://avatars.githubusercontent.com/u/44784663?v=4", - "profile": "http://wznmickey.com", - "contributions": [ - "doc" - ] - }, - { - "login": "NicolasRoelandt", - "name": "NicolasRoelandt", - "avatar_url": "https://avatars.githubusercontent.com/u/8594193?v=4", - "profile": "https://github.com/NicolasRoelandt", - "contributions": [ - "doc" - ] - }, - { - "login": "jbouganim-parallel", - "name": "Josh Bouganim", - "avatar_url": "https://avatars.githubusercontent.com/u/150748285?v=4", - "profile": "https://github.com/jbouganim-parallel", - "contributions": [ - "code" - ] - }, - { - "login": "loshz", - "name": "Dan", - "avatar_url": "https://avatars.githubusercontent.com/u/3449337?v=4", - "profile": "https://loshz.com", - "contributions": [ - "code" - ] - } - ], - "contributorsPerLine": 8, - "projectName": "rustlings", - "projectOwner": "rust-lang", - "repoType": "github", - "repoHost": "https://github.com", - "skipCi": true, - "commitConvention": "angular", - "commitType": "docs" -} diff --git a/AUTHORS.md b/AUTHORS.md deleted file mode 100644 index d82a5caba7..0000000000 --- a/AUTHORS.md +++ /dev/null @@ -1,397 +0,0 @@ -## Authors - -This file lists the people that have contributed to this project. - -Excluded from this list are @carols10cents and @diannasoreil, the principal -authors. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Carol (Nichols || Goulding)
Carol (Nichols || Goulding)

πŸ’» πŸ–‹
QuietMisdreavus
QuietMisdreavus

πŸ’» πŸ–‹
Robert M Lugg
Robert M Lugg

πŸ–‹
Hynek Schlawack
Hynek Schlawack

πŸ’»
Katharina Fey
Katharina Fey

πŸ’»
lukabavdaz
lukabavdaz

πŸ’» πŸ–‹
Erik Vesteraas
Erik Vesteraas

πŸ’»
delet0r
delet0r

πŸ’»
Shaun Bennett
Shaun Bennett

πŸ’»
Andrew Bagshaw
Andrew Bagshaw

πŸ’»
Kyle Isom
Kyle Isom

πŸ’»
Colin Pitrat
Colin Pitrat

πŸ’»
Zac Anger
Zac Anger

πŸ’»
Matthias Geier
Matthias Geier

πŸ’»
Chris Pearce
Chris Pearce

πŸ’»
Yvan Sraka
Yvan Sraka

πŸ’»
Denys Smirnov
Denys Smirnov

πŸ’»
eddyp
eddyp

πŸ’»
Brian Kung
Brian Kung

πŸ’» πŸ–‹
Russell
Russell

πŸ’»
Dan Wilhelm
Dan Wilhelm

πŸ“–
Jesse
Jesse

πŸ’» πŸ–‹
Fredrik JambrΓ©n
Fredrik JambrΓ©n

πŸ’»
Pete McFarlane
Pete McFarlane

πŸ–‹
nkanderson
nkanderson

πŸ’» πŸ–‹
Ajax M
Ajax M

πŸ“–
Dylan Nugent
Dylan Nugent

πŸ–‹
vyaslav
vyaslav

πŸ’» πŸ–‹
George
George

πŸ’»
Thomas Holloway
Thomas Holloway

πŸ’» πŸ–‹
Jubilee
Jubilee

πŸ’»
WofWca
WofWca

πŸ’»
Roberto Vidal
Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧
Jens
Jens

πŸ“–
Rahat Ahmed
Rahat Ahmed

πŸ“–
Abdou Seck
Abdou Seck

πŸ’» πŸ–‹ πŸ‘€
Katie
Katie

πŸ’»
Socrates
Socrates

πŸ“–
gnodarse
gnodarse

πŸ–‹
Harrison Metzger
Harrison Metzger

πŸ’»
Torben Jonas
Torben Jonas

πŸ’» πŸ–‹
Paul Bissex
Paul Bissex

πŸ“–
Steven Mann
Steven Mann

πŸ’» πŸ–‹
Mario Reder
Mario Reder

πŸ’» πŸ–‹
skim
skim

πŸ’»
Sanjay K
Sanjay K

πŸ’» πŸ–‹
Rohan Jain
Rohan Jain

πŸ’»
Said Aspen
Said Aspen

πŸ’» πŸ–‹
Ufuk Celebi
Ufuk Celebi

πŸ’»
lebedevsergey
lebedevsergey

πŸ“–
Aleksei Trifonov
Aleksei Trifonov

πŸ–‹
Darren Meehan
Darren Meehan

πŸ–‹
Jihchi Lee
Jihchi Lee

πŸ–‹
Christofer Bertonha
Christofer Bertonha

πŸ–‹
Vivek Bharath Akupatni
Vivek Bharath Akupatni

πŸ’» ⚠️
DΓ­dac SementΓ© FernΓ‘ndez
DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹
Rob Story
Rob Story

πŸ’»
Siobhan Jacobson
Siobhan Jacobson

πŸ’»
Evan Carroll
Evan Carroll

πŸ–‹
Jawaad Mahmood
Jawaad Mahmood

πŸ–‹
Gaurang Tandon
Gaurang Tandon

πŸ–‹
Stefan Kupresak
Stefan Kupresak

πŸ–‹
Greg Leonard
Greg Leonard

πŸ–‹
Ryan McQuen
Ryan McQuen

πŸ’»
Annika
Annika

πŸ‘€
Axel Viala
Axel Viala

πŸ’»
Mohammed Sazid Al Rashid
Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»
Caleb Webber
Caleb Webber

🚧
Peter N
Peter N

🚧
seancad
seancad

🚧
Will Hayworth
Will Hayworth

πŸ–‹
Christian Zeller
Christian Zeller

πŸ–‹
Jean-Francois Chevrette
Jean-Francois Chevrette

πŸ–‹ πŸ’»
John Baber-Lucero
John Baber-Lucero

πŸ–‹
Tal
Tal

πŸ–‹
apogeeoak
apogeeoak

πŸ–‹ πŸ’»
Larry Garfield
Larry Garfield

πŸ–‹
circumspect
circumspect

πŸ–‹
Cyrus Wyett
Cyrus Wyett

πŸ–‹
cadolphs
cadolphs

πŸ’»
Pascal H.
Pascal H.

πŸ–‹
Rod Elias
Rod Elias

πŸ–‹
Matt Lebl
Matt Lebl

πŸ’»
Ignacio Le Fluk
Ignacio Le Fluk

πŸ–‹
Taylor Yu
Taylor Yu

πŸ’» πŸ–‹
Patrick Hintermayer
Patrick Hintermayer

πŸ’»
Pete Pavlovski
Pete Pavlovski

πŸ–‹
k12ish
k12ish

πŸ–‹
Shao Yang Hong
Shao Yang Hong

πŸ–‹
Brandon Macer
Brandon Macer

πŸ–‹
Stoian Dan
Stoian Dan

πŸ–‹
Pi Delport
Pi Delport

πŸ–‹
Sateesh
Sateesh

πŸ’» πŸ–‹
ZC
ZC

πŸ–‹
hyperparabolic
hyperparabolic

πŸ’»
arlecchino
arlecchino

πŸ“–
Richthofen
Richthofen

πŸ’»
Ivan Nerazumov
Ivan Nerazumov

πŸ“–
lauralindzey
lauralindzey

πŸ“–
Rakshit Sinha
Rakshit Sinha

πŸ–‹
Damian
Damian

πŸ–‹
Ben Armstead
Ben Armstead

πŸ’»
anuk909
anuk909

πŸ–‹ πŸ’»
granddaifuku
granddaifuku

πŸ–‹
Weilet
Weilet

πŸ–‹
LIU JIE
LIU JIE

πŸ–‹
Antoine BΓΌsch
Antoine BΓΌsch

πŸ’»
frogtd
frogtd

πŸ–‹
Zhenghao Lu
Zhenghao Lu

πŸ–‹
Fredrik Enestad
Fredrik Enestad

πŸ–‹
xuesong
xuesong

πŸ–‹
Michael Walsh
Michael Walsh

πŸ’»
alirezaghey
alirezaghey

πŸ–‹
Franklin van Nes
Franklin van Nes

πŸ’»
nekonako
nekonako

πŸ’»
ZX
ZX

πŸ–‹
Yang Wen
Yang Wen

πŸ–‹
Brandon High
Brandon High

πŸ“–
x-hgg-x
x-hgg-x

πŸ’»
Kisaragi
Kisaragi

πŸ“–
Lucas Aries
Lucas Aries

πŸ–‹
ragreenburg
ragreenburg

πŸ–‹
stevenfukase
stevenfukase

πŸ–‹
J-S-Kim
J-S-Kim

πŸ–‹
Fointard
Fointard

πŸ–‹
Ryan Lowe
Ryan Lowe

πŸ’»
cui fliter
cui fliter

πŸ–‹
Ron Lusk
Ron Lusk

πŸ–‹
Bryan Lee
Bryan Lee

πŸ–‹
Nandaja Varma
Nandaja Varma

πŸ“–
pwygab
pwygab

πŸ’»
Lucas Grigolon Varela
Lucas Grigolon Varela

πŸ–‹
Bufo
Bufo

πŸ–‹
Jack Clayton
Jack Clayton

πŸ’»
Konstantin
Konstantin

πŸ–‹
0pling
0pling

πŸ–‹
KatanaFluorescent
KatanaFluorescent

πŸ’»
Drew Morris
Drew Morris

πŸ’»
camperdue42
camperdue42

πŸ–‹
YsuOS
YsuOS

πŸ–‹
Steven Nguyen
Steven Nguyen

πŸ–‹
nacairns1
nacairns1

πŸ–‹
Paulo Gabriel Justino Bezerra
Paulo Gabriel Justino Bezerra

πŸ–‹
Jason
Jason

πŸ–‹
exdx
exdx

πŸ–‹
James Zow
James Zow

πŸ–‹
James Bromley
James Bromley

πŸ–‹
swhiteCQC
swhiteCQC

πŸ–‹
Neil Pate
Neil Pate

πŸ–‹
wojexe
wojexe

πŸ–‹
Mattia Schiavon
Mattia Schiavon

πŸ–‹
Eric Jolibois
Eric Jolibois

πŸ–‹
Edwin Chang
Edwin Chang

πŸ–‹
Saikat Das
Saikat Das

πŸ–‹
Jeremy Goh
Jeremy Goh

πŸ–‹
Lioness100
Lioness100

πŸ–‹
Tristan Nicholls
Tristan Nicholls

πŸ–‹
Claire
Claire

πŸ–‹
Maurice Van Wassenhove
Maurice Van Wassenhove

πŸ–‹
John Mendelewski
John Mendelewski

πŸ’»
Brian Fakhoury
Brian Fakhoury

πŸ–‹
Markus Boehme
Markus Boehme

πŸ’»
Nico Vromans
Nico Vromans

πŸ–‹
vostok92
vostok92

πŸ–‹
Magnus RΓΈdseth
Magnus RΓΈdseth

πŸ–‹
rubiesonthesky
rubiesonthesky

πŸ–‹
Gabriel Bianconi
Gabriel Bianconi

πŸ–‹
Kody Low
Kody Low

πŸ–‹
rzrymiak
rzrymiak

πŸ–‹
Miguel Raz GuzmΓ‘n Macedo
Miguel Raz GuzmΓ‘n Macedo

πŸ–‹
Magnus Markling
Magnus Markling

πŸ–‹
Tiago De Gaspari
Tiago De Gaspari

πŸ–‹
skaunov
skaunov

πŸ–‹
Cal Jacobson
Cal Jacobson

πŸ–‹
Duchoud Nicolas
Duchoud Nicolas

πŸ–‹
Gaëtan Faugère
Gaëtan Faugère

πŸ”§
bhbuehler
bhbuehler

πŸ–‹
Yuri Astrakhan
Yuri Astrakhan

πŸ’»
azzamsa
azzamsa

πŸ’»
mvanschellebeeck
mvanschellebeeck

πŸ–‹
Arkid
Arkid

πŸ–‹
Tom Kunc
Tom Kunc

πŸ–‹
Marek FurΓ‘k
Marek FurΓ‘k

πŸ–‹
Winter
Winter

πŸ’»
Moritz BΓΆhme
Moritz BΓΆhme

πŸ’»
craymel
craymel

πŸ–‹
TK Buristrakul
TK Buristrakul

πŸ–‹
Kent Worthington
Kent Worthington

πŸ–‹
seporterfield
seporterfield

πŸ–‹
David Barroso
David Barroso

πŸš‡
Tobias Klauser
Tobias Klauser

πŸ’»
0xMySt1c
0xMySt1c

πŸ”§
Ten
Ten

πŸ’»
jones martin
jones martin

πŸ–‹
cloppingemu
cloppingemu

πŸ’»
Kevin Wan
Kevin Wan

πŸ–‹
Ruby
Ruby

πŸ’»
Alexander Gill
Alexander Gill

πŸ’»
Jarrod Sanders
Jarrod Sanders

πŸ–‹
Andrew Sen
Andrew Sen

πŸ–‹
Grzegorz Ε»ur
Grzegorz Ε»ur

πŸ–‹
Daan Wynen
Daan Wynen

πŸ–‹
Anush
Anush

πŸ“–
Gleb Shevchenko
Gleb Shevchenko

πŸ–‹
Edmundo Paulino
Edmundo Paulino

πŸš‡
Emmanuel Roullit
Emmanuel Roullit

πŸš‡
Nidhal Messaoudi
Nidhal Messaoudi

πŸ’»
Mahdi Bahrami
Mahdi Bahrami

πŸ”§
Nagidal
Nagidal

πŸ–‹
Adam Brewer
Adam Brewer

πŸ–‹
Eugene
Eugene

πŸ”§
Ed Sweeney
Ed Sweeney

πŸ–‹
javihernant
javihernant

πŸ–‹
Vegard
Vegard

πŸ–‹
Ryan Whitehouse
Ryan Whitehouse

πŸ–‹
Ali Afsharzadeh
Ali Afsharzadeh

πŸ–‹
Keogami
Keogami

πŸ“–
Alexandre Esse
Alexandre Esse

πŸ–‹
Sagar Vora
Sagar Vora

πŸ–‹
Kacper Poneta
Kacper Poneta

πŸ–‹
Aaron Suggs
Aaron Suggs

πŸ–‹
Alex
Alex

πŸ–‹
Sebastian TΓΆrnquist
Sebastian TΓΆrnquist

πŸ–‹
Sebastian LaVine
Sebastian LaVine

πŸ’»
Alan Gerber
Alan Gerber

πŸ–‹
Eric
Eric

πŸ–‹
Aaron Wang
Aaron Wang

πŸ–‹
Noah
Noah

πŸ–‹
rb5014
rb5014

πŸ–‹
deedy5
deedy5

πŸ–‹
lionel-rowe
lionel-rowe

πŸ–‹
Ben
Ben

πŸ–‹
b1ue64
b1ue64

πŸ–‹
lazywalker
lazywalker

πŸ–‹
proofconstruction
proofconstruction

πŸš‡
IVIURRAY
IVIURRAY

πŸ–‹
Bert Apperlo
Bert Apperlo

πŸ–‹
Florine W. Dekker
Florine W. Dekker

πŸ–‹
Mehul Gangavelli
Mehul Gangavelli

πŸ–‹
Mikael Frosthage
Mikael Frosthage

πŸ–‹
Robert Fry
Robert Fry

πŸ–‹
tajo48
tajo48

πŸ–‹
Anish
Anish

πŸ–‹
vnprc
vnprc

πŸ–‹
Joshua Carlson
Joshua Carlson

πŸ–‹
Nicholas R. Smith
Nicholas R. Smith

πŸ’»
Alexander GonzΓ‘lez
Alexander GonzΓ‘lez

πŸ–‹
Marcus HΓΆjvall
Marcus HΓΆjvall

πŸ–‹
Alon Hearter
Alon Hearter

πŸ–‹
shirts
shirts

πŸ–‹
Ivan Vasiunyk
Ivan Vasiunyk

πŸ–‹
Mo
Mo

πŸ’»
x10an14
x10an14

πŸš‡
Roi Gabay
Roi Gabay

πŸ–‹
MΓ‘tΓ© KovΓ‘cs
MΓ‘tΓ© KovΓ‘cs

πŸ–‹
GΓ‘bor SzabΓ³
GΓ‘bor SzabΓ³

πŸ–‹
Yamila Moreno
Yamila Moreno

πŸ–‹
Will Hack
Will Hack

πŸ–‹
Michael
Michael

πŸ–‹
Mohammed Sadiq
Mohammed Sadiq

πŸ–‹
Jakob
Jakob

πŸ–‹
Oscar Bonilla
Oscar Bonilla

πŸ–‹
Jon Erling Hustadnes
Jon Erling Hustadnes

πŸ–‹
Charles Hall
Charles Hall

πŸš‡
Luka Krmpotić
Luka Krmpotić

πŸ–‹
Jurglic
Jurglic

πŸ–‹
Ofir Lauber
Ofir Lauber

πŸ–‹
Chris Rose
Chris Rose

πŸš‡
d1t2
d1t2

πŸš‡
docwilco
docwilco

πŸ’»
Matt Nield
Matt Nield

πŸ–‹
The Bearodactyl
The Bearodactyl

πŸ’»
markgreene74
markgreene74

πŸ’»
Versha Dhankar
Versha Dhankar

πŸ“–
Tristram Oaten
Tristram Oaten

πŸ–‹
Daniel Tinazzi
Daniel Tinazzi

πŸ–‹
Raymon Roos
Raymon Roos

πŸ–‹
Matthias
Matthias

πŸ’»
J. NeuschΓ€fer
J. NeuschΓ€fer

πŸ’»
Bastian Pedersen
Bastian Pedersen

πŸ–‹
gerases
gerases

πŸ–‹
Luca Plian
Luca Plian

πŸ’»
RenΓ© Reifenrath
RenΓ© Reifenrath

πŸ–‹
Peter Neave
Peter Neave

πŸš‡
Jan
Jan

πŸ–‹
Kyle VanderBeek
Kyle VanderBeek

πŸš‡
pavedroad
pavedroad

πŸ–‹
luna
luna

πŸ–‹
Evan Miller
Evan Miller

πŸ–‹
luvchurchill
luvchurchill

πŸ’»
Ze-en Xiong
Ze-en Xiong

πŸ–‹
Parnav Harinathan
Parnav Harinathan

πŸ–‹
0Ahmed-0
0Ahmed-0

πŸ–‹
guizo792
guizo792

πŸ–‹
Kazuki Matsuo
Kazuki Matsuo

πŸ’»
Paul Leydier
Paul Leydier

πŸ“–
wznmickey
wznmickey

πŸ“–
NicolasRoelandt
NicolasRoelandt

πŸ“–
Josh Bouganim
Josh Bouganim

πŸ’»
Dan
Dan

πŸ’»
- - - - - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! From 09d8bc83ff2e7fe991dc72ab431a1cde8ee2c543 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 13:08:02 +0200 Subject: [PATCH 0760/1432] Remove README reference --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 96421ebdf4..9d09704242 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,3 @@ Now you should be done! ## Contributing See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md). - -## Contributors ✨ - -Thanks goes to the wonderful people listed in [AUTHORS.md](https://github.com/rust-lang/rustlings/blob/main/AUTHORS.md) πŸŽ‰ From aaf183142e27e8a3232481dfd6bdc09e4403aeeb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 16:17:33 +0200 Subject: [PATCH 0761/1432] =?UTF-8?q?Bring=20back=20the=20thanks=20to=20co?= =?UTF-8?q?ntributors=20=E2=9D=A4=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9d09704242..36e771b0a4 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,7 @@ Now you should be done! ## Contributing See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md). + +## Contributors ✨ + +Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) πŸŽ‰β€οΈ From daa090981aeafb24ed907cf85de49904ce17fc37 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 17:17:21 +0200 Subject: [PATCH 0762/1432] Update README --- README.md | 100 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 36e771b0a4..bfa3f06f8f 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,124 @@

-# rustlings πŸ¦€β€οΈ +# Rustlings πŸ¦€β€οΈ
-Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! +Greetings and welcome to Rustlings. +This project contains small exercises to get you used to reading and writing Rust code. +This includes reading and responding to compiler messages! -Alternatively, for a first-time Rust learner, there are several other resources: +It is recommended to do the Rustlings exercises in parallel to reading [the official Rust book](https://doc.rust-lang.org/book/), the most comprehensive resource for learning Rust πŸ“šοΈ -- [The Book](https://doc.rust-lang.org/book/index.html) - The most comprehensive resource for learning Rust, but a bit theoretical sometimes. You will be using this along with Rustlings! -- [Rust By Example](https://doc.rust-lang.org/rust-by-example/index.html) - Learn Rust by solving little exercises! It's almost like `rustlings`, but online +[Rust By Example](https://doc.rust-lang.org/rust-by-example/) is another recommended resource that you might find helpful. +It contains code examples and exercises similar to Rustlings, but online. ## Getting Started -_Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._ -_Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt install gcc`. Yum: `sudo yum -y install gcc`._ +### Installing Rust -You will need to have Rust installed. You can get it by visiting . This'll also install Cargo, Rust's package/project manager. +Before installing Rustlings, you need to have _Rust installed_. +Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. +This'll also install _Cargo_, Rust's package/project manager. - +🐧 If you're on Linux, make sure you've installed `gcc` (for a linker). Deb: `sudo apt install build-essential gcc`. Dnf: `sudo dnf install gcc`. -## Doing exercises +🍎 If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`. -The exercises are sorted by topic and can be found in the subdirectory `rustlings/exercises/`. For every topic there is an additional README file with some resources to get you started on the topic. We really recommend that you have a look at them before you start. +### Installing Rustlings -The task is simple. Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! Some exercises are also run as tests, but rustlings handles them all the same. To run the exercises in the recommended order, execute: +The following command will download and compile Rustlings: ```bash -rustlings watch +cargo install rustlings --locked ``` -This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. +### Initialization -In case you want to go by your own order, or want to only verify a single exercise, you can run: +After installing Rustlings, run the following command to initialize the `rustlings/` directory: ```bash -rustlings run myExercise1 +rustlings init ``` -Or simply use the following command to run the next unsolved exercise in the course: +Now, go into the newly initialized directory and run Rustlings for further instructions on getting started with the exercises: ```bash -rustlings run next +cd rustlings/ +rustlings ``` -In case you get stuck, you can run the following command to get a hint for your -exercise: +## Doing exercises + +The exercises are sorted by topic and can be found in the subdirectory `rustlings/exercises/`. +For every topic there is an additional README file with some resources to get you started on the topic. +We really recommend that you have a look at them before you start. + +The task is simple. +Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! +Some exercises are also run as tests, but Rustlings handles them all the same. +To run the exercises in the recommended order, execute: ```bash -rustlings hint myExercise1 +rustlings ``` -You can also get the hint for the next unsolved exercise with the following command: +This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). +It will also rerun automatically every time you change a file in the `exercises/` directory. + +In case you want to go by your own order, or want to only verify a single exercise, you can run: ```bash -rustlings hint next +rustlings run EXERCISE_NAME ``` -To check your progress, you can run the following command: +Or simply use the following command to run the next pending exercise in the course: ```bash -rustlings list +rustlings run ``` -## Testing yourself +In case you get stuck, you can run the following command to get a hint for your exercise: -After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. These quizzes are found in `exercises/quizN.rs`. +```bash +rustlings hint EXERCISE_NAME +``` + +You can also get the hint for the next pending exercise with the following command: + +```bash +rustlings hint +``` + +## Quizzes + +After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. +These quizzes are found in `exercises/quizN.rs`. ## Continuing On -Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. + + +Once you've completed Rustlings, put your new knowledge to good use! +Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. ## Uninstalling Rustlings -If you want to remove Rustlings from your system, there are two steps. First, you'll need to remove the exercises folder that the install script created -for you: +If you want to remove Rustlings from your system, there are two steps. + +1️⃣ Remove the `rustlings` directory that was created by `rustlings init`: ```bash -rm -rf rustlings # or your custom folder name, if you chose and or renamed it +rm -r rustlings ``` -Second, run `cargo uninstall` to remove the `rustlings` binary: +2️⃣ Run `cargo uninstall` to remove the `rustlings` binary: ```bash cargo uninstall rustlings ``` -Now you should be done! +That's it! ## Contributing @@ -94,4 +126,4 @@ See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUT ## Contributors ✨ -Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) πŸŽ‰β€οΈ +Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) πŸŽ‰ From a2be6754bf2833371fe99ddc2d01c0d518f8eb27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 18 Apr 2024 17:17:39 +0200 Subject: [PATCH 0763/1432] Make the exercise name option for the hint subcommand --- src/main.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 04697379fc..4c5f1146f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,10 +63,10 @@ enum Subcommands { /// The name of the exercise name: String, }, - /// Return a hint for the given exercise + /// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified. Hint { /// The name of the exercise - name: String, + name: Option, }, #[command(subcommand)] Dev(DevCommands), @@ -162,7 +162,9 @@ fn main() -> Result<()> { println!("The exercise {exercise_path} has been reset"); } Some(Subcommands::Hint { name }) => { - app_state.set_current_exercise_by_name(&name)?; + if let Some(name) = name { + app_state.set_current_exercise_by_name(&name)?; + } println!("{}", app_state.current_exercise().hint); } // Handled in an earlier match. From ea504e6bf11a23bb010b7b98a58a97ec2dcd0489 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 19 Apr 2024 12:41:13 +0200 Subject: [PATCH 0764/1432] Update deps --- Cargo.lock | 103 ++++++++++++++++++++++++++++------------------------- Cargo.toml | 10 +++--- 2 files changed, 60 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4853d0c37..0988dbcdf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "assert_cmd" @@ -203,9 +203,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encode_unicode" @@ -390,9 +390,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mio" @@ -495,18 +495,18 @@ 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", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -599,18 +599,18 @@ dependencies = [ [[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", @@ -619,9 +619,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -645,15 +645,15 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.55" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -677,9 +677,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "fb686a972ccef8537b39eead3968b0e8616cb5040dbb9bba93007c8e07c9215f" dependencies = [ "indexmap", "serde", @@ -789,7 +789,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -809,17 +809,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -830,9 +831,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -842,9 +843,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -854,9 +855,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -866,9 +873,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -878,9 +885,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -890,9 +897,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -902,15 +909,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 2d152cfc5f..6f523abfce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,17 +9,17 @@ authors = [ edition = "2021" [dependencies] -anyhow = "1.0.81" +anyhow = "1.0.82" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" -serde_json = "1.0.115" -serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.116" +serde = { version = "1.0.198", features = ["derive"] } shlex = "1.3.0" -toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.11", default-features = false, features = ["parse", "serde"] } which = "6.0.1" -winnow = "0.6.5" +winnow = "0.6.6" [[bin]] name = "rustlings" From d83c91edc6365c90eb8d50be6f7036b38a64cba2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 18:20:15 +0200 Subject: [PATCH 0765/1432] Ignore all lock files but the one in root --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 80f9092abb..945382c37d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Cargo target/ -/tests/fixture/*/Cargo.lock -/dev/Cargo.lock +Cargo.lock +!/Cargo.lock # State file .rustlings-state.txt From f1a60780b9d8cd7be544c3e09ddeb3834493c271 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 19:26:19 +0200 Subject: [PATCH 0766/1432] Rename constant --- src/dev.rs | 6 +++--- src/dev/check.rs | 4 ++-- src/dev/update.rs | 4 ++-- src/exercise.rs | 4 ++-- src/main.rs | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 1430f11e55..f28218112d 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Context, Result}; use clap::Subcommand; -use crate::DEVELOPING_OFFICIAL_RUSTLINGS; +use crate::DEBUG_PROFILE; mod check; mod init; @@ -18,8 +18,8 @@ impl DevCommands { pub fn run(self) -> Result<()> { match self { DevCommands::Init => { - if DEVELOPING_OFFICIAL_RUSTLINGS { - bail!("Disabled while developing the official Rustlings"); + if DEBUG_PROFILE { + bail!("Disabled in the debug build"); } init::init().context(INIT_ERR) diff --git a/src/dev/check.rs b/src/dev/check.rs index e95eb3c6f5..cd115b71ec 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -8,7 +8,7 @@ use std::{ use crate::{ info_file::{ExerciseInfo, InfoFile}, - CURRENT_FORMAT_VERSION, DEVELOPING_OFFICIAL_RUSTLINGS, + CURRENT_FORMAT_VERSION, DEBUG_PROFILE, }; fn forbidden_char(input: &str) -> Option { @@ -193,7 +193,7 @@ pub fn check() -> Result<()> { let info_file = InfoFile::parse()?; check_exercises(&info_file)?; - if DEVELOPING_OFFICIAL_RUSTLINGS { + if DEBUG_PROFILE { check_cargo_toml( &info_file.exercises, include_str!("../../dev/Cargo.toml"), diff --git a/src/dev/update.rs b/src/dev/update.rs index 65dcf76823..1f032f7983 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -4,7 +4,7 @@ use anyhow::{Context, Result}; use crate::{ info_file::{ExerciseInfo, InfoFile}, - DEVELOPING_OFFICIAL_RUSTLINGS, + DEBUG_PROFILE, }; use super::check::{append_bins, bins_start_end_ind}; @@ -30,7 +30,7 @@ fn update_cargo_toml( pub fn update() -> Result<()> { let info_file = InfoFile::parse()?; - if DEVELOPING_OFFICIAL_RUSTLINGS { + if DEBUG_PROFILE { update_cargo_toml( &info_file.exercises, include_str!("../../dev/Cargo.toml"), diff --git a/src/exercise.rs b/src/exercise.rs index eb7b725ee7..e85efe4cdd 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -7,7 +7,7 @@ use std::{ process::{Command, Output}, }; -use crate::{info_file::Mode, DEVELOPING_OFFICIAL_RUSTLINGS}; +use crate::{info_file::Mode, DEBUG_PROFILE}; pub struct TerminalFileLink<'a> { path: &'a str, @@ -48,7 +48,7 @@ impl Exercise { cmd.arg(command); // A hack to make `cargo run` work when developing Rustlings. - if DEVELOPING_OFFICIAL_RUSTLINGS && Path::new("tests").exists() { + if DEBUG_PROFILE && Path::new("tests").exists() { cmd.arg("--manifest-path").arg("dev/Cargo.toml"); } diff --git a/src/main.rs b/src/main.rs index 4c5f1146f1..69ac0a39ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ mod run; mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; -const DEVELOPING_OFFICIAL_RUSTLINGS: bool = { +const DEBUG_PROFILE: bool = { #[allow(unused_assignments, unused_mut)] let mut debug_profile = false; @@ -79,8 +79,8 @@ fn main() -> Result<()> { match args.command { Some(Subcommands::Init) => { - if DEVELOPING_OFFICIAL_RUSTLINGS { - bail!("Disabled while developing the official Rustlings"); + if DEBUG_PROFILE { + bail!("Disabled in the debug build"); } return init::init().context("Initialization failed"); From 04d36996dd745af284be380bd902e0bad491b87a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 19:27:00 +0200 Subject: [PATCH 0767/1432] Update deps --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b194de618e..fdc79365d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,9 +656,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno", @@ -771,9 +771,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -830,9 +830,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -856,9 +856,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", diff --git a/Cargo.toml b/Cargo.toml index 5e3be11313..6181b1e9fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ notify-debouncer-mini = "0.4.1" 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"] } +toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } which = "6.0.1" [dev-dependencies] From 49e4a1fab04560cf0e868ab8214dfc94e76b9f4b Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 19:34:55 +0200 Subject: [PATCH 0768/1432] Catch the usage of the old method --- dev/rustlings-repo.txt | 1 + src/main.rs | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 dev/rustlings-repo.txt diff --git a/dev/rustlings-repo.txt b/dev/rustlings-repo.txt new file mode 100644 index 0000000000..62793612c4 --- /dev/null +++ b/dev/rustlings-repo.txt @@ -0,0 +1 @@ +This file is used to check if the user tries to run Rustlings in the repository (the method before v6) diff --git a/src/main.rs b/src/main.rs index 69ac0a39ba..bf2498a9dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,6 +75,10 @@ enum Subcommands { fn main() -> Result<()> { let args = Args::parse(); + if !DEBUG_PROFILE && Path::new("dev/rustlings-repo.txt").exists() { + bail!("{OLD_METHOD_ERR}"); + } + which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; match args.command { @@ -174,6 +178,11 @@ fn main() -> Result<()> { Ok(()) } +const OLD_METHOD_ERR: &str = "You are trying to run Rustlings using the old method before v6. +The new method doesn't include cloning the Rustlings' repository. +Please follow the instructions in the README: +https://github.com/rust-lang/rustlings#getting-started"; + const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`. Did you already install Rust? Try running `cargo --version` to diagnose the problem."; From 642c3bd37e3195f7f744a5fa60a53e59d8da5526 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 20:22:01 +0200 Subject: [PATCH 0769/1432] Fix the generated Cargo.toml after rustlings init --- dev/Cargo.toml | 2 +- src/cargo_toml.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++ src/dev/check.rs | 51 +++++++++--------------------------------- src/dev/update.rs | 20 +++++++---------- src/init.rs | 26 +++++++++++---------- src/main.rs | 1 + 6 files changed, 91 insertions(+), 66 deletions(-) create mode 100644 src/cargo_toml.rs diff --git a/dev/Cargo.toml b/dev/Cargo.toml index e66973e061..eddf01646d 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,4 +1,4 @@ -# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update` +# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`. This comment line will be stripped in `rustlings init`. bin = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, { name = "intro2", path = "../exercises/00_intro/intro2.rs" }, diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs new file mode 100644 index 0000000000..2345a7ee9b --- /dev/null +++ b/src/cargo_toml.rs @@ -0,0 +1,57 @@ +use anyhow::{Context, Result}; + +use crate::info_file::ExerciseInfo; + +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, + 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"); + } +} + +pub fn updated_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 mut updated_cargo_toml = Vec::with_capacity(1 << 13); + updated_cargo_toml.extend_from_slice(current_cargo_toml[..bins_start_ind].as_bytes()); + append_bins( + &mut updated_cargo_toml, + exercise_infos, + exercise_path_prefix, + ); + updated_cargo_toml.extend_from_slice(current_cargo_toml[bins_end_ind..].as_bytes()); + + Ok(updated_cargo_toml) +} diff --git a/src/dev/check.rs b/src/dev/check.rs index cd115b71ec..9859c3e39a 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -7,6 +7,7 @@ use std::{ }; use crate::{ + cargo_toml::{append_bins, bins_start_end_ind}, info_file::{ExerciseInfo, InfoFile}, CURRENT_FORMAT_VERSION, DEBUG_PROFILE, }; @@ -136,41 +137,6 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { 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, - 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, @@ -183,7 +149,13 @@ fn check_cargo_toml( 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"); + if DEBUG_PROFILE { + bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it"); + } else { + bail!( + "The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it", + ); + } } Ok(()) @@ -198,14 +170,11 @@ pub fn check() -> Result<()> { &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, ¤t_cargo_toml, b"").context( - "The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it", - )?; + check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"")?; } println!("\nEverything looks fine!"); diff --git a/src/dev/update.rs b/src/dev/update.rs index 1f032f7983..d2f20aaed3 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -3,26 +3,22 @@ use std::fs; use anyhow::{Context, Result}; use crate::{ + cargo_toml::updated_cargo_toml, info_file::{ExerciseInfo, InfoFile}, DEBUG_PROFILE, }; -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], + cargo_toml_path: &str, ) -> Result<()> { - let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?; + let updated_cargo_toml = + updated_cargo_toml(exercise_infos, current_cargo_toml, exercise_path_prefix)?; - 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")?; + fs::write(cargo_toml_path, updated_cargo_toml) + .context("Failed to write the `Cargo.toml` file")?; Ok(()) } @@ -34,8 +30,8 @@ pub fn update() -> Result<()> { update_cargo_toml( &info_file.exercises, include_str!("../../dev/Cargo.toml"), - "dev/Cargo.toml", b"../", + "dev/Cargo.toml", ) .context("Failed to update the file `dev/Cargo.toml`")?; @@ -43,7 +39,7 @@ pub fn update() -> Result<()> { } 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, ¤t_cargo_toml, "Cargo.toml", b"") + update_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"", "Cargo.toml") .context("Failed to update the file `Cargo.toml`")?; println!("Updated `Cargo.toml`"); diff --git a/src/init.rs b/src/init.rs index 52315e240e..f210db77ab 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,17 +6,7 @@ use std::{ path::Path, }; -use crate::embedded::EMBEDDED_FILES; - -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 -}; +use crate::{cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile}; pub fn init() -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { @@ -38,7 +28,19 @@ pub fn init() -> Result<()> { .init_exercises_dir() .context("Failed to initialize the `rustlings/exercises` directory")?; - fs::write("Cargo.toml", CARGO_TOML) + let info_file = InfoFile::parse()?; + let current_cargo_toml = include_str!("../dev/Cargo.toml"); + // Skip the first line (comment). + let newline_ind = current_cargo_toml + .as_bytes() + .iter() + .position(|c| *c == b'\n') + .context("The embedded `Cargo.toml` is empty or contains only one line.")?; + let current_cargo_toml = + ¤t_cargo_toml[(newline_ind + 1).min(current_cargo_toml.len() - 1)..]; + let updated_cargo_toml = updated_cargo_toml(&info_file.exercises, current_cargo_toml, b"") + .context("Failed to generate `Cargo.toml`")?; + fs::write("Cargo.toml", updated_cargo_toml) .context("Failed to create the file `rustlings/Cargo.toml`")?; fs::write(".gitignore", GITIGNORE) diff --git a/src/main.rs b/src/main.rs index bf2498a9dd..494c40d4a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ use std::{ use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; mod app_state; +mod cargo_toml; mod dev; mod embedded; mod exercise; From e3b9124b85502d21b8d45abe8e4e7e4d3e08e7be Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 23:24:10 +0200 Subject: [PATCH 0770/1432] Add a confirmation prompt to the init subcommand --- src/main.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.rs b/src/main.rs index 494c40d4a8..f52699c7fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -88,6 +88,14 @@ fn main() -> Result<()> { bail!("Disabled in the debug build"); } + { + let mut stdout = io::stdout().lock(); + stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; + stdout.flush()?; + io::stdin().lock().read_until(b'\n', &mut Vec::new())?; + stdout.write_all(b"\n")?; + } + return init::init().context("Initialization failed"); } Some(Subcommands::Dev(dev_command)) => return dev_command.run(), From 30040d77781e03043e72d09d7fe8cf1cf5436a9c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 23:39:44 +0200 Subject: [PATCH 0771/1432] Add a disclaimer to the state file --- src/app_state.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 9868781801..09de2a3e84 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -53,7 +53,8 @@ impl AppState { } // See `Self::write` for more information about the file format. - let mut lines = self.file_buf.split(|c| *c == b'\n'); + let mut lines = self.file_buf.split(|c| *c == b'\n').skip(2); + let Some(current_exercise_name) = lines.next() else { return StateFileStatus::NotRead; }; @@ -300,13 +301,17 @@ impl AppState { // Write the state file. // The file's format is very simple: - // - The first line is the name of the current exercise. It must end with `\n` even if there - // are no done exercises. + // - The first line is a comment. // - The second line is an empty line. + // - The third line is the name of the current exercise. It must end with `\n` even if there + // are no done exercises. + // - The fourth line is an empty line. // - All remaining lines are the names of done exercises. fn write(&mut self) -> Result<()> { self.file_buf.clear(); + self.file_buf + .extend_from_slice(b"DON'T EDIT THIS FILE!\n\n"); self.file_buf .extend_from_slice(self.current_exercise().name.as_bytes()); self.file_buf.push(b'\n'); From 61a84a2c118af05493c86862e2eb5dbf7977a02e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 23:43:49 +0200 Subject: [PATCH 0772/1432] dev init -> dev new PATH --- src/dev.rs | 8 ++++---- src/dev/{init.rs => new.rs} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename src/dev/{init.rs => new.rs} (100%) diff --git a/src/dev.rs b/src/dev.rs index f28218112d..a55a95e14e 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -4,12 +4,12 @@ use clap::Subcommand; use crate::DEBUG_PROFILE; mod check; -mod init; +mod new; mod update; #[derive(Subcommand)] pub enum DevCommands { - Init, + New { path: String }, Check, Update, } @@ -17,12 +17,12 @@ pub enum DevCommands { impl DevCommands { pub fn run(self) -> Result<()> { match self { - DevCommands::Init => { + DevCommands::New { path } => { if DEBUG_PROFILE { bail!("Disabled in the debug build"); } - init::init().context(INIT_ERR) + new::init().context(INIT_ERR) } DevCommands::Check => check::check(), DevCommands::Update => update::update(), diff --git a/src/dev/init.rs b/src/dev/new.rs similarity index 100% rename from src/dev/init.rs rename to src/dev/new.rs From e93a99e19e5f1a5dee6b28ed2f703bff57038b11 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 22 Apr 2024 00:34:55 +0200 Subject: [PATCH 0773/1432] Third-party exercises should be in a separate Git repo --- src/dev.rs | 6 ++-- src/dev/new.rs | 93 +++++++++++++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index a55a95e14e..68777d1057 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use anyhow::{bail, Context, Result}; use clap::Subcommand; @@ -9,7 +11,7 @@ mod update; #[derive(Subcommand)] pub enum DevCommands { - New { path: String }, + New { path: PathBuf }, Check, Update, } @@ -22,7 +24,7 @@ impl DevCommands { bail!("Disabled in the debug build"); } - new::init().context(INIT_ERR) + new::new(&path).context(INIT_ERR) } DevCommands::Check => check::check(), DevCommands::Update => update::update(), diff --git a/src/dev/new.rs b/src/dev/new.rs index 3ce5055c5c..b0828a4b4e 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -1,41 +1,72 @@ -use anyhow::{Context, Result}; -use std::fs::{self, create_dir}; +use anyhow::{bail, Context, Result}; +use std::{ + env::set_current_dir, + fs::{self, create_dir}, + path::Path, + process::Command, +}; use crate::CURRENT_FORMAT_VERSION; -pub fn init() -> Result<()> { - create_dir("rustlings").context("Failed to create the directory `rustlings`")?; +fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> { + create_dir(dir_name) + .with_context(|| format!("Failed to create the directory {current_dir}/{dir_name}"))?; + println!("Created the directory {current_dir}/{dir_name}"); + Ok(()) +} - create_dir("rustlings/exercises") - .context("Failed to create the directory `rustlings/exercises`")?; +fn write_rel_file(file_name: &str, current_dir: &str, content: C) -> Result<()> +where + C: AsRef<[u8]>, +{ + fs::write(file_name, content) + .with_context(|| format!("Failed to create the file {current_dir}/{file_name}"))?; + // Space to align with `create_rel_dir`. + println!("Created the file {current_dir}/{file_name}"); + Ok(()) +} - create_dir("rustlings/solutions") - .context("Failed to create the directory `rustlings/solutions`")?; +pub fn new(path: &Path) -> Result<()> { + let dir_name = path.to_string_lossy(); - 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`")?; + create_dir(path).with_context(|| format!("Failed to create the directory {dir_name}"))?; + println!("Created the directory {dir_name}"); + + set_current_dir(path) + .with_context(|| format!("Failed to set {dir_name} as the current directory"))?; - fs::write("rustlings/Cargo.toml", CARGO_TOML) - .context("Failed to create the file `rustlings/Cargo.toml`")?; + if !Command::new("git") + .arg("init") + .status() + .context("Failed to run `git init`")? + .success() + { + bail!("`git init` didn't run successfully. See the error message above"); + } - fs::write("rustlings/.gitignore", crate::init::GITIGNORE) - .context("Failed to create the file `rustlings/.gitignore`")?; + write_rel_file(".gitignore", &dir_name, crate::init::GITIGNORE)?; - fs::write("rustlings/README.md", README) - .context("Failed to create the file `rustlings/README.md`")?; + create_rel_dir("exercises", &dir_name)?; + create_rel_dir("solutions", &dir_name)?; - create_dir("rustlings/.vscode") - .context("Failed to create the directory `rustlings/.vscode`")?; - fs::write( - "rustlings/.vscode/extensions.json", + write_rel_file( + "info.toml", + &dir_name, + format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"), + )?; + + write_rel_file("Cargo.toml", &dir_name, CARGO_TOML)?; + + write_rel_file("README.md", &dir_name, README)?; + + create_rel_dir(".vscode", &dir_name)?; + write_rel_file( + ".vscode/extensions.json", + &dir_name, crate::init::VS_CODE_EXTENSIONS_JSON, - ) - .context("Failed to create the file `rustlings/.vscode/extensions.json`")?; + )?; - println!("{INIT_DONE}"); + println!("\nInitialization done βœ“"); Ok(()) } @@ -97,13 +128,3 @@ First, 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"]"#; From 4ce2714da1f079e81b6887b52b1acfbc283a3d63 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 22 Apr 2024 00:38:34 +0200 Subject: [PATCH 0774/1432] Add --no-git --- src/dev.rs | 10 +++++++--- src/dev/new.rs | 13 +++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 68777d1057..d7f9af6502 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -11,7 +11,11 @@ mod update; #[derive(Subcommand)] pub enum DevCommands { - New { path: PathBuf }, + New { + path: PathBuf, + #[arg(long)] + no_git: bool, + }, Check, Update, } @@ -19,12 +23,12 @@ pub enum DevCommands { impl DevCommands { pub fn run(self) -> Result<()> { match self { - DevCommands::New { path } => { + DevCommands::New { path, no_git } => { if DEBUG_PROFILE { bail!("Disabled in the debug build"); } - new::new(&path).context(INIT_ERR) + new::new(&path, no_git).context(INIT_ERR) } DevCommands::Check => check::check(), DevCommands::Update => update::update(), diff --git a/src/dev/new.rs b/src/dev/new.rs index b0828a4b4e..82aba42bb8 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -26,7 +26,7 @@ where Ok(()) } -pub fn new(path: &Path) -> Result<()> { +pub fn new(path: &Path, no_git: bool) -> Result<()> { let dir_name = path.to_string_lossy(); create_dir(path).with_context(|| format!("Failed to create the directory {dir_name}"))?; @@ -35,11 +35,12 @@ pub fn new(path: &Path) -> Result<()> { set_current_dir(path) .with_context(|| format!("Failed to set {dir_name} as the current directory"))?; - if !Command::new("git") - .arg("init") - .status() - .context("Failed to run `git init`")? - .success() + if !no_git + && !Command::new("git") + .arg("init") + .status() + .context("Failed to run `git init`")? + .success() { bail!("`git init` didn't run successfully. See the error message above"); } From 86684b7fc9dd5e8bedad6056565645d1d980925c Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 22 Apr 2024 00:45:16 +0200 Subject: [PATCH 0775/1432] Document dev commands --- src/dev.rs | 5 +++++ src/main.rs | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index d7f9af6502..38338cd0a4 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -11,12 +11,17 @@ mod update; #[derive(Subcommand)] pub enum DevCommands { + /// Create a new project for third-party Rustlings exercises New { + /// The path to create the project in path: PathBuf, + /// Don't initialize a Git repository in the project directory #[arg(long)] no_git: bool, }, + /// Run checks on the exercises Check, + /// Update the `Cargo.toml` file for the exercises Update, } diff --git a/src/main.rs b/src/main.rs index f52699c7fa..9ff218a356 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,7 +54,7 @@ struct Args { enum Subcommands { /// Initialize Rustlings Init, - /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified. + /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified Run { /// The name of the exercise name: Option, @@ -64,11 +64,12 @@ enum Subcommands { /// The name of the exercise name: String, }, - /// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified. + /// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified Hint { /// The name of the exercise name: Option, }, + /// Commands for developing (third-party) Rustlings exercises #[command(subcommand)] Dev(DevCommands), } From ad8e5444837b5c2b06497b9b592fbbb8c2db057e Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 22 Apr 2024 01:07:36 +0200 Subject: [PATCH 0776/1432] Move quizzes --- README.md | 2 +- exercises/{ => quizzes}/quiz1.rs | 0 exercises/{ => quizzes}/quiz2.rs | 0 exercises/{ => quizzes}/quiz3.rs | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename exercises/{ => quizzes}/quiz1.rs (100%) rename exercises/{ => quizzes}/quiz2.rs (100%) rename exercises/{ => quizzes}/quiz3.rs (100%) diff --git a/README.md b/README.md index bfa3f06f8f..84125a0191 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ rustlings hint ## Quizzes After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. -These quizzes are found in `exercises/quizN.rs`. +These quizzes are found in `exercises/quizzes`. ## Continuing On diff --git a/exercises/quiz1.rs b/exercises/quizzes/quiz1.rs similarity index 100% rename from exercises/quiz1.rs rename to exercises/quizzes/quiz1.rs diff --git a/exercises/quiz2.rs b/exercises/quizzes/quiz2.rs similarity index 100% rename from exercises/quiz2.rs rename to exercises/quizzes/quiz2.rs diff --git a/exercises/quiz3.rs b/exercises/quizzes/quiz3.rs similarity index 100% rename from exercises/quiz3.rs rename to exercises/quizzes/quiz3.rs From 5349f0e8d4ea115861d7ba4d9f1f54a9b096a055 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 23 Apr 2024 15:32:01 +0200 Subject: [PATCH 0777/1432] Add README to the quizzes directory --- README.md | 5 ----- exercises/quizzes/README.md | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 exercises/quizzes/README.md diff --git a/README.md b/README.md index 84125a0191..2164269f45 100644 --- a/README.md +++ b/README.md @@ -90,11 +90,6 @@ You can also get the hint for the next pending exercise with the following comma rustlings hint ``` -## Quizzes - -After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. -These quizzes are found in `exercises/quizzes`. - ## Continuing On diff --git a/exercises/quizzes/README.md b/exercises/quizzes/README.md new file mode 100644 index 0000000000..4d3bcd9450 --- /dev/null +++ b/exercises/quizzes/README.md @@ -0,0 +1,3 @@ +# Quizzes + +After every couple of sections, there will be a quiz in this directory that'll test your knowledge on a bunch of sections at once. From e5a19a4c33e16e517e4d597acb721ed281c7bdae Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 23 Apr 2024 15:32:07 +0200 Subject: [PATCH 0778/1432] Update deps --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdc79365d5..1618eec1b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,9 +656,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", From 2dac8e509bed07c30a98983cfb6b80f73a1582e9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 23 Apr 2024 19:18:25 +0200 Subject: [PATCH 0779/1432] Refactor embedded files to add solutions --- Cargo.lock | 2 + Cargo.toml | 8 +- info.toml | 3 + rustlings-macros/Cargo.toml | 2 + rustlings-macros/src/lib.rs | 99 +++++----------- solutions/00_intro/intro1.rs | 1 + solutions/00_intro/intro2.rs | 1 + solutions/01_variables/variables1.rs | 1 + solutions/01_variables/variables2.rs | 1 + solutions/01_variables/variables3.rs | 1 + solutions/01_variables/variables4.rs | 1 + solutions/01_variables/variables5.rs | 1 + solutions/01_variables/variables6.rs | 1 + solutions/02_functions/functions1.rs | 1 + solutions/02_functions/functions2.rs | 1 + solutions/02_functions/functions3.rs | 1 + solutions/02_functions/functions4.rs | 1 + solutions/02_functions/functions5.rs | 1 + solutions/03_if/if1.rs | 1 + solutions/03_if/if2.rs | 1 + solutions/03_if/if3.rs | 1 + .../04_primitive_types/primitive_types1.rs | 1 + .../04_primitive_types/primitive_types2.rs | 1 + .../04_primitive_types/primitive_types3.rs | 1 + .../04_primitive_types/primitive_types4.rs | 1 + .../04_primitive_types/primitive_types5.rs | 1 + .../04_primitive_types/primitive_types6.rs | 1 + solutions/05_vecs/vecs1.rs | 1 + solutions/05_vecs/vecs2.rs | 1 + .../06_move_semantics/move_semantics1.rs | 1 + .../06_move_semantics/move_semantics2.rs | 1 + .../06_move_semantics/move_semantics3.rs | 1 + .../06_move_semantics/move_semantics4.rs | 1 + .../06_move_semantics/move_semantics5.rs | 1 + .../06_move_semantics/move_semantics6.rs | 1 + solutions/07_structs/structs1.rs | 1 + solutions/07_structs/structs2.rs | 1 + solutions/07_structs/structs3.rs | 1 + solutions/08_enums/enums1.rs | 1 + solutions/08_enums/enums2.rs | 1 + solutions/08_enums/enums3.rs | 1 + solutions/09_strings/strings1.rs | 1 + solutions/09_strings/strings2.rs | 1 + solutions/09_strings/strings3.rs | 1 + solutions/09_strings/strings4.rs | 1 + solutions/10_modules/modules1.rs | 1 + solutions/10_modules/modules2.rs | 1 + solutions/10_modules/modules3.rs | 1 + solutions/11_hashmaps/hashmaps1.rs | 1 + solutions/11_hashmaps/hashmaps2.rs | 1 + solutions/11_hashmaps/hashmaps3.rs | 1 + solutions/12_options/options1.rs | 1 + solutions/12_options/options2.rs | 1 + solutions/12_options/options3.rs | 1 + solutions/13_error_handling/errors1.rs | 1 + solutions/13_error_handling/errors2.rs | 1 + solutions/13_error_handling/errors3.rs | 1 + solutions/13_error_handling/errors4.rs | 1 + solutions/13_error_handling/errors5.rs | 1 + solutions/13_error_handling/errors6.rs | 1 + solutions/14_generics/generics1.rs | 1 + solutions/14_generics/generics2.rs | 1 + solutions/15_traits/traits1.rs | 1 + solutions/15_traits/traits2.rs | 1 + solutions/15_traits/traits3.rs | 1 + solutions/15_traits/traits4.rs | 1 + solutions/15_traits/traits5.rs | 1 + solutions/16_lifetimes/lifetimes1.rs | 1 + solutions/16_lifetimes/lifetimes2.rs | 1 + solutions/16_lifetimes/lifetimes3.rs | 1 + solutions/17_tests/tests1.rs | 1 + solutions/17_tests/tests2.rs | 1 + solutions/17_tests/tests3.rs | 1 + solutions/17_tests/tests4.rs | 1 + solutions/18_iterators/iterators1.rs | 1 + solutions/18_iterators/iterators2.rs | 1 + solutions/18_iterators/iterators3.rs | 1 + solutions/18_iterators/iterators4.rs | 1 + solutions/18_iterators/iterators5.rs | 1 + solutions/19_smart_pointers/arc1.rs | 1 + solutions/19_smart_pointers/box1.rs | 1 + solutions/19_smart_pointers/cow1.rs | 1 + solutions/19_smart_pointers/rc1.rs | 1 + solutions/20_threads/threads1.rs | 1 + solutions/20_threads/threads2.rs | 1 + solutions/20_threads/threads3.rs | 1 + solutions/21_macros/macros1.rs | 1 + solutions/21_macros/macros2.rs | 1 + solutions/21_macros/macros3.rs | 1 + solutions/21_macros/macros4.rs | 1 + solutions/22_clippy/clippy1.rs | 1 + solutions/22_clippy/clippy2.rs | 1 + solutions/22_clippy/clippy3.rs | 1 + solutions/23_conversions/as_ref_mut.rs | 1 + solutions/23_conversions/from_into.rs | 1 + solutions/23_conversions/from_str.rs | 1 + solutions/23_conversions/try_from_into.rs | 1 + solutions/23_conversions/using_as.rs | 1 + solutions/quizzes/quiz1.rs | 1 + solutions/quizzes/quiz2.rs | 1 + solutions/quizzes/quiz3.rs | 1 + src/app_state.rs | 33 +++--- src/embedded.rs | 110 +++++++----------- src/exercise.rs | 1 + src/init.rs | 4 +- 105 files changed, 199 insertions(+), 159 deletions(-) create mode 100644 solutions/00_intro/intro1.rs create mode 100644 solutions/00_intro/intro2.rs create mode 100644 solutions/01_variables/variables1.rs create mode 100644 solutions/01_variables/variables2.rs create mode 100644 solutions/01_variables/variables3.rs create mode 100644 solutions/01_variables/variables4.rs create mode 100644 solutions/01_variables/variables5.rs create mode 100644 solutions/01_variables/variables6.rs create mode 100644 solutions/02_functions/functions1.rs create mode 100644 solutions/02_functions/functions2.rs create mode 100644 solutions/02_functions/functions3.rs create mode 100644 solutions/02_functions/functions4.rs create mode 100644 solutions/02_functions/functions5.rs create mode 100644 solutions/03_if/if1.rs create mode 100644 solutions/03_if/if2.rs create mode 100644 solutions/03_if/if3.rs create mode 100644 solutions/04_primitive_types/primitive_types1.rs create mode 100644 solutions/04_primitive_types/primitive_types2.rs create mode 100644 solutions/04_primitive_types/primitive_types3.rs create mode 100644 solutions/04_primitive_types/primitive_types4.rs create mode 100644 solutions/04_primitive_types/primitive_types5.rs create mode 100644 solutions/04_primitive_types/primitive_types6.rs create mode 100644 solutions/05_vecs/vecs1.rs create mode 100644 solutions/05_vecs/vecs2.rs create mode 100644 solutions/06_move_semantics/move_semantics1.rs create mode 100644 solutions/06_move_semantics/move_semantics2.rs create mode 100644 solutions/06_move_semantics/move_semantics3.rs create mode 100644 solutions/06_move_semantics/move_semantics4.rs create mode 100644 solutions/06_move_semantics/move_semantics5.rs create mode 100644 solutions/06_move_semantics/move_semantics6.rs create mode 100644 solutions/07_structs/structs1.rs create mode 100644 solutions/07_structs/structs2.rs create mode 100644 solutions/07_structs/structs3.rs create mode 100644 solutions/08_enums/enums1.rs create mode 100644 solutions/08_enums/enums2.rs create mode 100644 solutions/08_enums/enums3.rs create mode 100644 solutions/09_strings/strings1.rs create mode 100644 solutions/09_strings/strings2.rs create mode 100644 solutions/09_strings/strings3.rs create mode 100644 solutions/09_strings/strings4.rs create mode 100644 solutions/10_modules/modules1.rs create mode 100644 solutions/10_modules/modules2.rs create mode 100644 solutions/10_modules/modules3.rs create mode 100644 solutions/11_hashmaps/hashmaps1.rs create mode 100644 solutions/11_hashmaps/hashmaps2.rs create mode 100644 solutions/11_hashmaps/hashmaps3.rs create mode 100644 solutions/12_options/options1.rs create mode 100644 solutions/12_options/options2.rs create mode 100644 solutions/12_options/options3.rs create mode 100644 solutions/13_error_handling/errors1.rs create mode 100644 solutions/13_error_handling/errors2.rs create mode 100644 solutions/13_error_handling/errors3.rs create mode 100644 solutions/13_error_handling/errors4.rs create mode 100644 solutions/13_error_handling/errors5.rs create mode 100644 solutions/13_error_handling/errors6.rs create mode 100644 solutions/14_generics/generics1.rs create mode 100644 solutions/14_generics/generics2.rs create mode 100644 solutions/15_traits/traits1.rs create mode 100644 solutions/15_traits/traits2.rs create mode 100644 solutions/15_traits/traits3.rs create mode 100644 solutions/15_traits/traits4.rs create mode 100644 solutions/15_traits/traits5.rs create mode 100644 solutions/16_lifetimes/lifetimes1.rs create mode 100644 solutions/16_lifetimes/lifetimes2.rs create mode 100644 solutions/16_lifetimes/lifetimes3.rs create mode 100644 solutions/17_tests/tests1.rs create mode 100644 solutions/17_tests/tests2.rs create mode 100644 solutions/17_tests/tests3.rs create mode 100644 solutions/17_tests/tests4.rs create mode 100644 solutions/18_iterators/iterators1.rs create mode 100644 solutions/18_iterators/iterators2.rs create mode 100644 solutions/18_iterators/iterators3.rs create mode 100644 solutions/18_iterators/iterators4.rs create mode 100644 solutions/18_iterators/iterators5.rs create mode 100644 solutions/19_smart_pointers/arc1.rs create mode 100644 solutions/19_smart_pointers/box1.rs create mode 100644 solutions/19_smart_pointers/cow1.rs create mode 100644 solutions/19_smart_pointers/rc1.rs create mode 100644 solutions/20_threads/threads1.rs create mode 100644 solutions/20_threads/threads2.rs create mode 100644 solutions/20_threads/threads3.rs create mode 100644 solutions/21_macros/macros1.rs create mode 100644 solutions/21_macros/macros2.rs create mode 100644 solutions/21_macros/macros3.rs create mode 100644 solutions/21_macros/macros4.rs create mode 100644 solutions/22_clippy/clippy1.rs create mode 100644 solutions/22_clippy/clippy2.rs create mode 100644 solutions/22_clippy/clippy3.rs create mode 100644 solutions/23_conversions/as_ref_mut.rs create mode 100644 solutions/23_conversions/from_into.rs create mode 100644 solutions/23_conversions/from_str.rs create mode 100644 solutions/23_conversions/try_from_into.rs create mode 100644 solutions/23_conversions/using_as.rs create mode 100644 solutions/quizzes/quiz1.rs create mode 100644 solutions/quizzes/quiz2.rs create mode 100644 solutions/quizzes/quiz3.rs diff --git a/Cargo.lock b/Cargo.lock index 1618eec1b1..93617fabfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,6 +690,8 @@ name = "rustlings-macros" version = "6.0.0-alpha.0" dependencies = [ "quote", + "serde", + "toml_edit", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6181b1e9fd..1e6a24ca3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,10 @@ authors = [ license = "MIT" edition = "2021" +[workspace.dependencies] +serde = { version = "1.0.198", features = ["derive"] } +toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } + [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" @@ -41,8 +45,8 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" 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.12", default-features = false, features = ["parse", "serde"] } +serde.workspace = true +toml_edit.workspace = true which = "6.0.1" [dev-dependencies] diff --git a/info.toml b/info.toml index 27071a5cb6..6549e1c450 100644 --- a/info.toml +++ b/info.toml @@ -236,6 +236,7 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" +dir = "quizzes" mode = "test" hint = "No hints this time ;)" @@ -637,6 +638,7 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" +dir = "quizzes" mode = "test" hint = "No hints this time ;)" @@ -870,6 +872,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" +dir = "quizzes" mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 095deaa3a1..f925f692cc 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -11,3 +11,5 @@ proc-macro = true [dependencies] quote = "1.0.36" +serde.workspace = true +toml_edit.workspace = true diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d95a93a63c..0bf3dbdffb 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -1,86 +1,39 @@ use proc_macro::TokenStream; use quote::quote; -use std::{fs::read_dir, panic, path::PathBuf}; +use serde::Deserialize; -fn path_to_string(path: PathBuf) -> String { - path.into_os_string() - .into_string() - .unwrap_or_else(|original| { - panic!("The path {} is invalid UTF8", original.to_string_lossy()); - }) +#[derive(Deserialize)] +struct ExerciseInfo { + name: String, + dir: String, +} + +#[derive(Deserialize)] +struct InfoFile { + exercises: Vec, } #[proc_macro] 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"); - - if entry.file_type().unwrap().is_file() { - let path = entry.path(); - if path.file_name().unwrap() != "README.md" { - files.push(path_to_string(path)); - } - - continue; - } - - let dir_path = entry.path(); - let dir_files = read_dir(&dir_path).unwrap_or_else(|e| { - panic!("Failed to open the directory {}: {e}", dir_path.display()); - }); - let dir_path = path_to_string(dir_path); - let dir_files = dir_files.filter_map(|entry| { - let entry = entry.unwrap_or_else(|e| { - panic!("Failed to read the directory {dir_path}: {e}"); - }); - let path = entry.path(); - - if !entry.file_type().unwrap().is_file() { - panic!("Found {} but expected only files", path.display()); - } - - if path.file_name().unwrap() == "README.md" { - return None; - } - - Some(path_to_string(path)) - }); - - dirs.push(quote! { - EmbeddedFlatDir { - path: #dir_path, - readme: EmbeddedFile { - path: ::std::concat!(#dir_path, "/README.md"), - content: ::std::include_bytes!(::std::concat!("../", #dir_path, "/README.md")), - }, - content: &[ - #(EmbeddedFile { - path: #dir_files, - content: ::std::include_bytes!(::std::concat!("../", #dir_files)), - }),* - ], - } - }); - } + let exercises = toml_edit::de::from_str::(include_str!("../../info.toml")) + .expect("Failed to parse `info.toml`") + .exercises; + + let exercise_files = exercises + .iter() + .map(|exercise| format!("../exercises/{}/{}.rs", exercise.dir, exercise.name)); + let solution_files = exercises + .iter() + .map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name)); + let dirs = exercises.iter().map(|exercise| &exercise.dir); + let readmes = exercises + .iter() + .map(|exercise| format!("../exercises/{}/README.md", exercise.dir)); quote! { EmbeddedFiles { - exercises_dir: ExercisesDir { - readme: EmbeddedFile { - path: "exercises/README.md", - content: ::std::include_bytes!("../exercises/README.md"), - }, - files: &[#( - EmbeddedFile { - path: #files, - content: ::std::include_bytes!(::std::concat!("../", #files)), - } - ),*], - dirs: &[#(#dirs),*], - }, + exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files) }),*], + exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*] } } .into() diff --git a/solutions/00_intro/intro1.rs b/solutions/00_intro/intro1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/00_intro/intro1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/00_intro/intro2.rs b/solutions/00_intro/intro2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/00_intro/intro2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables1.rs b/solutions/01_variables/variables1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/01_variables/variables1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables2.rs b/solutions/01_variables/variables2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/01_variables/variables2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/01_variables/variables3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/01_variables/variables4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/01_variables/variables5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables6.rs b/solutions/01_variables/variables6.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/01_variables/variables6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions1.rs b/solutions/02_functions/functions1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/02_functions/functions1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions2.rs b/solutions/02_functions/functions2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/02_functions/functions2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions3.rs b/solutions/02_functions/functions3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/02_functions/functions3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions4.rs b/solutions/02_functions/functions4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/02_functions/functions4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions5.rs b/solutions/02_functions/functions5.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/02_functions/functions5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if1.rs b/solutions/03_if/if1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/03_if/if1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if2.rs b/solutions/03_if/if2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/03_if/if2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if3.rs b/solutions/03_if/if3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/03_if/if3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types1.rs b/solutions/04_primitive_types/primitive_types1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/04_primitive_types/primitive_types1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types2.rs b/solutions/04_primitive_types/primitive_types2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/04_primitive_types/primitive_types2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types3.rs b/solutions/04_primitive_types/primitive_types3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/04_primitive_types/primitive_types3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types4.rs b/solutions/04_primitive_types/primitive_types4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/04_primitive_types/primitive_types4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types5.rs b/solutions/04_primitive_types/primitive_types5.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/04_primitive_types/primitive_types5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types6.rs b/solutions/04_primitive_types/primitive_types6.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/04_primitive_types/primitive_types6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/05_vecs/vecs1.rs b/solutions/05_vecs/vecs1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/05_vecs/vecs1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/05_vecs/vecs2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics1.rs b/solutions/06_move_semantics/move_semantics1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/06_move_semantics/move_semantics1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics2.rs b/solutions/06_move_semantics/move_semantics2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/06_move_semantics/move_semantics2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics3.rs b/solutions/06_move_semantics/move_semantics3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/06_move_semantics/move_semantics3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics6.rs b/solutions/06_move_semantics/move_semantics6.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/06_move_semantics/move_semantics6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs1.rs b/solutions/07_structs/structs1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/07_structs/structs1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs2.rs b/solutions/07_structs/structs2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/07_structs/structs2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs3.rs b/solutions/07_structs/structs3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/07_structs/structs3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums1.rs b/solutions/08_enums/enums1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/08_enums/enums1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/08_enums/enums2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/08_enums/enums3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings1.rs b/solutions/09_strings/strings1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/09_strings/strings1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings2.rs b/solutions/09_strings/strings2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/09_strings/strings2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings3.rs b/solutions/09_strings/strings3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/09_strings/strings3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/09_strings/strings4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules1.rs b/solutions/10_modules/modules1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/10_modules/modules1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/10_modules/modules2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules3.rs b/solutions/10_modules/modules3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/10_modules/modules3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps1.rs b/solutions/11_hashmaps/hashmaps1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/11_hashmaps/hashmaps1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/11_hashmaps/hashmaps2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/12_options/options1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options2.rs b/solutions/12_options/options2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/12_options/options2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options3.rs b/solutions/12_options/options3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/12_options/options3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors1.rs b/solutions/13_error_handling/errors1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/13_error_handling/errors1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/13_error_handling/errors2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors3.rs b/solutions/13_error_handling/errors3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/13_error_handling/errors3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/13_error_handling/errors4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors5.rs b/solutions/13_error_handling/errors5.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/13_error_handling/errors5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/13_error_handling/errors6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/14_generics/generics1.rs b/solutions/14_generics/generics1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/14_generics/generics1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/14_generics/generics2.rs b/solutions/14_generics/generics2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/14_generics/generics2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits1.rs b/solutions/15_traits/traits1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/15_traits/traits1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits2.rs b/solutions/15_traits/traits2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/15_traits/traits2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/15_traits/traits3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/15_traits/traits4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits5.rs b/solutions/15_traits/traits5.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/15_traits/traits5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes3.rs b/solutions/16_lifetimes/lifetimes3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/16_lifetimes/lifetimes3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests1.rs b/solutions/17_tests/tests1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/17_tests/tests1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests2.rs b/solutions/17_tests/tests2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/17_tests/tests2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/17_tests/tests3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests4.rs b/solutions/17_tests/tests4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/17_tests/tests4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators1.rs b/solutions/18_iterators/iterators1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/18_iterators/iterators1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators2.rs b/solutions/18_iterators/iterators2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/18_iterators/iterators2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/18_iterators/iterators3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/18_iterators/iterators4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/18_iterators/iterators5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/arc1.rs b/solutions/19_smart_pointers/arc1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/19_smart_pointers/arc1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/box1.rs b/solutions/19_smart_pointers/box1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/19_smart_pointers/box1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/cow1.rs b/solutions/19_smart_pointers/cow1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/19_smart_pointers/cow1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/19_smart_pointers/rc1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads1.rs b/solutions/20_threads/threads1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/20_threads/threads1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads2.rs b/solutions/20_threads/threads2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/20_threads/threads2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads3.rs b/solutions/20_threads/threads3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/20_threads/threads3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros1.rs b/solutions/21_macros/macros1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/21_macros/macros1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros2.rs b/solutions/21_macros/macros2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/21_macros/macros2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros3.rs b/solutions/21_macros/macros3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/21_macros/macros3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros4.rs b/solutions/21_macros/macros4.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/21_macros/macros4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy1.rs b/solutions/22_clippy/clippy1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/22_clippy/clippy1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy2.rs b/solutions/22_clippy/clippy2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/22_clippy/clippy2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/22_clippy/clippy3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/as_ref_mut.rs b/solutions/23_conversions/as_ref_mut.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/23_conversions/as_ref_mut.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/from_into.rs b/solutions/23_conversions/from_into.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/23_conversions/from_into.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/23_conversions/from_str.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/try_from_into.rs b/solutions/23_conversions/try_from_into.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/23_conversions/try_from_into.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/using_as.rs b/solutions/23_conversions/using_as.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/23_conversions/using_as.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz1.rs b/solutions/quizzes/quiz1.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/quizzes/quiz1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/quizzes/quiz2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs new file mode 100644 index 0000000000..70b786d12e --- /dev/null +++ b/solutions/quizzes/quiz3.rs @@ -0,0 +1 @@ +// TODO diff --git a/src/app_state.rs b/src/app_state.rs index 09de2a3e84..0767c2b2ca 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,11 +11,7 @@ use std::{ process::{Command, Stdio}, }; -use crate::{ - embedded::{WriteStrategy, EMBEDDED_FILES}, - exercise::Exercise, - info_file::ExerciseInfo, -}; +use crate::{embedded::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"; @@ -100,10 +96,15 @@ impl AppState { exercise_info.name.shrink_to_fit(); let name = exercise_info.name.leak(); + let dir = exercise_info.dir.map(|mut dir| { + dir.shrink_to_fit(); + &*dir.leak() + }); let hint = exercise_info.hint.trim().to_owned(); Exercise { + dir, name, path, mode: exercise_info.mode, @@ -181,10 +182,16 @@ impl AppState { Ok(()) } - fn reset_path(&self, path: &str) -> Result<()> { + fn reset(&self, ind: usize, dir_name: Option<&str>, path: &str) -> Result<()> { if self.official_exercises { return EMBEDDED_FILES - .write_exercise_to_disk(path, WriteStrategy::Overwrite) + .write_exercise_to_disk( + ind, + dir_name.context( + "Official exercises must be nested in the `exercises` directory", + )?, + path, + ) .with_context(|| format!("Failed to reset the exercise {path}")); } @@ -209,11 +216,11 @@ impl AppState { } 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)?; + let exercise = self.current_exercise(); + self.reset(self.current_exercise_ind, exercise.dir, exercise.path)?; - Ok(path) + Ok(exercise.path) } pub fn reset_exercise_by_ind(&mut self, exercise_ind: usize) -> Result<&'static str> { @@ -221,11 +228,11 @@ impl AppState { bail!(BAD_INDEX_ERR); } - let path = self.exercises[exercise_ind].path; self.set_pending(exercise_ind)?; - self.reset_path(path)?; + let exercise = &self.exercises[exercise_ind]; + self.reset(exercise_ind, exercise.dir, exercise.path)?; - Ok(path) + Ok(exercise.path) } fn next_pending_exercise_ind(&self) -> Option { diff --git a/src/embedded.rs b/src/embedded.rs index eae3099800..ccd8dca475 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,9 +1,10 @@ use std::{ - fs::{create_dir, File, OpenOptions}, + fs::{create_dir, OpenOptions}, io::{self, Write}, - path::Path, }; +use crate::info_file::ExerciseInfo; + pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); #[derive(Clone, Copy)] @@ -13,107 +14,78 @@ pub enum WriteStrategy { } impl WriteStrategy { - fn open>(self, path: P) -> io::Result { - match self { + fn write(self, path: &str, content: &[u8]) -> io::Result<()> { + let file = match self { Self::IfNotExists => OpenOptions::new().create_new(true).write(true).open(path), Self::Overwrite => OpenOptions::new() .create(true) .write(true) .truncate(true) .open(path), - } + }; + + file?.write_all(content) } } -struct EmbeddedFile { - path: &'static str, - content: &'static [u8], +struct ExerciseFiles { + exercise: &'static [u8], + solution: &'static [u8], } -impl EmbeddedFile { - fn write_to_disk(&self, strategy: WriteStrategy) -> io::Result<()> { - strategy.open(self.path)?.write_all(self.content) - } +struct ExerciseDir { + name: &'static str, + readme: &'static [u8], } -struct EmbeddedFlatDir { - path: &'static str, - readme: EmbeddedFile, - content: &'static [EmbeddedFile], -} - -impl EmbeddedFlatDir { +impl ExerciseDir { fn init_on_disk(&self) -> io::Result<()> { - let path = Path::new(self.path); - - if let Err(e) = create_dir(path) { - if e.kind() != io::ErrorKind::AlreadyExists { - return Err(e); + if let Err(e) = create_dir(format!("exercises/{}", self.name)) { + if e.kind() == io::ErrorKind::AlreadyExists { + return Ok(()); } + + return Err(e); } - self.readme.write_to_disk(WriteStrategy::Overwrite) + WriteStrategy::Overwrite.write(&format!("exercises/{}/README.md", self.name), self.readme) } } -struct ExercisesDir { - readme: EmbeddedFile, - files: &'static [EmbeddedFile], - dirs: &'static [EmbeddedFlatDir], -} - pub struct EmbeddedFiles { - exercises_dir: ExercisesDir, + exercise_files: &'static [ExerciseFiles], + exercise_dirs: &'static [ExerciseDir], } impl EmbeddedFiles { - pub fn init_exercises_dir(&self) -> io::Result<()> { + pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> io::Result<()> { create_dir("exercises")?; - self.exercises_dir - .readme - .write_to_disk(WriteStrategy::IfNotExists)?; - - for file in self.exercises_dir.files { - file.write_to_disk(WriteStrategy::IfNotExists)?; - } + WriteStrategy::IfNotExists.write( + "exercises/README.md", + include_bytes!("../exercises/README.md"), + )?; - for dir in self.exercises_dir.dirs { + for dir in self.exercise_dirs { dir.init_on_disk()?; + } - for file in dir.content { - file.write_to_disk(WriteStrategy::IfNotExists)?; - } + for (exercise_info, exercise_files) in exercise_infos.iter().zip(self.exercise_files) { + WriteStrategy::IfNotExists.write(&exercise_info.path(), exercise_files.exercise)?; } Ok(()) } - pub fn write_exercise_to_disk

(&self, path: P, strategy: WriteStrategy) -> io::Result<()> - where - P: AsRef, - { - let path = path.as_ref(); - - if let Some(file) = self - .exercises_dir - .files - .iter() - .find(|file| Path::new(file.path) == path) - { - return file.write_to_disk(strategy); - } - - for dir in self.exercises_dir.dirs { - if let Some(file) = dir.content.iter().find(|file| Path::new(file.path) == path) { - dir.init_on_disk()?; - return file.write_to_disk(strategy); - } - } + pub fn write_exercise_to_disk(&self, exercise_ind: usize, dir_name: &str, path: &str) -> io::Result<()> { + let Some(dir) = self.exercise_dirs.iter().find(|dir| dir.name == dir_name) else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("`{dir_name}` not found in the embedded directories"), + )); + }; - Err(io::Error::new( - io::ErrorKind::NotFound, - format!("{} not found in the embedded files", path.display()), - )) + dir.init_on_disk()?; + WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].exercise) } } diff --git a/src/exercise.rs b/src/exercise.rs index e85efe4cdd..4493a57576 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -31,6 +31,7 @@ impl<'a> Display for TerminalFileLink<'a> { } pub struct Exercise { + pub dir: Option<&'static str>, // Exercise's unique name pub name: &'static str, // Exercise's path diff --git a/src/init.rs b/src/init.rs index f210db77ab..f1a9509976 100644 --- a/src/init.rs +++ b/src/init.rs @@ -24,11 +24,11 @@ pub fn init() -> Result<()> { set_current_dir("rustlings") .context("Failed to change the current directory to `rustlings`")?; + let info_file = InfoFile::parse()?; EMBEDDED_FILES - .init_exercises_dir() + .init_exercises_dir(&info_file.exercises) .context("Failed to initialize the `rustlings/exercises` directory")?; - let info_file = InfoFile::parse()?; let current_cargo_toml = include_str!("../dev/Cargo.toml"); // Skip the first line (comment). let newline_ind = current_cargo_toml From b77007887c5e3e369a3dc7347c93c6b7293f8534 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 00:47:46 +0200 Subject: [PATCH 0780/1432] Write the solution file on done --- src/app_state.rs | 10 +++++++++ src/embedded.rs | 54 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 0767c2b2ca..152a67447d 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -264,6 +264,16 @@ impl AppState { self.n_done += 1; } + if self.official_exercises { + EMBEDDED_FILES.write_solution_to_disk( + self.current_exercise_ind, + exercise + .dir + .context("Official exercises must be nested in the `exercises` directory")?, + exercise.name, + )?; + } + let Some(ind) = self.next_pending_exercise_ind() else { writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; diff --git a/src/embedded.rs b/src/embedded.rs index ccd8dca475..2de3b1cf6a 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,5 +1,6 @@ +use anyhow::{bail, Context, Error, Result}; use std::{ - fs::{create_dir, OpenOptions}, + fs::{create_dir, create_dir_all, OpenOptions}, io::{self, Write}, }; @@ -14,7 +15,7 @@ pub enum WriteStrategy { } impl WriteStrategy { - fn write(self, path: &str, content: &[u8]) -> io::Result<()> { + fn write(self, path: &str, content: &[u8]) -> Result<()> { let file = match self { Self::IfNotExists => OpenOptions::new().create_new(true).write(true).open(path), Self::Overwrite => OpenOptions::new() @@ -24,7 +25,9 @@ impl WriteStrategy { .open(path), }; - file?.write_all(content) + file.context("Failed to open the file `{path}` in write mode")? + .write_all(content) + .context("Failed to write the file {path}") } } @@ -39,16 +42,22 @@ struct ExerciseDir { } impl ExerciseDir { - fn init_on_disk(&self) -> io::Result<()> { - if let Err(e) = create_dir(format!("exercises/{}", self.name)) { + fn init_on_disk(&self) -> Result<()> { + let dir_path = format!("exercises/{}", self.name); + if let Err(e) = create_dir(&dir_path) { if e.kind() == io::ErrorKind::AlreadyExists { return Ok(()); } - return Err(e); + return Err( + Error::from(e).context(format!("Failed to create the directory {dir_path}")) + ); } - WriteStrategy::Overwrite.write(&format!("exercises/{}/README.md", self.name), self.readme) + WriteStrategy::Overwrite + .write(&format!("exercises/{}/README.md", self.name), self.readme)?; + + Ok(()) } } @@ -58,8 +67,8 @@ pub struct EmbeddedFiles { } impl EmbeddedFiles { - pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> io::Result<()> { - create_dir("exercises")?; + pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> Result<()> { + create_dir("exercises").context("Failed to create the directory `exercises`")?; WriteStrategy::IfNotExists.write( "exercises/README.md", @@ -77,15 +86,32 @@ impl EmbeddedFiles { Ok(()) } - pub fn write_exercise_to_disk(&self, exercise_ind: usize, dir_name: &str, path: &str) -> io::Result<()> { + pub fn write_exercise_to_disk( + &self, + exercise_ind: usize, + dir_name: &str, + path: &str, + ) -> Result<()> { let Some(dir) = self.exercise_dirs.iter().find(|dir| dir.name == dir_name) else { - return Err(io::Error::new( - io::ErrorKind::NotFound, - format!("`{dir_name}` not found in the embedded directories"), - )); + bail!("`{dir_name}` not found in the embedded directories"); }; dir.init_on_disk()?; WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].exercise) } + + pub fn write_solution_to_disk( + &self, + exercise_ind: usize, + dir_name: &str, + exercise_name: &str, + ) -> Result<()> { + let dir_path = format!("solutions/{dir_name}"); + create_dir_all(&dir_path).context("Failed to create the directory {dir_path}")?; + + WriteStrategy::Overwrite.write( + &format!("{dir_path}/{exercise_name}.rs"), + self.exercise_files[exercise_ind].solution, + ) + } } From e4ee2cd548ded403dd540ab21f97093633a72da6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 00:48:58 +0200 Subject: [PATCH 0781/1432] Don't write solutions in debug mode --- src/app_state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 152a67447d..6f393bc598 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,7 +11,7 @@ use std::{ process::{Command, Stdio}, }; -use crate::{embedded::EMBEDDED_FILES, exercise::Exercise, info_file::ExerciseInfo}; +use crate::{embedded::EMBEDDED_FILES, exercise::Exercise, info_file::ExerciseInfo, DEBUG_PROFILE}; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -264,7 +264,7 @@ impl AppState { self.n_done += 1; } - if self.official_exercises { + if self.official_exercises && !DEBUG_PROFILE { EMBEDDED_FILES.write_solution_to_disk( self.current_exercise_ind, exercise From ef02c6c6ab93adf64353c13d6f036d1cd4187af0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 00:58:52 +0200 Subject: [PATCH 0782/1432] Use the embedded info.toml in debug mode --- src/info_file.rs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/info_file.rs b/src/info_file.rs index 879609e3a9..f344464b69 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -1,6 +1,8 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; -use std::fs; +use std::{fs, io::ErrorKind}; + +use crate::DEBUG_PROFILE; // The mode of the exercise. #[derive(Deserialize, Copy, Clone)] @@ -46,18 +48,27 @@ pub struct InfoFile { } impl InfoFile { + fn from_embedded() -> Result { + toml_edit::de::from_str(include_str!("../info.toml")) + .context("Failed to parse the embedded `info.toml` file") + } + pub fn parse() -> Result { + if DEBUG_PROFILE { + return Self::from_embedded(); + } + // Read a local `info.toml` if it exists. - let slf: Self = match fs::read_to_string("info.toml") { - Ok(file_content) => toml_edit::de::from_str(&file_content) + let slf = match fs::read_to_string("info.toml") { + Ok(file_content) => toml_edit::de::from_str::(&file_content) .context("Failed to parse the `info.toml` file")?, - Err(e) => match e.kind() { - std::io::ErrorKind::NotFound => { - toml_edit::de::from_str(include_str!("../info.toml")) - .context("Failed to parse the embedded `info.toml` file")? + Err(e) => { + if e.kind() == ErrorKind::NotFound { + return Self::from_embedded(); } - _ => return Err(Error::from(e).context("Failed to read the `info.toml` file")), - }, + + return Err(Error::from(e).context("Failed to read the `info.toml` file")); + } }; if slf.exercises.is_empty() { From edf57626129467dacc0c6f04b2ca00e64d5b2245 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 01:17:39 +0200 Subject: [PATCH 0783/1432] Preallocate path --- src/embedded.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index 2de3b1cf6a..756b41410b 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -43,7 +43,13 @@ struct ExerciseDir { impl ExerciseDir { fn init_on_disk(&self) -> Result<()> { - let dir_path = format!("exercises/{}", self.name); + let path_prefix = "exercises/"; + let readme_path_postfix = "/README.md"; + let mut dir_path = + String::with_capacity(path_prefix.len() + self.name.len() + readme_path_postfix.len()); + dir_path.push_str(path_prefix); + dir_path.push_str(self.name); + if let Err(e) = create_dir(&dir_path) { if e.kind() == io::ErrorKind::AlreadyExists { return Ok(()); @@ -54,8 +60,11 @@ impl ExerciseDir { ); } - WriteStrategy::Overwrite - .write(&format!("exercises/{}/README.md", self.name), self.readme)?; + let readme_path = { + dir_path.push_str(readme_path_postfix); + dir_path + }; + WriteStrategy::Overwrite.write(&readme_path, self.readme)?; Ok(()) } From 8a085a0a85c759029cd57c28364867bde817e738 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 02:52:30 +0200 Subject: [PATCH 0784/1432] Dump solution and show its path --- src/app_state.rs | 45 ++++++++++++++++++++++++++++++++++---------- src/embedded.rs | 10 ++++------ src/exercise.rs | 28 ++------------------------- src/main.rs | 1 + src/run.rs | 14 ++++++++++++-- src/terminal_link.rs | 23 ++++++++++++++++++++++ src/watch/state.rs | 35 ++++++++++++++++++++++++++-------- 7 files changed, 104 insertions(+), 52 deletions(-) create mode 100644 src/terminal_link.rs diff --git a/src/app_state.rs b/src/app_state.rs index 6f393bc598..33d3de2bc1 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -257,21 +257,46 @@ impl AppState { } } - pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { - let exercise = &mut self.exercises[self.current_exercise_ind]; - if !exercise.done { - exercise.done = true; - self.n_done += 1; + pub fn current_solution_path(&self) -> Result> { + if DEBUG_PROFILE { + return Ok(None); } - if self.official_exercises && !DEBUG_PROFILE { + let current_exercise = self.current_exercise(); + + if self.official_exercises { + let dir_name = current_exercise + .dir + .context("Official exercises must be nested in the `exercises` directory")?; + let solution_path = format!("solutions/{dir_name}/{}.rs", current_exercise.name); + EMBEDDED_FILES.write_solution_to_disk( self.current_exercise_ind, - exercise - .dir - .context("Official exercises must be nested in the `exercises` directory")?, - exercise.name, + dir_name, + &solution_path, )?; + + Ok(Some(solution_path)) + } else { + let solution_path = if let Some(dir) = current_exercise.dir { + format!("solutions/{dir}/{}.rs", current_exercise.name) + } else { + format!("solutions/{}.rs", current_exercise.name) + }; + + if Path::new(&solution_path).exists() { + return Ok(Some(solution_path)); + } + + Ok(None) + } + } + + pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { + let exercise = &mut self.exercises[self.current_exercise_ind]; + if !exercise.done { + exercise.done = true; + self.n_done += 1; } let Some(ind) = self.next_pending_exercise_ind() else { diff --git a/src/embedded.rs b/src/embedded.rs index 756b41410b..d7952a1f4f 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -113,14 +113,12 @@ impl EmbeddedFiles { &self, exercise_ind: usize, dir_name: &str, - exercise_name: &str, + path: &str, ) -> Result<()> { let dir_path = format!("solutions/{dir_name}"); - create_dir_all(&dir_path).context("Failed to create the directory {dir_path}")?; + create_dir_all(&dir_path) + .with_context(|| format!("Failed to create the directory {dir_path}"))?; - WriteStrategy::Overwrite.write( - &format!("{dir_path}/{exercise_name}.rs"), - self.exercise_files[exercise_ind].solution, - ) + WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].solution) } } diff --git a/src/exercise.rs b/src/exercise.rs index 4493a57576..45ac208614 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -2,33 +2,11 @@ use anyhow::{Context, Result}; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, - fs, path::Path, process::{Command, Output}, }; -use crate::{info_file::Mode, DEBUG_PROFILE}; - -pub struct TerminalFileLink<'a> { - path: &'a str, -} - -impl<'a> Display for TerminalFileLink<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Ok(Some(canonical_path)) = fs::canonicalize(self.path) - .as_deref() - .map(|path| path.to_str()) - { - write!( - f, - "\x1b]8;;file://{}\x1b\\{}\x1b]8;;\x1b\\", - canonical_path, self.path, - ) - } else { - write!(f, "{}", self.path,) - } - } -} +use crate::{info_file::Mode, terminal_link::TerminalFileLink, DEBUG_PROFILE}; pub struct Exercise { pub dir: Option<&'static str>, @@ -85,9 +63,7 @@ impl Exercise { } pub fn terminal_link(&self) -> StyledContent> { - style(TerminalFileLink { path: self.path }) - .underlined() - .blue() + style(TerminalFileLink(self.path)).underlined().blue() } } diff --git a/src/main.rs b/src/main.rs index 9ff218a356..790fff698d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ mod init; mod list; mod progress_bar; mod run; +mod terminal_link; mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; diff --git a/src/run.rs b/src/run.rs index 863b584efb..a2b6972025 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,8 +1,11 @@ use anyhow::{bail, Result}; -use crossterm::style::Stylize; +use crossterm::style::{style, Stylize}; use std::io::{self, Write}; -use crate::app_state::{AppState, ExercisesProgress}; +use crate::{ + app_state::{AppState, ExercisesProgress}, + terminal_link::TerminalFileLink, +}; pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); @@ -29,6 +32,13 @@ pub fn run(app_state: &mut AppState) -> Result<()> { exercise.path.green(), ))?; + if let Some(solution_path) = app_state.current_solution_path()? { + println!( + "\nA solution file can be found at {}\n", + style(TerminalFileLink(&solution_path)).underlined().green(), + ); + } + match app_state.done_current_exercise(&mut stdout)? { ExercisesProgress::AllDone => (), ExercisesProgress::Pending => println!( diff --git a/src/terminal_link.rs b/src/terminal_link.rs new file mode 100644 index 0000000000..c9e6bced12 --- /dev/null +++ b/src/terminal_link.rs @@ -0,0 +1,23 @@ +use std::{ + fmt::{self, Display, Formatter}, + fs, +}; + +pub struct TerminalFileLink<'a>(pub &'a str); + +impl<'a> Display for TerminalFileLink<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if let Ok(Some(canonical_path)) = fs::canonicalize(self.0) + .as_deref() + .map(|path| path.to_str()) + { + write!( + f, + "\x1b]8;;file://{}\x1b\\{}\x1b]8;;\x1b\\", + canonical_path, self.0, + ) + } else { + write!(f, "{}", self.0) + } + } +} diff --git a/src/watch/state.rs b/src/watch/state.rs index c0f6c532b6..5f4abf3c45 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,6 +1,6 @@ use anyhow::Result; use crossterm::{ - style::Stylize, + style::{style, Stylize}, terminal::{size, Clear, ClearType}, ExecutableCommand, }; @@ -9,15 +9,22 @@ use std::io::{self, StdoutLock, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, progress_bar::progress_bar, + terminal_link::TerminalFileLink, }; +enum DoneStatus { + DoneWithSolution(String), + DoneWithoutSolution, + Pending, +} + pub struct WatchState<'a> { writer: StdoutLock<'a>, app_state: &'a mut AppState, stdout: Option>, stderr: Option>, show_hint: bool, - show_done: bool, + done_status: DoneStatus, manual_run: bool, } @@ -31,7 +38,7 @@ impl<'a> WatchState<'a> { stdout: None, stderr: None, show_hint: false, - show_done: false, + done_status: DoneStatus::Pending, manual_run, } } @@ -49,13 +56,18 @@ impl<'a> WatchState<'a> { if output.status.success() { self.stderr = None; - self.show_done = true; + self.done_status = + if let Some(solution_path) = self.app_state.current_solution_path()? { + DoneStatus::DoneWithSolution(solution_path) + } else { + DoneStatus::DoneWithoutSolution + }; } else { self.app_state .set_pending(self.app_state.current_exercise_ind())?; self.stderr = Some(output.stderr); - self.show_done = false; + self.done_status = DoneStatus::Pending; } self.render() @@ -67,7 +79,7 @@ impl<'a> WatchState<'a> { } pub fn next_exercise(&mut self) -> Result { - if !self.show_done { + if matches!(self.done_status, DoneStatus::Pending) { self.writer .write_all(b"The current exercise isn't done yet\n")?; self.show_prompt()?; @@ -84,7 +96,7 @@ impl<'a> WatchState<'a> { self.writer.write_fmt(format_args!("{}un/", 'r'.bold()))?; } - if self.show_done { + if !matches!(self.done_status, DoneStatus::Pending) { self.writer.write_fmt(format_args!("{}ext/", 'n'.bold()))?; } @@ -124,7 +136,7 @@ impl<'a> WatchState<'a> { ))?; } - if self.show_done { + if !matches!(self.done_status, DoneStatus::Pending) { self.writer.write_fmt(format_args!( "{}\n\n", "Exercise done βœ“ @@ -134,6 +146,13 @@ When you are done experimenting, enter `n` or `next` to go to the next exercise ))?; } + if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { + self.writer.write_fmt(format_args!( + "A solution file can be found at {}\n\n", + style(TerminalFileLink(solution_path)).underlined().green() + ))?; + } + let line_width = size()?.0; let progress_bar = progress_bar( self.app_state.n_done(), From 71053101c32d0fd374f8e122880b3d682519bac6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 13:28:25 +0200 Subject: [PATCH 0785/1432] Add --locked --- README.md | 4 ++-- install.ps1 | 2 +- install.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6b9c98336d..821d76cd4d 100644 --- a/README.md +++ b/README.md @@ -74,13 +74,13 @@ If you get a permission denied message, you might have to exclude the directory ## Manually -Basically: Clone the repository at the latest tag, run `cargo install --path .`. +Basically: Clone the repository at the latest tag, run `cargo install --locked --path .`. ```bash # find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.6.1) git clone -b 5.6.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings -cargo install --force --path . +cargo install --locked --force --path . ``` If there are installation errors, ensure that your toolchain is up to date. For the latest, run: diff --git a/install.ps1 b/install.ps1 index 844b0134cd..8ab5b88eaf 100644 --- a/install.ps1 +++ b/install.ps1 @@ -78,7 +78,7 @@ Set-Location $path git checkout -q tags/$version Write-Host "Installing the 'rustlings' executable..." -cargo install --force --path . +cargo install --locked --force --path . if (!(Get-Command rustlings -ErrorAction SilentlyContinue)) { Write-Host "WARNING: Please check that you have '~/.cargo/bin' in your PATH environment variable!" } diff --git a/install.sh b/install.sh index fdbe8d4386..f6031cf8f9 100755 --- a/install.sh +++ b/install.sh @@ -165,7 +165,7 @@ echo "Checking out version $Version..." git checkout -q ${Version} echo "Installing the 'rustlings' executable..." -cargo install --force --path . +cargo install --locked --force --path . if ! [ -x "$(command -v rustlings)" ] then From 4ef345e706ab5150d96faadf41a28aee49534361 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 15:58:34 +0200 Subject: [PATCH 0786/1432] Update dependency --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93617fabfd..df9ee74366 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -954,11 +954,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] From 8ebd2f9df232dbe630510b1994c896871aa58208 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 16:15:14 +0200 Subject: [PATCH 0787/1432] Update Cargo.toml files --- Cargo.toml | 13 ++++++++++--- rustlings-macros/Cargo.toml | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e6a24ca3f..f4615b136d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,14 @@ exclude = [ ] [workspace.package] -version = "6.0.0-alpha.0" +version = "6.0.0-beta.0" authors = [ "Liv ", + "Mo Bitar ", + # Alumni "Carol (Nichols || Goulding) ", - "Mo ", ] +repository = "https://github.com/rust-lang/rustlings" license = "MIT" edition = "2021" @@ -27,8 +29,13 @@ description = "Small exercises to get you used to reading and writing Rust code! default-run = "rustlings" version.workspace = true authors.workspace = true +repository.workspace = true license.workspace = true edition.workspace = true +keywords = [ + "exercise", + "learning", +] include = [ "/exercises/", "/info.toml", @@ -44,7 +51,7 @@ crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } +rustlings-macros = { path = "rustlings-macros", version = "6.0.0-beta.0" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index f925f692cc..c9c1d2f7cf 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "rustlings-macros" -description = "A macros crate intended to be only used by rustlings" +description = "A macros crate intended to be used only by Rustlings" version.workspace = true authors.workspace = true +repository.workspace = true license.workspace = true edition.workspace = true From 0df0be835279a9dd176244b9014f459399153300 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 16:26:34 +0200 Subject: [PATCH 0788/1432] Update Cargo.lock --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df9ee74366..5f0b597870 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,7 +669,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.0" dependencies = [ "anyhow", "assert_cmd", @@ -687,7 +687,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.0" dependencies = [ "quote", "serde", From d8c2ab8349854cbc7f4a994c7413d266cc38bc24 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 16:26:48 +0200 Subject: [PATCH 0789/1432] Fix tests --- src/info_file.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/info_file.rs b/src/info_file.rs index f344464b69..6938cd03e1 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -2,8 +2,6 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; use std::{fs, io::ErrorKind}; -use crate::DEBUG_PROFILE; - // The mode of the exercise. #[derive(Deserialize, Copy, Clone)] #[serde(rename_all = "lowercase")] @@ -48,23 +46,15 @@ pub struct InfoFile { } impl InfoFile { - fn from_embedded() -> Result { - toml_edit::de::from_str(include_str!("../info.toml")) - .context("Failed to parse the embedded `info.toml` file") - } - pub fn parse() -> Result { - if DEBUG_PROFILE { - return Self::from_embedded(); - } - // Read a local `info.toml` if it exists. let slf = match fs::read_to_string("info.toml") { Ok(file_content) => toml_edit::de::from_str::(&file_content) .context("Failed to parse the `info.toml` file")?, Err(e) => { if e.kind() == ErrorKind::NotFound { - return Self::from_embedded(); + return toml_edit::de::from_str(include_str!("../info.toml")) + .context("Failed to parse the embedded `info.toml` file"); } return Err(Error::from(e).context("Failed to read the `info.toml` file")); From 67fa01774223b08833c21baeb13bdec9e4a298a0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 01:56:01 +0200 Subject: [PATCH 0790/1432] Use os_pipe --- Cargo.lock | 11 +++++ Cargo.toml | 1 + src/app_state.rs | 13 +++++- src/exercise.rs | 110 +++++++++++++++++++++++++++++++++++++++------ src/main.rs | 6 ++- src/run.rs | 11 +++-- src/watch/state.rs | 26 +++-------- 7 files changed, 135 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f0b597870..823fec36dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -519,6 +519,16 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -677,6 +687,7 @@ dependencies = [ "crossterm", "hashbrown", "notify-debouncer-mini", + "os_pipe", "predicates", "ratatui", "rustlings-macros", diff --git a/Cargo.toml b/Cargo.toml index f4615b136d..31e74568fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" +os_pipe = "1.1.5" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-beta.0" } serde.workspace = true diff --git a/src/app_state.rs b/src/app_state.rs index 33d3de2bc1..4160f6e5ac 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,7 +11,12 @@ use std::{ process::{Command, Stdio}, }; -use crate::{embedded::EMBEDDED_FILES, exercise::Exercise, info_file::ExerciseInfo, DEBUG_PROFILE}; +use crate::{ + embedded::EMBEDDED_FILES, + exercise::{Exercise, OUTPUT_CAPACITY}, + info_file::ExerciseInfo, + DEBUG_PROFILE, +}; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -302,11 +307,13 @@ impl AppState { let Some(ind) = self.next_pending_exercise_ind() else { writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); for (exercise_ind, exercise) in self.exercises().iter().enumerate() { writer.write_fmt(format_args!("Running {exercise} ... "))?; writer.flush()?; - if !exercise.run()?.status.success() { + let success = exercise.run(&mut output)?; + if !success { writer.write_fmt(format_args!("{}\n\n", "FAILED".red()))?; self.current_exercise_ind = exercise_ind; @@ -322,6 +329,8 @@ impl AppState { } writer.write_fmt(format_args!("{}\n", "ok".green()))?; + + output.clear(); } writer.execute(Clear(ClearType::All))?; diff --git a/src/exercise.rs b/src/exercise.rs index 45ac208614..17cc8d7791 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -2,11 +2,42 @@ use anyhow::{Context, Result}; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, - path::Path, - process::{Command, Output}, + io::{Read, Write}, + process::Command, }; -use crate::{info_file::Mode, terminal_link::TerminalFileLink, DEBUG_PROFILE}; +use crate::{in_official_repo, info_file::Mode, terminal_link::TerminalFileLink, DEBUG_PROFILE}; + +// TODO +pub const OUTPUT_CAPACITY: usize = 1 << 12; + +fn run_command(mut cmd: Command, cmd_description: &str, output: &mut Vec) -> Result { + let (mut reader, writer) = os_pipe::pipe().with_context(|| { + format!("Failed to create a pipe to run the command `{cmd_description}``") + })?; + + let mut handle = cmd + .stdout(writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{cmd_description}`") + })?) + .stderr(writer) + .spawn() + .with_context(|| format!("Failed to run the command `{cmd_description}`"))?; + + // Prevent pipe deadlock. + drop(cmd); + + reader + .read_to_end(output) + .with_context(|| format!("Failed to read the output of the command `{cmd_description}`"))?; + + output.push(b'\n'); + + handle + .wait() + .with_context(|| format!("Failed to wait on the command `{cmd_description}` to exit")) + .map(|status| status.success()) +} pub struct Exercise { pub dir: Option<&'static str>, @@ -22,13 +53,30 @@ pub struct Exercise { } impl Exercise { - fn cargo_cmd(&self, command: &str, args: &[&str]) -> Result { + fn run_bin(&self, output: &mut Vec) -> Result { + writeln!(output, "{}", "Output".bold().magenta().underlined())?; + + let bin_path = format!("target/debug/{}", self.name); + run_command(Command::new(&bin_path), &bin_path, output) + } + + fn cargo_cmd( + &self, + command: &str, + args: &[&str], + cmd_description: &str, + output: &mut Vec, + dev: bool, + ) -> Result { let mut cmd = Command::new("cargo"); cmd.arg(command); // A hack to make `cargo run` work when developing Rustlings. - if DEBUG_PROFILE && Path::new("tests").exists() { - cmd.arg("--manifest-path").arg("dev/Cargo.toml"); + if dev { + cmd.arg("--manifest-path") + .arg("dev/Cargo.toml") + .arg("--target-dir") + .arg("target"); } cmd.arg("--color") @@ -36,15 +84,43 @@ impl Exercise { .arg("-q") .arg("--bin") .arg(self.name) - .args(args) - .output() - .context("Failed to run Cargo") + .args(args); + + run_command(cmd, cmd_description, output) + } + + fn cargo_cmd_with_bin_output( + &self, + command: &str, + args: &[&str], + cmd_description: &str, + output: &mut Vec, + dev: bool, + ) -> Result { + // Discard the output of `cargo build` because it will be shown again by the Cargo command. + output.clear(); + + let cargo_cmd_success = self.cargo_cmd(command, args, cmd_description, output, dev)?; + + let run_success = self.run_bin(output)?; + + Ok(cargo_cmd_success && run_success) } - pub fn run(&self) -> Result { + pub fn run(&self, output: &mut Vec) -> Result { + output.clear(); + + // Developing the official Rustlings. + let dev = DEBUG_PROFILE && in_official_repo(); + + let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev)?; + if !build_success { + return Ok(false); + } + match self.mode { - Mode::Run => self.cargo_cmd("run", &[]), - Mode::Test => self.cargo_cmd( + Mode::Run => self.run_bin(output), + Mode::Test => self.cargo_cmd_with_bin_output( "test", &[ "--", @@ -54,10 +130,16 @@ impl Exercise { "--format", "pretty", ], + "cargo test …", + output, + dev, ), - Mode::Clippy => self.cargo_cmd( + Mode::Clippy => self.cargo_cmd_with_bin_output( "clippy", - &["--", "-D", "warnings", "-D", "clippy::float_cmp"], + &["--", "-D", "warnings"], + "cargo clippy …", + output, + dev, ), } } diff --git a/src/main.rs b/src/main.rs index 790fff698d..a928504400 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,10 +75,14 @@ enum Subcommands { Dev(DevCommands), } +fn in_official_repo() -> bool { + Path::new("dev/rustlings-repo.txt").exists() +} + fn main() -> Result<()> { let args = Args::parse(); - if !DEBUG_PROFILE && Path::new("dev/rustlings-repo.txt").exists() { + if !DEBUG_PROFILE && in_official_repo() { bail!("{OLD_METHOD_ERR}"); } diff --git a/src/run.rs b/src/run.rs index a2b6972025..1db8dcbb6e 100644 --- a/src/run.rs +++ b/src/run.rs @@ -4,20 +4,19 @@ use std::io::{self, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, + exercise::OUTPUT_CAPACITY, terminal_link::TerminalFileLink, }; pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); - let output = exercise.run()?; + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + let success = exercise.run(&mut output)?; let mut stdout = io::stdout().lock(); - stdout.write_all(&output.stdout)?; - stdout.write_all(b"\n")?; - stdout.write_all(&output.stderr)?; - stdout.flush()?; + stdout.write_all(&output)?; - if !output.status.success() { + if !success { app_state.set_pending(app_state.current_exercise_ind())?; bail!( diff --git a/src/watch/state.rs b/src/watch/state.rs index 5f4abf3c45..df492dc80f 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -8,6 +8,7 @@ use std::io::{self, StdoutLock, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, + exercise::OUTPUT_CAPACITY, progress_bar::progress_bar, terminal_link::TerminalFileLink, }; @@ -21,8 +22,7 @@ enum DoneStatus { pub struct WatchState<'a> { writer: StdoutLock<'a>, app_state: &'a mut AppState, - stdout: Option>, - stderr: Option>, + output: Vec, show_hint: bool, done_status: DoneStatus, manual_run: bool, @@ -35,8 +35,7 @@ impl<'a> WatchState<'a> { Self { writer, app_state, - stdout: None, - stderr: None, + output: Vec::with_capacity(OUTPUT_CAPACITY), show_hint: false, done_status: DoneStatus::Pending, manual_run, @@ -51,11 +50,8 @@ impl<'a> WatchState<'a> { pub fn run_current_exercise(&mut self) -> Result<()> { self.show_hint = false; - let output = self.app_state.current_exercise().run()?; - self.stdout = Some(output.stdout); - - if output.status.success() { - self.stderr = None; + let success = self.app_state.current_exercise().run(&mut self.output)?; + if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { DoneStatus::DoneWithSolution(solution_path) @@ -66,7 +62,6 @@ impl<'a> WatchState<'a> { self.app_state .set_pending(self.app_state.current_exercise_ind())?; - self.stderr = Some(output.stderr); self.done_status = DoneStatus::Pending; } @@ -116,16 +111,7 @@ impl<'a> WatchState<'a> { self.writer.execute(Clear(ClearType::All))?; - if let Some(stdout) = &self.stdout { - self.writer.write_all(stdout)?; - self.writer.write_all(b"\n")?; - } - - if let Some(stderr) = &self.stderr { - self.writer.write_all(stderr)?; - self.writer.write_all(b"\n")?; - } - + self.writer.write_all(&self.output)?; self.writer.write_all(b"\n")?; if self.show_hint { From f92d45fa685e308c009cdf09d341bda41fcf9c52 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 02:03:26 +0200 Subject: [PATCH 0791/1432] Use write macros instead of write_fmt --- src/app_state.rs | 6 +++--- src/list/state.rs | 3 +-- src/run.rs | 7 ++++--- src/watch/state.rs | 37 ++++++++++++++++++++----------------- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 4160f6e5ac..11ac8ee4c1 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -309,12 +309,12 @@ impl AppState { let mut output = Vec::with_capacity(OUTPUT_CAPACITY); for (exercise_ind, exercise) in self.exercises().iter().enumerate() { - writer.write_fmt(format_args!("Running {exercise} ... "))?; + write!(writer, "Running {exercise} ... ")?; writer.flush()?; let success = exercise.run(&mut output)?; if !success { - writer.write_fmt(format_args!("{}\n\n", "FAILED".red()))?; + writeln!(writer, "{}\n", "FAILED".red())?; self.current_exercise_ind = exercise_ind; @@ -328,7 +328,7 @@ impl AppState { return Ok(ExercisesProgress::Pending); } - writer.write_fmt(format_args!("{}\n", "ok".green()))?; + writeln!(writer, "{}", "ok".green())?; output.clear(); } diff --git a/src/list/state.rs b/src/list/state.rs index 0253bb97c2..19a77fe03a 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -231,8 +231,7 @@ impl<'a> UiState<'a> { .context("Invalid selection index")?; let exercise_path = self.app_state.reset_exercise_by_ind(ind)?; - self.message - .write_fmt(format_args!("The exercise {exercise_path} has been reset"))?; + write!(self.message, "The exercise {exercise_path} has been reset")?; Ok(self.with_updated_rows()) } diff --git a/src/run.rs b/src/run.rs index 1db8dcbb6e..cbc9ad708c 100644 --- a/src/run.rs +++ b/src/run.rs @@ -25,11 +25,12 @@ pub fn run(app_state: &mut AppState) -> Result<()> { ); } - stdout.write_fmt(format_args!( - "{}{}\n", + writeln!( + stdout, + "{}{}", "βœ“ Successfully ran ".green(), exercise.path.green(), - ))?; + )?; if let Some(solution_path) = app_state.current_solution_path()? { println!( diff --git a/src/watch/state.rs b/src/watch/state.rs index df492dc80f..40c01bfcb9 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -88,19 +88,18 @@ impl<'a> WatchState<'a> { self.writer.write_all(b"\n")?; if self.manual_run { - self.writer.write_fmt(format_args!("{}un/", 'r'.bold()))?; + write!(self.writer, "{}un/", 'r'.bold())?; } if !matches!(self.done_status, DoneStatus::Pending) { - self.writer.write_fmt(format_args!("{}ext/", 'n'.bold()))?; + write!(self.writer, "{}ext/", 'n'.bold())?; } if !self.show_hint { - self.writer.write_fmt(format_args!("{}int/", 'h'.bold()))?; + write!(self.writer, "{}int/", 'h'.bold())?; } - self.writer - .write_fmt(format_args!("{}ist/{}uit? ", 'l'.bold(), 'q'.bold()))?; + write!(self.writer, "{}ist/{}uit? ", 'l'.bold(), 'q'.bold())?; self.writer.flush() } @@ -115,28 +114,31 @@ impl<'a> WatchState<'a> { self.writer.write_all(b"\n")?; if self.show_hint { - self.writer.write_fmt(format_args!( - "{}\n{}\n\n", + writeln!( + self.writer, + "{}\n{}\n", "Hint".bold().cyan().underlined(), self.app_state.current_exercise().hint, - ))?; + )?; } if !matches!(self.done_status, DoneStatus::Pending) { - self.writer.write_fmt(format_args!( - "{}\n\n", + writeln!( + self.writer, + "{}\n", "Exercise done βœ“ When you are done experimenting, enter `n` or `next` to go to the next exercise πŸ¦€" .bold() .green(), - ))?; + )?; } if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { - self.writer.write_fmt(format_args!( - "A solution file can be found at {}\n\n", + writeln!( + self.writer, + "A solution file can be found at {}\n", style(TerminalFileLink(solution_path)).underlined().green() - ))?; + )?; } let line_width = size()?.0; @@ -145,10 +147,11 @@ When you are done experimenting, enter `n` or `next` to go to the next exercise self.app_state.exercises().len() as u16, line_width, )?; - self.writer.write_fmt(format_args!( - "{progress_bar}Current exercise: {}\n", + writeln!( + self.writer, + "{progress_bar}Current exercise: {}", self.app_state.current_exercise().terminal_link(), - ))?; + )?; self.show_prompt()?; From 2af0cd9ccef07edf27abf7046dbe15e32d1b476d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 03:25:45 +0200 Subject: [PATCH 0792/1432] Replace `mode` by `test` and `strict_clippy` --- info.toml | 143 +++++++++++--------------------- src/app_state.rs | 5 +- src/dev/new.rs | 13 ++- src/exercise.rs | 131 +++++++++++++++++------------ src/info_file.rs | 22 ++--- tests/fixture/failure/info.toml | 3 +- tests/fixture/state/info.toml | 5 +- tests/fixture/success/info.toml | 3 +- 8 files changed, 147 insertions(+), 178 deletions(-) diff --git a/info.toml b/info.toml index d5369d502b..14944728e5 100644 --- a/info.toml +++ b/info.toml @@ -39,7 +39,7 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md [[exercises]] name = "intro1" dir = "00_intro" -mode = "run" +test = false # TODO: Fix hint hint = """ Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file @@ -48,7 +48,7 @@ to move on to the next exercise.""" [[exercises]] name = "intro2" dir = "00_intro" -mode = "run" +test = false hint = """ The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" @@ -57,7 +57,7 @@ The compiler is informing us that we've got the name of the print macro wrong, a [[exercises]] name = "variables1" dir = "01_variables" -mode = "run" +test = false hint = """ The declaration in the first line in the main function is missing a keyword that is needed in Rust to create a new variable binding.""" @@ -65,7 +65,7 @@ that is needed in Rust to create a new variable binding.""" [[exercises]] name = "variables2" dir = "01_variables" -mode = "run" +test = false hint = """ The compiler message is saying that Rust cannot infer the type that the variable binding `x` has with what is given here. @@ -84,7 +84,7 @@ What if `x` is the same type as `10`? What if it's a different type?""" [[exercises]] name = "variables3" dir = "01_variables" -mode = "run" +test = false hint = """ Oops! In this exercise, we have a variable binding that we've created on in the first line in the `main` function, and we're trying to use it in the next line, @@ -98,7 +98,7 @@ programming language -- thankfully the Rust compiler has caught this for us!""" [[exercises]] name = "variables4" dir = "01_variables" -mode = "run" +test = false hint = """ In Rust, variable bindings are immutable by default. But here we're trying to reassign a different value to `x`! There's a keyword we can use to make @@ -107,7 +107,7 @@ a variable binding mutable instead.""" [[exercises]] name = "variables5" dir = "01_variables" -mode = "run" +test = false hint = """ In `variables4` we already learned how to make an immutable variable mutable using a special keyword. Unfortunately this doesn't help us much in this @@ -125,7 +125,7 @@ Try to solve this exercise afterwards using this technique.""" [[exercises]] name = "variables6" dir = "01_variables" -mode = "run" +test = false hint = """ We know about variables and mutability, but there is another important type of variable available: constants. @@ -145,7 +145,7 @@ https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants [[exercises]] name = "functions1" dir = "02_functions" -mode = "run" +test = false hint = """ This main function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. @@ -155,7 +155,7 @@ Sounds a lot like `main`, doesn't it?""" [[exercises]] name = "functions2" dir = "02_functions" -mode = "run" +test = false hint = """ Rust requires that all parts of a function's signature have type annotations, but `call_me` is missing the type annotation of `num`.""" @@ -163,7 +163,7 @@ but `call_me` is missing the type annotation of `num`.""" [[exercises]] name = "functions3" dir = "02_functions" -mode = "run" +test = false hint = """ This time, the function *declaration* is okay, but there's something wrong with the place where we're calling the function.""" @@ -171,7 +171,7 @@ with the place where we're calling the function.""" [[exercises]] name = "functions4" dir = "02_functions" -mode = "run" +test = false hint = """ The error message points to the function `sale_price` and says it expects a type after the `->`. This is where the function's return type should be -- take a @@ -180,7 +180,7 @@ look at the `is_even` function for an example!""" [[exercises]] name = "functions5" dir = "02_functions" -mode = "run" +test = false hint = """ This is a really common error that can be fixed by removing one character. It happens because Rust distinguishes between expressions and statements: @@ -199,7 +199,6 @@ They are not the same. There are two solutions: [[exercises]] name = "if1" dir = "03_if" -mode = "test" hint = """ It's possible to do this in one line if you would like! @@ -215,7 +214,6 @@ Remember in Rust that: [[exercises]] name = "if2" dir = "03_if" -mode = "test" hint = """ For that first compiler error, it's important in Rust that each conditional block returns the same type! To get the tests passing, you will need a couple @@ -224,7 +222,6 @@ conditions checking different input values.""" [[exercises]] name = "if3" dir = "03_if" -mode = "test" hint = """ In Rust, every arm of an `if` expression has to return the same type of value. Make sure the type is consistent across all arms.""" @@ -234,7 +231,6 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" dir = "quizzes" -mode = "test" hint = "No hints this time ;)" # PRIMITIVE TYPES @@ -242,19 +238,19 @@ hint = "No hints this time ;)" [[exercises]] name = "primitive_types1" dir = "04_primitive_types" -mode = "run" +test = false hint = "No hints this time ;)" [[exercises]] name = "primitive_types2" dir = "04_primitive_types" -mode = "run" +test = false hint = "No hints this time ;)" [[exercises]] name = "primitive_types3" dir = "04_primitive_types" -mode = "run" +test = false hint = """ There's a shorthand to initialize Arrays with a certain size that does not require you to type in 100 items (but you certainly can if you want!). @@ -270,7 +266,6 @@ for `a.len() >= 100`?""" [[exercises]] name = "primitive_types4" dir = "04_primitive_types" -mode = "test" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the @@ -285,7 +280,7 @@ https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] name = "primitive_types5" dir = "04_primitive_types" -mode = "run" +test = false hint = """ Take a look at the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type @@ -298,7 +293,6 @@ of the tuple. You can do it!!""" [[exercises]] name = "primitive_types6" dir = "04_primitive_types" -mode = "test" hint = """ While you could use a destructuring `let` for the tuple here, try indexing into it instead, as explained in the last example of the @@ -311,7 +305,6 @@ Now you have another tool in your toolbox!""" [[exercises]] name = "vecs1" dir = "05_vecs" -mode = "test" hint = """ In Rust, there are two ways to define a Vector. 1. One way is to use the `Vec::new()` function to create a new vector @@ -326,7 +319,6 @@ of the Rust book to learn more. [[exercises]] name = "vecs2" dir = "05_vecs" -mode = "test" hint = """ In the first function we are looping over the Vector and getting a reference to one `element` at a time. @@ -349,7 +341,6 @@ What do you think is the more commonly used pattern under Rust developers? [[exercises]] name = "move_semantics1" dir = "06_move_semantics" -mode = "test" hint = """ So you've got the "cannot borrow immutable local variable `vec` as mutable" error on the line where we push an element to the vector, right? @@ -363,7 +354,6 @@ happens!""" [[exercises]] name = "move_semantics2" dir = "06_move_semantics" -mode = "test" hint = """ When running this exercise for the first time, you'll notice an error about "borrow of moved value". In Rust, when an argument is passed to a function and @@ -384,7 +374,6 @@ try them all: [[exercises]] name = "move_semantics3" dir = "06_move_semantics" -mode = "test" hint = """ The difference between this one and the previous ones is that the first line of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, @@ -394,7 +383,6 @@ an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" dir = "06_move_semantics" -mode = "test" hint = """ Stop reading whenever you feel like you have enough direction :) Or try doing one step and then fixing the compiler errors that result! @@ -408,7 +396,6 @@ So the end goal is to: [[exercises]] name = "move_semantics5" dir = "06_move_semantics" -mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of referent (`x`) immediately after @@ -420,7 +407,7 @@ https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-ref [[exercises]] name = "move_semantics6" dir = "06_move_semantics" -mode = "run" +test = false hint = """ To find the answer, you can consult the book section "References and Borrowing": https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html @@ -441,7 +428,6 @@ Another hint: it has to do with the `&` character.""" [[exercises]] name = "structs1" dir = "07_structs" -mode = "test" hint = """ Rust has more than one type of struct. Three actually, all variants are used to package related data together. @@ -461,7 +447,6 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" dir = "07_structs" -mode = "test" hint = """ Creating instances of structs is easy, all you need to do is assign some values to its fields. @@ -473,7 +458,6 @@ https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-ins [[exercises]] name = "structs3" dir = "07_structs" -mode = "test" hint = """ For `is_international`: What makes a package international? Seems related to the places it goes through right? @@ -489,13 +473,13 @@ https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" [[exercises]] name = "enums1" dir = "08_enums" -mode = "run" +test = false hint = "No hints this time ;)" [[exercises]] name = "enums2" dir = "08_enums" -mode = "run" +test = false hint = """ You can create enumerations that have different variants with different types such as no data, anonymous structs, a single string, tuples, ...etc""" @@ -503,7 +487,6 @@ such as no data, anonymous structs, a single string, tuples, ...etc""" [[exercises]] name = "enums3" dir = "08_enums" -mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. @@ -517,7 +500,7 @@ to get value in the variant.""" [[exercises]] name = "strings1" dir = "09_strings" -mode = "run" +test = false hint = """ The `current_favorite_color` function is currently returning a string slice with the `'static` lifetime. We know this because the data of the string lives @@ -531,7 +514,7 @@ another way that uses the `From` trait.""" [[exercises]] name = "strings2" dir = "09_strings" -mode = "run" +test = false hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way @@ -546,7 +529,6 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion [[exercises]] name = "strings3" dir = "09_strings" -mode = "test" hint = """ There's tons of useful standard library functions for strings. Let's try and use some of them: https://doc.rust-lang.org/std/string/struct.String.html#method.trim @@ -557,7 +539,7 @@ the string slice into an owned string, which you can then freely extend.""" [[exercises]] name = "strings4" dir = "09_strings" -mode = "run" +test = false hint = "No hints this time ;)" # MODULES @@ -565,7 +547,7 @@ hint = "No hints this time ;)" [[exercises]] name = "modules1" dir = "10_modules" -mode = "run" +test = false hint = """ Everything is private in Rust by default-- but there's a keyword we can use to make something public! The compiler error should point to the thing that @@ -574,7 +556,7 @@ needs to be public.""" [[exercises]] name = "modules2" dir = "10_modules" -mode = "run" +test = false hint = """ The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and @@ -586,7 +568,7 @@ Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-w [[exercises]] name = "modules3" dir = "10_modules" -mode = "run" +test = false hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a `use` statement for these two to bring them into scope. You can use nested @@ -597,7 +579,6 @@ paths or the glob operator to bring these two in using only one line.""" [[exercises]] name = "hashmaps1" dir = "11_hashmaps" -mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out the type for the `basket`. @@ -609,7 +590,6 @@ Hint 2: Number of fruits should be at least 5. And you have to put [[exercises]] name = "hashmaps2" dir = "11_hashmaps" -mode = "test" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value @@ -618,7 +598,6 @@ Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only- [[exercises]] name = "hashmaps3" dir = "11_hashmaps" -mode = "test" hint = """ Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert entries corresponding to each team in the scores table. @@ -636,7 +615,6 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" dir = "quizzes" -mode = "test" hint = "No hints this time ;)" # OPTIONS @@ -644,7 +622,6 @@ hint = "No hints this time ;)" [[exercises]] name = "options1" dir = "12_options" -mode = "test" hint = """ Options can have a `Some` value, with an inner value, or a `None` value, without an inner value. @@ -656,7 +633,6 @@ it doesn't panic in your face later?""" [[exercises]] name = "options2" dir = "12_options" -mode = "test" hint = """ Check out: @@ -673,7 +649,7 @@ Also see `Option::flatten` [[exercises]] name = "options3" dir = "12_options" -mode = "run" +test = false hint = """ The compiler says a partial move happened in the `match` statement. How can this be avoided? The compiler shows the correction needed. @@ -686,7 +662,6 @@ https://doc.rust-lang.org/std/keyword.ref.html""" [[exercises]] name = "errors1" dir = "13_error_handling" -mode = "test" hint = """ `Ok` and `Err` are the two variants of `Result`, so what the tests are saying is that `generate_nametag_text` should return a `Result` instead of an `Option`. @@ -702,7 +677,6 @@ To make this change, you'll need to: [[exercises]] name = "errors2" dir = "13_error_handling" -mode = "test" hint = """ One way to handle this is using a `match` statement on `item_quantity.parse::()` where the cases are `Ok(something)` and @@ -718,7 +692,7 @@ and give it a try!""" [[exercises]] name = "errors3" dir = "13_error_handling" -mode = "run" +test = false hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your @@ -730,7 +704,6 @@ positive results.""" [[exercises]] name = "errors4" dir = "13_error_handling" -mode = "test" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. @@ -742,7 +715,7 @@ everything is... okay :)""" [[exercises]] name = "errors5" dir = "13_error_handling" -mode = "run" +test = false hint = """ There are two different possible `Result` types produced within `main()`, which are propagated using `?` operators. How do we declare a return type from @@ -766,7 +739,6 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "errors6" dir = "13_error_handling" -mode = "test" hint = """ This exercise uses a completed version of `PositiveNonzeroInteger` from errors4. @@ -788,7 +760,7 @@ https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" [[exercises]] name = "generics1" dir = "14_generics" -mode = "run" +test = false hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any type. @@ -798,7 +770,6 @@ You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] name = "generics2" dir = "14_generics" -mode = "test" hint = """ Currently we are wrapping only values of type `u32`. @@ -812,7 +783,6 @@ If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html [[exercises]] name = "traits1" dir = "15_traits" -mode = "test" hint = """ A discussion about Traits in Rust can be found at: https://doc.rust-lang.org/book/ch10-02-traits.html @@ -821,7 +791,6 @@ https://doc.rust-lang.org/book/ch10-02-traits.html [[exercises]] name = "traits2" dir = "15_traits" -mode = "test" hint = """ Notice how the trait takes ownership of `self`, and returns `Self`. @@ -834,7 +803,6 @@ the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" [[exercises]] name = "traits3" dir = "15_traits" -mode = "test" hint = """ Traits can have a default implementation for functions. Structs that implement the trait can then use the default version of these functions if they choose not @@ -846,7 +814,6 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#def [[exercises]] name = "traits4" dir = "15_traits" -mode = "test" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing the '??' with 'impl ' @@ -857,7 +824,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#tra [[exercises]] name = "traits5" dir = "15_traits" -mode = "run" +test = false hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the '??' with 'impl <> + <>'. @@ -870,7 +837,6 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" dir = "quizzes" -mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back to your knowledge of traits, specifically 'Trait Bound Syntax' @@ -882,7 +848,7 @@ You may also need this: `use std::fmt::Display;`.""" [[exercises]] name = "lifetimes1" dir = "16_lifetimes" -mode = "run" +test = false hint = """ Let the compiler guide you. Also take a look at the book if you need help: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" @@ -890,7 +856,7 @@ https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] name = "lifetimes2" dir = "16_lifetimes" -mode = "run" +test = false hint = """ Remember that the generic lifetime `'a` will get the concrete lifetime that is equal to the smaller of the lifetimes of `x` and `y`. @@ -904,7 +870,7 @@ inner block: [[exercises]] name = "lifetimes3" dir = "16_lifetimes" -mode = "run" +test = false hint = """ If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" @@ -914,7 +880,6 @@ to be added?""" [[exercises]] name = "tests1" dir = "17_tests" -mode = "test" hint = """ You don't even need to write any code to test -- you can just test values and run that, even though you wouldn't do that in real life. :) @@ -929,7 +894,6 @@ ones pass, and which ones fail :)""" [[exercises]] name = "tests2" dir = "17_tests" -mode = "test" hint = """ Like the previous exercise, you don't need to write any code to get this test to compile and run. @@ -942,7 +906,6 @@ argument comes first and which comes second!""" [[exercises]] name = "tests3" dir = "17_tests" -mode = "test" hint = """ You can call a function right where you're passing arguments to `assert!`. So you could do something like `assert!(having_fun())`. @@ -953,7 +916,6 @@ what you're doing using `!`, like `assert!(!having_fun())`.""" [[exercises]] name = "tests4" dir = "17_tests" -mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. @@ -967,7 +929,6 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa [[exercises]] name = "iterators1" dir = "18_iterators" -mode = "test" hint = """ Step 1: @@ -990,7 +951,6 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. [[exercises]] name = "iterators2" dir = "18_iterators" -mode = "test" hint = """ Step 1: @@ -1016,7 +976,6 @@ powerful and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" dir = "18_iterators" -mode = "test" hint = """ The `divide` function needs to return the correct error when even division is not possible. @@ -1035,7 +994,6 @@ powerful! It can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" dir = "18_iterators" -mode = "test" hint = """ In an imperative language, you might write a `for` loop that updates a mutable variable. Or, you might write code utilizing recursion and a match clause. In @@ -1047,7 +1005,6 @@ Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" dir = "18_iterators" -mode = "test" hint = """ The documentation for the `std::iter::Iterator` trait contains numerous methods that would be helpful here. @@ -1066,7 +1023,6 @@ a different method that could make your code more compact than using `fold`.""" [[exercises]] name = "box1" dir = "19_smart_pointers" -mode = "test" hint = """ Step 1: @@ -1090,7 +1046,6 @@ definition and try other types! [[exercises]] name = "rc1" dir = "19_smart_pointers" -mode = "test" hint = """ This is a straightforward exercise to use the `Rc` type. Each `Planet` has ownership of the `Sun`, and uses `Rc::clone()` to increment the reference count @@ -1109,7 +1064,7 @@ See more at: https://doc.rust-lang.org/book/ch15-04-rc.html [[exercises]] name = "arc1" dir = "19_smart_pointers" -mode = "run" +test = false hint = """ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` @@ -1127,7 +1082,6 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" dir = "19_smart_pointers" -mode = "test" hint = """ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is called. @@ -1141,7 +1095,7 @@ on the `Cow` type. [[exercises]] name = "threads1" dir = "20_threads" -mode = "run" +test = false hint = """ `JoinHandle` is a struct that is returned from a spawned thread: https://doc.rust-lang.org/std/thread/fn.spawn.html @@ -1159,7 +1113,7 @@ https://doc.rust-lang.org/std/thread/struct.JoinHandle.html [[exercises]] name = "threads2" dir = "20_threads" -mode = "run" +test = false hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access to **immutable** data. But we want to *change* the number of `jobs_completed` @@ -1181,7 +1135,6 @@ https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-betwee [[exercises]] name = "threads3" dir = "20_threads" -mode = "test" hint = """ An alternate way to handle concurrency between threads is to use an `mpsc` (multiple producer, single consumer) channel to communicate. @@ -1200,7 +1153,7 @@ See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. [[exercises]] name = "macros1" dir = "21_macros" -mode = "run" +test = false hint = """ When you call a macro, you need to add something special compared to a regular function call. If you're stuck, take a look at what's inside @@ -1209,7 +1162,7 @@ regular function call. If you're stuck, take a look at what's inside [[exercises]] name = "macros2" dir = "21_macros" -mode = "run" +test = false hint = """ Macros don't quite play by the same rules as the rest of Rust, in terms of what's available where. @@ -1220,7 +1173,7 @@ Unlike other things in Rust, the order of "where you define a macro" versus [[exercises]] name = "macros3" dir = "21_macros" -mode = "run" +test = false hint = """ In order to use a macro outside of its module, you need to do something special to the module to lift the macro out into its parent. @@ -1231,7 +1184,7 @@ exported macros, if you've seen any of those around.""" [[exercises]] name = "macros4" dir = "21_macros" -mode = "run" +test = false hint = """ You only need to add a single character to make this compile. @@ -1248,7 +1201,8 @@ https://veykril.github.io/tlborm/""" [[exercises]] name = "clippy1" dir = "22_clippy" -mode = "clippy" +test = false +strict_clippy = true hint = """ Rust stores the highest precision version of any long or infinite precision mathematical constants in the Rust standard library: @@ -1264,14 +1218,16 @@ appropriate replacement constant from `std::f32::consts`...""" [[exercises]] name = "clippy2" dir = "22_clippy" -mode = "clippy" +test = false +strict_clippy = true hint = """ `for` loops over `Option` values are more clearly expressed as an `if let`""" [[exercises]] name = "clippy3" dir = "22_clippy" -mode = "clippy" +test = false +strict_clippy = true hint = "No hints this time!" # TYPE CONVERSIONS @@ -1279,7 +1235,6 @@ hint = "No hints this time!" [[exercises]] name = "using_as" dir = "23_conversions" -mode = "test" hint = """ Use the `as` operator to cast one of the operands in the last line of the `average` function into the expected return type.""" @@ -1287,14 +1242,12 @@ Use the `as` operator to cast one of the operands in the last line of the [[exercises]] name = "from_into" dir = "23_conversions" -mode = "test" hint = """ Follow the steps provided right before the `From` implementation""" [[exercises]] name = "from_str" dir = "23_conversions" -mode = "test" hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, or an `Err` with an error if the string is not valid. @@ -1315,7 +1268,6 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "try_from_into" dir = "23_conversions" -mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. You can also use the example at @@ -1338,6 +1290,5 @@ Challenge: Can you make the `TryFrom` implementations generic over many integer [[exercises]] name = "as_ref_mut" dir = "23_conversions" -mode = "test" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/src/app_state.rs b/src/app_state.rs index 11ac8ee4c1..476b5a9cb7 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -112,7 +112,8 @@ impl AppState { dir, name, path, - mode: exercise_info.mode, + test: exercise_info.test, + strict_clippy: exercise_info.strict_clippy, hint, done: false, } @@ -329,8 +330,6 @@ impl AppState { } writeln!(writer, "{}", "ok".green())?; - - output.clear(); } writer.execute(Clear(ClearType::All))?; diff --git a/src/dev/new.rs b/src/dev/new.rs index 82aba42bb8..8f870107af 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -99,10 +99,15 @@ name = "???" # 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" +# Rustlings expects the exercise to contain tests and run them. +# You can optionally disable testing by setting `test` to `false` (the default is `true`). +# In that case, the exercise will be considered done when it just successfully compiles. +# test = true + +# Rustlings will always run Clippy on exercises. +# You can optionally set `strict_clippy` to `true` (the default is `false`) to only consider +# the exercise as done when there are no warnings left. +# strict_clippy = false # A multi-line hint to be shown to users on request. hint = """???""" diff --git a/src/exercise.rs b/src/exercise.rs index 17cc8d7791..366dc2b69c 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -3,24 +3,38 @@ use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, io::{Read, Write}, - process::Command, + process::{Command, Stdio}, }; -use crate::{in_official_repo, info_file::Mode, terminal_link::TerminalFileLink, DEBUG_PROFILE}; +use crate::{in_official_repo, terminal_link::TerminalFileLink, DEBUG_PROFILE}; // TODO pub const OUTPUT_CAPACITY: usize = 1 << 12; -fn run_command(mut cmd: Command, cmd_description: &str, output: &mut Vec) -> Result { +fn run_command( + mut cmd: Command, + cmd_description: &str, + output: &mut Vec, + stderr: bool, +) -> Result { let (mut reader, writer) = os_pipe::pipe().with_context(|| { format!("Failed to create a pipe to run the command `{cmd_description}``") })?; + let (stdout, stderr) = if stderr { + ( + Stdio::from(writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{cmd_description}`") + })?), + Stdio::from(writer), + ) + } else { + (Stdio::from(writer), Stdio::null()) + }; + let mut handle = cmd - .stdout(writer.try_clone().with_context(|| { - format!("Failed to clone the pipe writer for the command `{cmd_description}`") - })?) - .stderr(writer) + .stdout(stdout) + .stderr(stderr) .spawn() .with_context(|| format!("Failed to run the command `{cmd_description}`"))?; @@ -45,8 +59,8 @@ pub struct Exercise { pub name: &'static str, // Exercise's path pub path: &'static str, - // The mode of the exercise - pub mode: Mode, + pub test: bool, + pub strict_clippy: bool, // The hint text associated with the exercise pub hint: String, pub done: bool, @@ -54,10 +68,22 @@ pub struct Exercise { impl Exercise { fn run_bin(&self, output: &mut Vec) -> Result { - writeln!(output, "{}", "Output".bold().magenta().underlined())?; + writeln!(output, "{}", "Output".underlined())?; let bin_path = format!("target/debug/{}", self.name); - run_command(Command::new(&bin_path), &bin_path, output) + let success = run_command(Command::new(&bin_path), &bin_path, output, true)?; + + if !success { + writeln!( + output, + "{}", + "The exercise didn't run successfully (nonzero exit code)" + .bold() + .red() + )?; + } + + Ok(success) } fn cargo_cmd( @@ -67,6 +93,7 @@ impl Exercise { cmd_description: &str, output: &mut Vec, dev: bool, + stderr: bool, ) -> Result { let mut cmd = Command::new("cargo"); cmd.arg(command); @@ -86,25 +113,7 @@ impl Exercise { .arg(self.name) .args(args); - run_command(cmd, cmd_description, output) - } - - fn cargo_cmd_with_bin_output( - &self, - command: &str, - args: &[&str], - cmd_description: &str, - output: &mut Vec, - dev: bool, - ) -> Result { - // Discard the output of `cargo build` because it will be shown again by the Cargo command. - output.clear(); - - let cargo_cmd_success = self.cargo_cmd(command, args, cmd_description, output, dev)?; - - let run_success = self.run_bin(output)?; - - Ok(cargo_cmd_success && run_success) + run_command(cmd, cmd_description, output, stderr) } pub fn run(&self, output: &mut Vec) -> Result { @@ -113,35 +122,49 @@ impl Exercise { // Developing the official Rustlings. let dev = DEBUG_PROFILE && in_official_repo(); - let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev)?; + let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev, true)?; if !build_success { return Ok(false); } - match self.mode { - Mode::Run => self.run_bin(output), - Mode::Test => self.cargo_cmd_with_bin_output( - "test", - &[ - "--", - "--color", - "always", - "--nocapture", - "--format", - "pretty", - ], - "cargo test …", - output, - dev, - ), - Mode::Clippy => self.cargo_cmd_with_bin_output( - "clippy", - &["--", "-D", "warnings"], - "cargo clippy …", - output, - dev, - ), + // Discard the output of `cargo build` because it will be shown again by the Cargo command. + output.clear(); + + let clippy_args: &[&str] = if self.strict_clippy { + &["--", "-D", "warnings"] + } else { + &[] + }; + let clippy_success = + self.cargo_cmd("clippy", clippy_args, "cargo clippy …", output, dev, true)?; + if !clippy_success { + return Ok(false); + } + + if !self.test { + return self.run_bin(output); } + + let test_success = self.cargo_cmd( + "test", + &[ + "--", + "--color", + "always", + "--nocapture", + "--format", + "pretty", + ], + "cargo test …", + output, + dev, + // Hide warnings because they are shown by Clippy. + false, + )?; + + let run_success = self.run_bin(output)?; + + Ok(test_success && run_success) } pub fn terminal_link(&self) -> StyledContent> { diff --git a/src/info_file.rs b/src/info_file.rs index 6938cd03e1..dbe4f089ed 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -2,18 +2,6 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; use std::{fs, io::ErrorKind}; -// The mode of the exercise. -#[derive(Deserialize, Copy, Clone)] -#[serde(rename_all = "lowercase")] -pub enum Mode { - // The exercise should be compiled as a binary - Run, - // The exercise should be compiled as a test harness - Test, - // The exercise should be linted with clippy - Clippy, -} - // Deserialized from the `info.toml` file. #[derive(Deserialize)] pub struct ExerciseInfo { @@ -21,11 +9,17 @@ pub struct ExerciseInfo { pub name: String, // The exercise's directory inside the `exercises` directory pub dir: Option, - // The mode of the exercise - pub mode: Mode, + #[serde(default = "default_true")] + pub test: bool, + #[serde(default)] + pub strict_clippy: bool, // The hint text associated with the exercise pub hint: String, } +#[inline] +const fn default_true() -> bool { + true +} impl ExerciseInfo { pub fn path(&self) -> String { diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index ef99a07ae4..554607a8f7 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -2,10 +2,9 @@ format_version = 1 [[exercises]] name = "compFailure" -mode = "run" +test = false hint = "" [[exercises]] name = "testFailure" -mode = "test" hint = "Hello!" diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index eec24ea198..ff0b932e4e 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -2,15 +2,14 @@ format_version = 1 [[exercises]] name = "pending_exercise" -mode = "run" +test = false hint = """""" [[exercises]] name = "pending_test_exercise" -mode = "test" hint = """""" [[exercises]] name = "finished_exercise" -mode = "run" +test = false hint = """""" diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index 88650ecbaf..d66d7d4764 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -2,10 +2,9 @@ format_version = 1 [[exercises]] name = "compSuccess" -mode = "run" +test = false hint = """""" [[exercises]] name = "testSuccess" -mode = "test" hint = """""" From d26f47ddddaabcde36d5cb02ec220ccd1c141f10 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 03:27:41 +0200 Subject: [PATCH 0793/1432] Fix tests --- tests/fixture/failure/exercises/compNoExercise.rs | 3 +-- tests/fixture/failure/exercises/testFailure.rs | 2 ++ tests/fixture/failure/exercises/testNotPassed.rs | 2 ++ tests/fixture/state/exercises/finished_exercise.rs | 6 +----- tests/fixture/state/exercises/pending_exercise.rs | 6 +----- tests/fixture/state/exercises/pending_test_exercise.rs | 2 ++ tests/fixture/success/exercises/testSuccess.rs | 2 ++ 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/fixture/failure/exercises/compNoExercise.rs b/tests/fixture/failure/exercises/compNoExercise.rs index f79c691f08..f328e4d9d0 100644 --- a/tests/fixture/failure/exercises/compNoExercise.rs +++ b/tests/fixture/failure/exercises/compNoExercise.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} diff --git a/tests/fixture/failure/exercises/testFailure.rs b/tests/fixture/failure/exercises/testFailure.rs index b33a5d2891..fcbcf90a4a 100644 --- a/tests/fixture/failure/exercises/testFailure.rs +++ b/tests/fixture/failure/exercises/testFailure.rs @@ -1,3 +1,5 @@ +fn main() {} + #[test] fn passing() { asset!(true); diff --git a/tests/fixture/failure/exercises/testNotPassed.rs b/tests/fixture/failure/exercises/testNotPassed.rs index a9fe88ddc8..de0d61c0b1 100644 --- a/tests/fixture/failure/exercises/testNotPassed.rs +++ b/tests/fixture/failure/exercises/testNotPassed.rs @@ -1,3 +1,5 @@ +fn main() {} + #[test] fn not_passing() { assert!(false); diff --git a/tests/fixture/state/exercises/finished_exercise.rs b/tests/fixture/state/exercises/finished_exercise.rs index 016b827c89..f328e4d9d0 100644 --- a/tests/fixture/state/exercises/finished_exercise.rs +++ b/tests/fixture/state/exercises/finished_exercise.rs @@ -1,5 +1 @@ -// fake_exercise - -fn main() { - -} +fn main() {} diff --git a/tests/fixture/state/exercises/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs index 016b827c89..f328e4d9d0 100644 --- a/tests/fixture/state/exercises/pending_exercise.rs +++ b/tests/fixture/state/exercises/pending_exercise.rs @@ -1,5 +1 @@ -// fake_exercise - -fn main() { - -} +fn main() {} diff --git a/tests/fixture/state/exercises/pending_test_exercise.rs b/tests/fixture/state/exercises/pending_test_exercise.rs index 2002ef17dd..718e1dbb7d 100644 --- a/tests/fixture/state/exercises/pending_test_exercise.rs +++ b/tests/fixture/state/exercises/pending_test_exercise.rs @@ -1,2 +1,4 @@ +fn main() {} + #[test] fn it_works() {} diff --git a/tests/fixture/success/exercises/testSuccess.rs b/tests/fixture/success/exercises/testSuccess.rs index 7139b50b06..4296cf61a9 100644 --- a/tests/fixture/success/exercises/testSuccess.rs +++ b/tests/fixture/success/exercises/testSuccess.rs @@ -1,3 +1,5 @@ +fn main() {} + #[test] fn passing() { println!("THIS TEST TOO SHALL PASS"); From 428998a4cf74246b0a1da4b8013b504d86cdabeb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 03:28:44 +0200 Subject: [PATCH 0794/1432] Quicker response to file changes --- src/watch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watch.rs b/src/watch.rs index 5ebe526fb9..1e22f59246 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -51,7 +51,7 @@ pub fn watch( // Otherwise, the file watcher exits. let _debouncer_guard = if let Some(exercise_paths) = notify_exercise_paths { let mut debouncer = new_debouncer( - Duration::from_secs(1), + Duration::from_millis(500), DebounceEventHandler { tx: tx.clone(), exercise_paths, From 1f1a62d83ef9398a1a31c904a2ef6d81f5455e59 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 14:43:02 +0200 Subject: [PATCH 0795/1432] Raise the output capacity --- src/exercise.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 366dc2b69c..21ae5828bd 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -8,8 +8,7 @@ use std::{ use crate::{in_official_repo, terminal_link::TerminalFileLink, DEBUG_PROFILE}; -// TODO -pub const OUTPUT_CAPACITY: usize = 1 << 12; +pub const OUTPUT_CAPACITY: usize = 1 << 14; fn run_command( mut cmd: Command, From 14fe248b4bfc3c577be7deacc346a959c7c0cc47 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 14:44:12 +0200 Subject: [PATCH 0796/1432] Optimize the notify event handler --- src/main.rs | 8 ++++---- src/watch.rs | 6 +++--- src/watch/notify_event.rs | 21 +++++++++++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index a928504400..7a142fdb63 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,23 +144,23 @@ fn main() -> Result<()> { match args.command { None => { - let notify_exercise_paths: Option<&'static [&'static str]> = if args.manual_run { + let notify_exercise_names = if args.manual_run { None } else { // For the the notify event handler thread. // Leaking is not a problem because the slice lives until the end of the program. Some( - app_state + &*app_state .exercises() .iter() - .map(|exercise| exercise.path) + .map(|exercise| exercise.name.as_bytes()) .collect::>() .leak(), ) }; loop { - match watch::watch(&mut app_state, notify_exercise_paths)? { + match watch::watch(&mut app_state, notify_exercise_names)? { 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 diff --git a/src/watch.rs b/src/watch.rs index 1e22f59246..2f4409acd3 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -42,19 +42,19 @@ pub enum WatchExit { pub fn watch( app_state: &mut AppState, - notify_exercise_paths: Option<&'static [&'static str]>, + notify_exercise_names: Option<&'static [&'static [u8]]>, ) -> Result { let (tx, rx) = channel(); let mut manual_run = false; // Prevent dropping the guard until the end of the function. // Otherwise, the file watcher exits. - let _debouncer_guard = if let Some(exercise_paths) = notify_exercise_paths { + let _debouncer_guard = if let Some(exercise_names) = notify_exercise_names { let mut debouncer = new_debouncer( Duration::from_millis(500), DebounceEventHandler { tx: tx.clone(), - exercise_paths, + exercise_names, }, ) .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index fb9a8c05cd..f66a83427c 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -5,7 +5,7 @@ use super::WatchEvent; pub struct DebounceEventHandler { pub tx: Sender, - pub exercise_paths: &'static [&'static str], + pub exercise_names: &'static [&'static [u8]], } impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { @@ -15,15 +15,24 @@ impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { let Some(exercise_ind) = event .iter() .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { + if event.kind != DebouncedEventKind::Any { return None; } - self.exercise_paths + let file_name = event.path.file_name()?.to_str()?.as_bytes(); + + if file_name.len() < 4 { + return None; + } + let (file_name_without_ext, ext) = file_name.split_at(file_name.len() - 3); + + if ext != b".rs" { + return None; + } + + self.exercise_names .iter() - .position(|path| event.path.ends_with(path)) + .position(|exercise_name| *exercise_name == file_name_without_ext) }) .min() else { From c1d28b502eb88aa06acf196ff055f285a8482132 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 14:51:14 +0200 Subject: [PATCH 0797/1432] Format test file :P --- tests/fixture/success/exercises/compSuccess.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/fixture/success/exercises/compSuccess.rs b/tests/fixture/success/exercises/compSuccess.rs index f79c691f08..f328e4d9d0 100644 --- a/tests/fixture/success/exercises/compSuccess.rs +++ b/tests/fixture/success/exercises/compSuccess.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} From c7c8d9968040d7df438fa33b32d9495415f751d2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:22:11 +0200 Subject: [PATCH 0798/1432] Moar responsive :P --- src/watch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watch.rs b/src/watch.rs index 2f4409acd3..5c3f1709b8 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -51,7 +51,7 @@ pub fn watch( // Otherwise, the file watcher exits. let _debouncer_guard = if let Some(exercise_names) = notify_exercise_names { let mut debouncer = new_debouncer( - Duration::from_millis(500), + Duration::from_millis(200), DebounceEventHandler { tx: tx.clone(), exercise_names, From 29abaee4ec22627475aeabaf8425f759d2463530 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:22:14 +0200 Subject: [PATCH 0799/1432] Update dep --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 823fec36dc..23c4887a76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -965,9 +965,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys 0.52.0", ] From b3b4b7d59c5ecbf9658f7cd2eae85ee1c9e41e73 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:23:24 +0200 Subject: [PATCH 0800/1432] Update initialized .gitignore --- src/init.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/init.rs b/src/init.rs index f1a9509976..eeb1c2925e 100644 --- a/src/init.rs +++ b/src/init.rs @@ -55,9 +55,10 @@ pub fn init() -> Result<()> { Ok(()) } -pub const GITIGNORE: &[u8] = b"Cargo.lock -.rustlings-state.txt +pub const GITIGNORE: &[u8] = b".rustlings-state.txt +Cargo.lock target +.vscode "; pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; From fcefa3d6144028a77ed381ddaabcf004a02c804c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:33:24 +0200 Subject: [PATCH 0801/1432] Name the exercises' package `exercises` --- dev/Cargo.toml | 3 ++- src/dev/new.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index eddf01646d..8da41a56bf 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -99,6 +99,7 @@ bin = [ ] [package] -name = "rustlings-dev" +name = "exercises" edition = "2021" +# Don't publish the exercises on crates.io! publish = false diff --git a/src/dev/new.rs b/src/dev/new.rs index 8f870107af..c5a0bd274d 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -118,8 +118,9 @@ const CARGO_TOML: &[u8] = bin = [] [package] -name = "rustlings" +name = "exercises" edition = "2021" +# Don't publish the exercises on crates.io! publish = false [dependencies] From 212c82c6f6a0356ed6b292ddc48a8444e8e9dbf2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:34:58 +0200 Subject: [PATCH 0802/1432] Don't ignore .vscode/extensions.json when developing third-party exercises --- src/dev/new.rs | 9 ++++++++- src/init.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/dev/new.rs b/src/dev/new.rs index c5a0bd274d..44487abfb2 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -45,7 +45,7 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { bail!("`git init` didn't run successfully. See the error message above"); } - write_rel_file(".gitignore", &dir_name, crate::init::GITIGNORE)?; + write_rel_file(".gitignore", &dir_name, GITIGNORE)?; create_rel_dir("exercises", &dir_name)?; create_rel_dir("solutions", &dir_name)?; @@ -72,6 +72,13 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { Ok(()) } +pub const GITIGNORE: &[u8] = b".rustlings-state.txt +Cargo.lock +target +.vscode +!.vscode/extensions.json +"; + const INFO_FILE_BEFORE_FORMAT_VERSION: &str = "# The format version is an indicator of the compatibility of third-party exercises with the # Rustlings program. diff --git a/src/init.rs b/src/init.rs index eeb1c2925e..7ce1272dc9 100644 --- a/src/init.rs +++ b/src/init.rs @@ -55,7 +55,7 @@ pub fn init() -> Result<()> { Ok(()) } -pub const GITIGNORE: &[u8] = b".rustlings-state.txt +const GITIGNORE: &[u8] = b".rustlings-state.txt Cargo.lock target .vscode From 6d1d42d2dd4a9323b0cd1f964d1651f27a9b328a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:41:52 +0200 Subject: [PATCH 0803/1432] Try to run `git init` --- src/init.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index 7ce1272dc9..addc5716ef 100644 --- a/src/init.rs +++ b/src/init.rs @@ -4,6 +4,7 @@ use std::{ fs::{self, create_dir}, io::ErrorKind, path::Path, + process::{Command, Stdio}, }; use crate::{cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile}; @@ -50,6 +51,13 @@ pub fn init() -> Result<()> { fs::write(".vscode/extensions.json", VS_CODE_EXTENSIONS_JSON) .context("Failed to create the file `rustlings/.vscode/extensions.json`")?; + // Ignore any Git error because Git initialization is not required. + let _ = Command::new("git") + .arg("init") + .stdin(Stdio::null()) + .stderr(Stdio::null()) + .status(); + println!("{POST_INIT_MSG}"); Ok(()) @@ -76,7 +84,8 @@ You probably already initialized Rustlings. Run `cd rustlings` Then run `rustlings` again"; -const POST_INIT_MSG: &str = "Done initialization! +const POST_INIT_MSG: &str = " +Done initialization! Run `cd rustlings` to go into the generated directory. Then run `rustlings` to get started."; From 8bf8b19a5dd4278a636a56440736c8f3df52b7a5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:51:12 +0200 Subject: [PATCH 0804/1432] Improve output after initialization --- src/init.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/init.rs b/src/init.rs index addc5716ef..ce239ea9ad 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,4 +1,5 @@ use anyhow::{bail, Context, Result}; +use crossterm::style::Stylize; use std::{ env::set_current_dir, fs::{self, create_dir}, @@ -58,7 +59,11 @@ pub fn init() -> Result<()> { .stderr(Stdio::null()) .status(); - println!("{POST_INIT_MSG}"); + println!( + "\n{}\n\n{}", + "Initialization done βœ“".green(), + POST_INIT_MSG.bold(), + ); Ok(()) } @@ -84,8 +89,5 @@ 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. +const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory. Then run `rustlings` to get started."; From c51f1b3f31478f8c82acbd83e9ae873e29159c5f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:58:46 +0200 Subject: [PATCH 0805/1432] Thanks Clippy :D --- src/dev.rs | 6 +++--- src/dev/check.rs | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 38338cd0a4..737de0de63 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -28,15 +28,15 @@ pub enum DevCommands { impl DevCommands { pub fn run(self) -> Result<()> { match self { - DevCommands::New { path, no_git } => { + Self::New { path, no_git } => { if DEBUG_PROFILE { bail!("Disabled in the debug build"); } new::new(&path, no_git).context(INIT_ERR) } - DevCommands::Check => check::check(), - DevCommands::Update => update::update(), + Self::Check => check::check(), + Self::Update => update::update(), } } } diff --git a/src/dev/check.rs b/src/dev/check.rs index 9859c3e39a..564aa0a2d7 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -151,11 +151,9 @@ fn check_cargo_toml( if old_bins != new_bins { if DEBUG_PROFILE { bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it"); - } else { - bail!( - "The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it", - ); } + + bail!("The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it"); } Ok(()) From 3ce32352945a81972b5b421d522ca5e6fbd28d2c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 16:08:07 +0200 Subject: [PATCH 0806/1432] Show warnings and errors in the tests --- src/exercise.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 21ae5828bd..50f360e90d 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -130,9 +130,9 @@ impl Exercise { output.clear(); let clippy_args: &[&str] = if self.strict_clippy { - &["--", "-D", "warnings"] + &["--profile", "test", "--", "-D", "warnings"] } else { - &[] + &["--profile", "test"] }; let clippy_success = self.cargo_cmd("clippy", clippy_args, "cargo clippy …", output, dev, true)?; From 177e2870c5272dacee662d3e9522bc57c5a134c0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 16:30:01 +0200 Subject: [PATCH 0807/1432] Edit comment --- solutions/00_intro/intro1.rs | 2 +- solutions/00_intro/intro2.rs | 2 +- solutions/01_variables/variables1.rs | 2 +- solutions/01_variables/variables2.rs | 2 +- solutions/01_variables/variables3.rs | 2 +- solutions/01_variables/variables4.rs | 2 +- solutions/01_variables/variables5.rs | 2 +- solutions/01_variables/variables6.rs | 2 +- solutions/02_functions/functions1.rs | 2 +- solutions/02_functions/functions2.rs | 2 +- solutions/02_functions/functions3.rs | 2 +- solutions/02_functions/functions4.rs | 2 +- solutions/02_functions/functions5.rs | 2 +- solutions/03_if/if1.rs | 2 +- solutions/03_if/if2.rs | 2 +- solutions/03_if/if3.rs | 2 +- solutions/04_primitive_types/primitive_types1.rs | 2 +- solutions/04_primitive_types/primitive_types2.rs | 2 +- solutions/04_primitive_types/primitive_types3.rs | 2 +- solutions/04_primitive_types/primitive_types4.rs | 2 +- solutions/04_primitive_types/primitive_types5.rs | 2 +- solutions/04_primitive_types/primitive_types6.rs | 2 +- solutions/05_vecs/vecs1.rs | 2 +- solutions/05_vecs/vecs2.rs | 2 +- solutions/06_move_semantics/move_semantics1.rs | 2 +- solutions/06_move_semantics/move_semantics2.rs | 2 +- solutions/06_move_semantics/move_semantics3.rs | 2 +- solutions/06_move_semantics/move_semantics4.rs | 2 +- solutions/06_move_semantics/move_semantics5.rs | 2 +- solutions/06_move_semantics/move_semantics6.rs | 2 +- solutions/07_structs/structs1.rs | 2 +- solutions/07_structs/structs2.rs | 2 +- solutions/07_structs/structs3.rs | 2 +- solutions/08_enums/enums1.rs | 2 +- solutions/08_enums/enums2.rs | 2 +- solutions/08_enums/enums3.rs | 2 +- solutions/09_strings/strings1.rs | 2 +- solutions/09_strings/strings2.rs | 2 +- solutions/09_strings/strings3.rs | 2 +- solutions/09_strings/strings4.rs | 2 +- solutions/10_modules/modules1.rs | 2 +- solutions/10_modules/modules2.rs | 2 +- solutions/10_modules/modules3.rs | 2 +- solutions/11_hashmaps/hashmaps1.rs | 2 +- solutions/11_hashmaps/hashmaps2.rs | 2 +- solutions/11_hashmaps/hashmaps3.rs | 2 +- solutions/12_options/options1.rs | 2 +- solutions/12_options/options2.rs | 2 +- solutions/12_options/options3.rs | 2 +- solutions/13_error_handling/errors1.rs | 2 +- solutions/13_error_handling/errors2.rs | 2 +- solutions/13_error_handling/errors3.rs | 2 +- solutions/13_error_handling/errors4.rs | 2 +- solutions/13_error_handling/errors5.rs | 2 +- solutions/13_error_handling/errors6.rs | 2 +- solutions/14_generics/generics1.rs | 2 +- solutions/14_generics/generics2.rs | 2 +- solutions/15_traits/traits1.rs | 2 +- solutions/15_traits/traits2.rs | 2 +- solutions/15_traits/traits3.rs | 2 +- solutions/15_traits/traits4.rs | 2 +- solutions/15_traits/traits5.rs | 2 +- solutions/16_lifetimes/lifetimes1.rs | 2 +- solutions/16_lifetimes/lifetimes2.rs | 2 +- solutions/16_lifetimes/lifetimes3.rs | 2 +- solutions/17_tests/tests1.rs | 2 +- solutions/17_tests/tests2.rs | 2 +- solutions/17_tests/tests3.rs | 2 +- solutions/17_tests/tests4.rs | 2 +- solutions/18_iterators/iterators1.rs | 2 +- solutions/18_iterators/iterators2.rs | 2 +- solutions/18_iterators/iterators3.rs | 2 +- solutions/18_iterators/iterators4.rs | 2 +- solutions/18_iterators/iterators5.rs | 2 +- solutions/19_smart_pointers/arc1.rs | 2 +- solutions/19_smart_pointers/box1.rs | 2 +- solutions/19_smart_pointers/cow1.rs | 2 +- solutions/19_smart_pointers/rc1.rs | 2 +- solutions/20_threads/threads1.rs | 2 +- solutions/20_threads/threads2.rs | 2 +- solutions/20_threads/threads3.rs | 2 +- solutions/21_macros/macros1.rs | 2 +- solutions/21_macros/macros2.rs | 2 +- solutions/21_macros/macros3.rs | 2 +- solutions/21_macros/macros4.rs | 2 +- solutions/22_clippy/clippy1.rs | 2 +- solutions/22_clippy/clippy2.rs | 2 +- solutions/22_clippy/clippy3.rs | 2 +- solutions/23_conversions/as_ref_mut.rs | 2 +- solutions/23_conversions/from_into.rs | 2 +- solutions/23_conversions/from_str.rs | 2 +- solutions/23_conversions/try_from_into.rs | 2 +- solutions/23_conversions/using_as.rs | 2 +- solutions/quizzes/quiz1.rs | 2 +- solutions/quizzes/quiz2.rs | 2 +- solutions/quizzes/quiz3.rs | 2 +- 96 files changed, 96 insertions(+), 96 deletions(-) diff --git a/solutions/00_intro/intro1.rs b/solutions/00_intro/intro1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/00_intro/intro1.rs +++ b/solutions/00_intro/intro1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/00_intro/intro2.rs b/solutions/00_intro/intro2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/00_intro/intro2.rs +++ b/solutions/00_intro/intro2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/01_variables/variables1.rs b/solutions/01_variables/variables1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/01_variables/variables1.rs +++ b/solutions/01_variables/variables1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/01_variables/variables2.rs b/solutions/01_variables/variables2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/01_variables/variables2.rs +++ b/solutions/01_variables/variables2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/01_variables/variables3.rs +++ b/solutions/01_variables/variables3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/01_variables/variables4.rs +++ b/solutions/01_variables/variables4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs index 70b786d12e..4e18198923 100644 --- a/solutions/01_variables/variables5.rs +++ b/solutions/01_variables/variables5.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/01_variables/variables6.rs b/solutions/01_variables/variables6.rs index 70b786d12e..4e18198923 100644 --- a/solutions/01_variables/variables6.rs +++ b/solutions/01_variables/variables6.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/02_functions/functions1.rs b/solutions/02_functions/functions1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/02_functions/functions1.rs +++ b/solutions/02_functions/functions1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/02_functions/functions2.rs b/solutions/02_functions/functions2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/02_functions/functions2.rs +++ b/solutions/02_functions/functions2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/02_functions/functions3.rs b/solutions/02_functions/functions3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/02_functions/functions3.rs +++ b/solutions/02_functions/functions3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/02_functions/functions4.rs b/solutions/02_functions/functions4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/02_functions/functions4.rs +++ b/solutions/02_functions/functions4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/02_functions/functions5.rs b/solutions/02_functions/functions5.rs index 70b786d12e..4e18198923 100644 --- a/solutions/02_functions/functions5.rs +++ b/solutions/02_functions/functions5.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/03_if/if1.rs b/solutions/03_if/if1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/03_if/if1.rs +++ b/solutions/03_if/if1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/03_if/if2.rs b/solutions/03_if/if2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/03_if/if2.rs +++ b/solutions/03_if/if2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/03_if/if3.rs b/solutions/03_if/if3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/03_if/if3.rs +++ b/solutions/03_if/if3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/04_primitive_types/primitive_types1.rs b/solutions/04_primitive_types/primitive_types1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/04_primitive_types/primitive_types1.rs +++ b/solutions/04_primitive_types/primitive_types1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/04_primitive_types/primitive_types2.rs b/solutions/04_primitive_types/primitive_types2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/04_primitive_types/primitive_types2.rs +++ b/solutions/04_primitive_types/primitive_types2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/04_primitive_types/primitive_types3.rs b/solutions/04_primitive_types/primitive_types3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/04_primitive_types/primitive_types3.rs +++ b/solutions/04_primitive_types/primitive_types3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/04_primitive_types/primitive_types4.rs b/solutions/04_primitive_types/primitive_types4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/04_primitive_types/primitive_types4.rs +++ b/solutions/04_primitive_types/primitive_types4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/04_primitive_types/primitive_types5.rs b/solutions/04_primitive_types/primitive_types5.rs index 70b786d12e..4e18198923 100644 --- a/solutions/04_primitive_types/primitive_types5.rs +++ b/solutions/04_primitive_types/primitive_types5.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/04_primitive_types/primitive_types6.rs b/solutions/04_primitive_types/primitive_types6.rs index 70b786d12e..4e18198923 100644 --- a/solutions/04_primitive_types/primitive_types6.rs +++ b/solutions/04_primitive_types/primitive_types6.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/05_vecs/vecs1.rs b/solutions/05_vecs/vecs1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/05_vecs/vecs1.rs +++ b/solutions/05_vecs/vecs1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/05_vecs/vecs2.rs +++ b/solutions/05_vecs/vecs2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/06_move_semantics/move_semantics1.rs b/solutions/06_move_semantics/move_semantics1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/06_move_semantics/move_semantics1.rs +++ b/solutions/06_move_semantics/move_semantics1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/06_move_semantics/move_semantics2.rs b/solutions/06_move_semantics/move_semantics2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/06_move_semantics/move_semantics2.rs +++ b/solutions/06_move_semantics/move_semantics2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/06_move_semantics/move_semantics3.rs b/solutions/06_move_semantics/move_semantics3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/06_move_semantics/move_semantics3.rs +++ b/solutions/06_move_semantics/move_semantics3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/06_move_semantics/move_semantics4.rs +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs index 70b786d12e..4e18198923 100644 --- a/solutions/06_move_semantics/move_semantics5.rs +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/06_move_semantics/move_semantics6.rs b/solutions/06_move_semantics/move_semantics6.rs index 70b786d12e..4e18198923 100644 --- a/solutions/06_move_semantics/move_semantics6.rs +++ b/solutions/06_move_semantics/move_semantics6.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/07_structs/structs1.rs b/solutions/07_structs/structs1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/07_structs/structs1.rs +++ b/solutions/07_structs/structs1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/07_structs/structs2.rs b/solutions/07_structs/structs2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/07_structs/structs2.rs +++ b/solutions/07_structs/structs2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/07_structs/structs3.rs b/solutions/07_structs/structs3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/07_structs/structs3.rs +++ b/solutions/07_structs/structs3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/08_enums/enums1.rs b/solutions/08_enums/enums1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/08_enums/enums1.rs +++ b/solutions/08_enums/enums1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/08_enums/enums2.rs +++ b/solutions/08_enums/enums2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/08_enums/enums3.rs +++ b/solutions/08_enums/enums3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/09_strings/strings1.rs b/solutions/09_strings/strings1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/09_strings/strings1.rs +++ b/solutions/09_strings/strings1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/09_strings/strings2.rs b/solutions/09_strings/strings2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/09_strings/strings2.rs +++ b/solutions/09_strings/strings2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/09_strings/strings3.rs b/solutions/09_strings/strings3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/09_strings/strings3.rs +++ b/solutions/09_strings/strings3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/09_strings/strings4.rs +++ b/solutions/09_strings/strings4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/10_modules/modules1.rs b/solutions/10_modules/modules1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/10_modules/modules1.rs +++ b/solutions/10_modules/modules1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/10_modules/modules2.rs +++ b/solutions/10_modules/modules2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/10_modules/modules3.rs b/solutions/10_modules/modules3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/10_modules/modules3.rs +++ b/solutions/10_modules/modules3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/11_hashmaps/hashmaps1.rs b/solutions/11_hashmaps/hashmaps1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/11_hashmaps/hashmaps1.rs +++ b/solutions/11_hashmaps/hashmaps1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/11_hashmaps/hashmaps2.rs +++ b/solutions/11_hashmaps/hashmaps2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/12_options/options1.rs +++ b/solutions/12_options/options1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/12_options/options2.rs b/solutions/12_options/options2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/12_options/options2.rs +++ b/solutions/12_options/options2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/12_options/options3.rs b/solutions/12_options/options3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/12_options/options3.rs +++ b/solutions/12_options/options3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/13_error_handling/errors1.rs b/solutions/13_error_handling/errors1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/13_error_handling/errors1.rs +++ b/solutions/13_error_handling/errors1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/13_error_handling/errors2.rs +++ b/solutions/13_error_handling/errors2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/13_error_handling/errors3.rs b/solutions/13_error_handling/errors3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/13_error_handling/errors3.rs +++ b/solutions/13_error_handling/errors3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/13_error_handling/errors4.rs +++ b/solutions/13_error_handling/errors4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/13_error_handling/errors5.rs b/solutions/13_error_handling/errors5.rs index 70b786d12e..4e18198923 100644 --- a/solutions/13_error_handling/errors5.rs +++ b/solutions/13_error_handling/errors5.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs index 70b786d12e..4e18198923 100644 --- a/solutions/13_error_handling/errors6.rs +++ b/solutions/13_error_handling/errors6.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/14_generics/generics1.rs b/solutions/14_generics/generics1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/14_generics/generics1.rs +++ b/solutions/14_generics/generics1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/14_generics/generics2.rs b/solutions/14_generics/generics2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/14_generics/generics2.rs +++ b/solutions/14_generics/generics2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/15_traits/traits1.rs b/solutions/15_traits/traits1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/15_traits/traits1.rs +++ b/solutions/15_traits/traits1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/15_traits/traits2.rs b/solutions/15_traits/traits2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/15_traits/traits2.rs +++ b/solutions/15_traits/traits2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/15_traits/traits3.rs +++ b/solutions/15_traits/traits3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/15_traits/traits4.rs +++ b/solutions/15_traits/traits4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/15_traits/traits5.rs b/solutions/15_traits/traits5.rs index 70b786d12e..4e18198923 100644 --- a/solutions/15_traits/traits5.rs +++ b/solutions/15_traits/traits5.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/16_lifetimes/lifetimes1.rs +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/16_lifetimes/lifetimes2.rs +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/16_lifetimes/lifetimes3.rs b/solutions/16_lifetimes/lifetimes3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/16_lifetimes/lifetimes3.rs +++ b/solutions/16_lifetimes/lifetimes3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/17_tests/tests1.rs b/solutions/17_tests/tests1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/17_tests/tests1.rs +++ b/solutions/17_tests/tests1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/17_tests/tests2.rs b/solutions/17_tests/tests2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/17_tests/tests2.rs +++ b/solutions/17_tests/tests2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/17_tests/tests3.rs +++ b/solutions/17_tests/tests3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/17_tests/tests4.rs b/solutions/17_tests/tests4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/17_tests/tests4.rs +++ b/solutions/17_tests/tests4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/18_iterators/iterators1.rs b/solutions/18_iterators/iterators1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/18_iterators/iterators1.rs +++ b/solutions/18_iterators/iterators1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/18_iterators/iterators2.rs b/solutions/18_iterators/iterators2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/18_iterators/iterators2.rs +++ b/solutions/18_iterators/iterators2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/18_iterators/iterators3.rs +++ b/solutions/18_iterators/iterators3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/18_iterators/iterators4.rs +++ b/solutions/18_iterators/iterators4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs index 70b786d12e..4e18198923 100644 --- a/solutions/18_iterators/iterators5.rs +++ b/solutions/18_iterators/iterators5.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/19_smart_pointers/arc1.rs b/solutions/19_smart_pointers/arc1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/19_smart_pointers/arc1.rs +++ b/solutions/19_smart_pointers/arc1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/19_smart_pointers/box1.rs b/solutions/19_smart_pointers/box1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/19_smart_pointers/box1.rs +++ b/solutions/19_smart_pointers/box1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/19_smart_pointers/cow1.rs b/solutions/19_smart_pointers/cow1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/19_smart_pointers/cow1.rs +++ b/solutions/19_smart_pointers/cow1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/19_smart_pointers/rc1.rs +++ b/solutions/19_smart_pointers/rc1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/20_threads/threads1.rs b/solutions/20_threads/threads1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/20_threads/threads1.rs +++ b/solutions/20_threads/threads1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/20_threads/threads2.rs b/solutions/20_threads/threads2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/20_threads/threads2.rs +++ b/solutions/20_threads/threads2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/20_threads/threads3.rs b/solutions/20_threads/threads3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/20_threads/threads3.rs +++ b/solutions/20_threads/threads3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/21_macros/macros1.rs b/solutions/21_macros/macros1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/21_macros/macros1.rs +++ b/solutions/21_macros/macros1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/21_macros/macros2.rs b/solutions/21_macros/macros2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/21_macros/macros2.rs +++ b/solutions/21_macros/macros2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/21_macros/macros3.rs b/solutions/21_macros/macros3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/21_macros/macros3.rs +++ b/solutions/21_macros/macros3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/21_macros/macros4.rs b/solutions/21_macros/macros4.rs index 70b786d12e..4e18198923 100644 --- a/solutions/21_macros/macros4.rs +++ b/solutions/21_macros/macros4.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/22_clippy/clippy1.rs b/solutions/22_clippy/clippy1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/22_clippy/clippy1.rs +++ b/solutions/22_clippy/clippy1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/22_clippy/clippy2.rs b/solutions/22_clippy/clippy2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/22_clippy/clippy2.rs +++ b/solutions/22_clippy/clippy2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/22_clippy/clippy3.rs +++ b/solutions/22_clippy/clippy3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/23_conversions/as_ref_mut.rs b/solutions/23_conversions/as_ref_mut.rs index 70b786d12e..4e18198923 100644 --- a/solutions/23_conversions/as_ref_mut.rs +++ b/solutions/23_conversions/as_ref_mut.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/23_conversions/from_into.rs b/solutions/23_conversions/from_into.rs index 70b786d12e..4e18198923 100644 --- a/solutions/23_conversions/from_into.rs +++ b/solutions/23_conversions/from_into.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs index 70b786d12e..4e18198923 100644 --- a/solutions/23_conversions/from_str.rs +++ b/solutions/23_conversions/from_str.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/23_conversions/try_from_into.rs b/solutions/23_conversions/try_from_into.rs index 70b786d12e..4e18198923 100644 --- a/solutions/23_conversions/try_from_into.rs +++ b/solutions/23_conversions/try_from_into.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/23_conversions/using_as.rs b/solutions/23_conversions/using_as.rs index 70b786d12e..4e18198923 100644 --- a/solutions/23_conversions/using_as.rs +++ b/solutions/23_conversions/using_as.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/quizzes/quiz1.rs b/solutions/quizzes/quiz1.rs index 70b786d12e..4e18198923 100644 --- a/solutions/quizzes/quiz1.rs +++ b/solutions/quizzes/quiz1.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs index 70b786d12e..4e18198923 100644 --- a/solutions/quizzes/quiz2.rs +++ b/solutions/quizzes/quiz2.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs index 70b786d12e..4e18198923 100644 --- a/solutions/quizzes/quiz3.rs +++ b/solutions/quizzes/quiz3.rs @@ -1 +1 @@ -// TODO +// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° From ca41f9e2df5512e9c283dfde9867a5329e9751c9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:02:07 +0200 Subject: [PATCH 0808/1432] Prepare for using cargo-release --- .typos.toml | 7 +++++++ Cargo.lock | 4 ++-- Cargo.toml | 8 ++++++-- release-hook.sh | 8 ++++++++ rustlings-macros/Cargo.toml | 3 +++ 5 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 .typos.toml create mode 100755 release-hook.sh diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000000..a74498ab1c --- /dev/null +++ b/.typos.toml @@ -0,0 +1,7 @@ +[files] +extend-exclude = [ + "CHANGELOG.md", +] + +[default.extend-words] +"ratatui" = "ratatui" diff --git a/Cargo.lock b/Cargo.lock index 23c4887a76..788cbbbd97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 31e74568fc..5f22665aea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" authors = [ "Liv ", "Mo Bitar ", @@ -41,6 +41,7 @@ include = [ "/info.toml", "/LICENSE", "/README.md", + "/solutions/", "/src/", ] @@ -52,7 +53,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "6.0.0-beta.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-alpha.0" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" @@ -66,3 +67,6 @@ panic = "abort" [profile.dev] panic = "abort" + +[package.metadata.release] +pre-release-hook = ["./release-hook.sh"] diff --git a/release-hook.sh b/release-hook.sh new file mode 100755 index 0000000000..3a2c537662 --- /dev/null +++ b/release-hook.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Error out if any command fails +set -e + +typos +cargo outdated -w --exit-code 1 +cargo test --workspace --all-targets diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index c9c1d2f7cf..f9aba6623e 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -14,3 +14,6 @@ proc-macro = true quote = "1.0.36" serde.workspace = true toml_edit.workspace = true + +[package.metadata.release] +verify = false From 5595e1c3975b71017b79771f3f0f85038e80d1e6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:32:10 +0200 Subject: [PATCH 0809/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 788cbbbd97..ca4d1315f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.1" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.1" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 5f22665aea..caa139e32c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-alpha.0" +version = "6.0.0-beta.1" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-alpha.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.1" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" From a4e623ea94757d976ec38a5a771ea07e64e9105e Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:33:24 +0200 Subject: [PATCH 0810/1432] Fix releasing rustlings --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index caa139e32c..ce3cf0494f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,3 +70,4 @@ panic = "abort" [package.metadata.release] pre-release-hook = ["./release-hook.sh"] +verify = false From 8d45cdb09df7fbff117b91f500ccf82980732a47 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:54:03 +0200 Subject: [PATCH 0811/1432] Fix missing info.toml in the macros crate --- Cargo.toml | 1 - README.md | 4 +++- rustlings-macros/Cargo.toml | 7 ++++--- rustlings-macros/info.toml | 1 + rustlings-macros/src/lib.rs | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) create mode 120000 rustlings-macros/info.toml diff --git a/Cargo.toml b/Cargo.toml index ce3cf0494f..caa139e32c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,4 +70,3 @@ panic = "abort" [package.metadata.release] pre-release-hook = ["./release-hook.sh"] -verify = false diff --git a/README.md b/README.md index 2164269f45..4b5029021c 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,10 @@ This'll also install _Cargo_, Rust's package/project manager. The following command will download and compile Rustlings: + + ```bash -cargo install rustlings --locked +cargo install rustlings@6.0.0-beta.2 --locked ``` ### Initialization diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index f9aba6623e..20d6776e20 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -6,6 +6,10 @@ authors.workspace = true repository.workspace = true license.workspace = true edition.workspace = true +include = [ + "/src/", + "/info.toml", +] [lib] proc-macro = true @@ -14,6 +18,3 @@ proc-macro = true quote = "1.0.36" serde.workspace = true toml_edit.workspace = true - -[package.metadata.release] -verify = false diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml new file mode 120000 index 0000000000..3795291236 --- /dev/null +++ b/rustlings-macros/info.toml @@ -0,0 +1 @@ +../info.toml \ No newline at end of file diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 0bf3dbdffb..fc2bcf142f 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -15,7 +15,7 @@ struct InfoFile { #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { - let exercises = toml_edit::de::from_str::(include_str!("../../info.toml")) + let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) .expect("Failed to parse `info.toml`") .exercises; From aaea5b490fabecbd1111a6ece6446fedfc9800f3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:54:59 +0200 Subject: [PATCH 0812/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca4d1315f3..6faeea2af0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-beta.1" +version = "6.0.0-beta.2" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.1" +version = "6.0.0-beta.2" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index caa139e32c..78ebb9c9c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.1" +version = "6.0.0-beta.2" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.1" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.2" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" From 5920a58e83e1594d2a8e00ec39c82ab1d345eb3d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:58:55 +0200 Subject: [PATCH 0813/1432] Include dev/Cargo.toml --- Cargo.toml | 8 +++++--- dev-Cargo.toml | 1 + src/dev/check.rs | 2 +- src/dev/update.rs | 2 +- src/init.rs | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) create mode 120000 dev-Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 78ebb9c9c5..b3bdaedc39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,12 +37,14 @@ keywords = [ "learning", ] include = [ + "/src/", "/exercises/", + "/solutions/", "/info.toml", - "/LICENSE", + # A symlink to be able to include `dev/Cargo.toml` although `dev` is excluded. + "/dev-Cargo.toml", "/README.md", - "/solutions/", - "/src/", + "/LICENSE", ] [dependencies] diff --git a/dev-Cargo.toml b/dev-Cargo.toml new file mode 120000 index 0000000000..9230c2e948 --- /dev/null +++ b/dev-Cargo.toml @@ -0,0 +1 @@ +dev/Cargo.toml \ No newline at end of file diff --git a/src/dev/check.rs b/src/dev/check.rs index 564aa0a2d7..b6e6f31fed 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -166,7 +166,7 @@ pub fn check() -> Result<()> { if DEBUG_PROFILE { check_cargo_toml( &info_file.exercises, - include_str!("../../dev/Cargo.toml"), + include_str!("../../dev-Cargo.toml"), b"../", )?; } else { diff --git a/src/dev/update.rs b/src/dev/update.rs index d2f20aaed3..fe7622c076 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -29,7 +29,7 @@ pub fn update() -> Result<()> { if DEBUG_PROFILE { update_cargo_toml( &info_file.exercises, - include_str!("../../dev/Cargo.toml"), + include_str!("../../dev-Cargo.toml"), b"../", "dev/Cargo.toml", ) diff --git a/src/init.rs b/src/init.rs index ce239ea9ad..8a9fb36554 100644 --- a/src/init.rs +++ b/src/init.rs @@ -31,7 +31,7 @@ pub fn init() -> Result<()> { .init_exercises_dir(&info_file.exercises) .context("Failed to initialize the `rustlings/exercises` directory")?; - let current_cargo_toml = include_str!("../dev/Cargo.toml"); + let current_cargo_toml = include_str!("../dev-Cargo.toml"); // Skip the first line (comment). let newline_ind = current_cargo_toml .as_bytes() From 078142c43cce4d312bcc506df998600f8c0b8647 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 21:07:10 +0200 Subject: [PATCH 0814/1432] Update dev/Cargo.toml --- README.md | 2 +- dev/Cargo.toml | 6 +++--- release-hook.sh | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4b5029021c..ff111e8ff6 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.2 --locked +cargo install rustlings@6.0.0-beta.3 --locked ``` ### Initialization diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 8da41a56bf..ad39debfa7 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -16,7 +16,7 @@ bin = [ { name = "if1", path = "../exercises/03_if/if1.rs" }, { name = "if2", path = "../exercises/03_if/if2.rs" }, { name = "if3", path = "../exercises/03_if/if3.rs" }, - { name = "quiz1", path = "../exercises/quiz1.rs" }, + { name = "quiz1", path = "../exercises/quizzes/quiz1.rs" }, { name = "primitive_types1", path = "../exercises/04_primitive_types/primitive_types1.rs" }, { name = "primitive_types2", path = "../exercises/04_primitive_types/primitive_types2.rs" }, { name = "primitive_types3", path = "../exercises/04_primitive_types/primitive_types3.rs" }, @@ -47,7 +47,7 @@ bin = [ { name = "hashmaps1", path = "../exercises/11_hashmaps/hashmaps1.rs" }, { name = "hashmaps2", path = "../exercises/11_hashmaps/hashmaps2.rs" }, { name = "hashmaps3", path = "../exercises/11_hashmaps/hashmaps3.rs" }, - { name = "quiz2", path = "../exercises/quiz2.rs" }, + { name = "quiz2", path = "../exercises/quizzes/quiz2.rs" }, { name = "options1", path = "../exercises/12_options/options1.rs" }, { name = "options2", path = "../exercises/12_options/options2.rs" }, { name = "options3", path = "../exercises/12_options/options3.rs" }, @@ -64,7 +64,7 @@ bin = [ { name = "traits3", path = "../exercises/15_traits/traits3.rs" }, { name = "traits4", path = "../exercises/15_traits/traits4.rs" }, { name = "traits5", path = "../exercises/15_traits/traits5.rs" }, - { name = "quiz3", path = "../exercises/quiz3.rs" }, + { name = "quiz3", path = "../exercises/quizzes/quiz3.rs" }, { name = "lifetimes1", path = "../exercises/16_lifetimes/lifetimes1.rs" }, { name = "lifetimes2", path = "../exercises/16_lifetimes/lifetimes2.rs" }, { name = "lifetimes3", path = "../exercises/16_lifetimes/lifetimes3.rs" }, diff --git a/release-hook.sh b/release-hook.sh index 3a2c537662..052832f245 100755 --- a/release-hook.sh +++ b/release-hook.sh @@ -3,6 +3,7 @@ # Error out if any command fails set -e +cargo run -- dev check typos cargo outdated -w --exit-code 1 cargo test --workspace --all-targets From 0d7b0361375fb73f537d672bbecc11181e19e8c9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 21:07:41 +0200 Subject: [PATCH 0815/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6faeea2af0..5767267e8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-beta.2" +version = "6.0.0-beta.3" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.2" +version = "6.0.0-beta.3" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index b3bdaedc39..1bc26c9038 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.2" +version = "6.0.0-beta.3" authors = [ "Liv ", "Mo Bitar ", @@ -55,7 +55,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.2" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.3" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" From e230ffcf03075c64b5bff9570b53ed86605c742e Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 01:19:52 +0200 Subject: [PATCH 0816/1432] Update the contributing guide --- CONTRIBUTING.md | 139 ++++++++++++------------------------------------ 1 file changed, 33 insertions(+), 106 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4fc7fb79ad..c6a2d176d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,129 +1,56 @@ -## Contributing to Rustlings +# Contributing to Rustlings -First off, thanks for taking the time to contribute!! ❀️ +First off, thanks for taking the time to contribute! ❀️ -### Quick Reference +## Quick Reference -I want to... +I want to … -_add an exercise! ➑️ [read this](#addex) and then [open a Pull Request](#prs)_ +- _report a bug!_ ➑️ [open an issue](#issues) +- _fix a bug!_ ➑️ [open a pull request](#pull-requests) +- _implement a new feature!_ ➑️ [open an issue to discuss it first, then a pull request](#issues) +- _add an exercise!_ ➑️ [read this](#adding-an-exercise) +- _update an outdated exercise!_ ➑️ [open a pull request](#pull-requests) -_update an outdated exercise! ➑️ [open a Pull Request](#prs)_ - -_report a bug! ➑️ [open an Issue](#issues)_ - -_fix a bug! ➑️ [open a Pull Request](#prs)_ - -_implement a new feature! ➑️ [open an Issue to discuss it first, then a Pull Request](#issues)_ - - -### Working on the source code - -`rustlings` is basically a glorified `rustc` wrapper. Therefore the source code -isn't really that complicated since the bulk of the work is done by `rustc`. - - -### Adding an exercise - -The first step is to add the exercise! Name the file `exercises/yourTopic/yourTopicN.rs`, make sure to -put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`. - -Next make sure it runs with `rustlings`. The exercise metadata is stored in `info.toml`, under the `exercises` array. The order of the `exercises` array determines the order the exercises are run by `rustlings verify` and `rustlings watch`. - -Add the metadata for your exercise in the correct order in the `exercises` array. If you are unsure of the correct ordering, add it at the bottom and ask in your pull request. The exercise metadata should contain the following: -```diff - ... -+ [[exercises]] -+ name = "yourTopicN" -+ path = "exercises/yourTopic/yourTopicN.rs" -+ mode = "compile" -+ hint = """ -+ Some kind of useful hint for your exercise.""" - ... -``` - -The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`. If you're working on a Clippy exercise, use `mode = "clippy"`. - -That's all! Feel free to put up a pull request. - - -### Issues +## Issues You can open an issue [here](https://github.com/rust-lang/rustlings/issues/new). If you're reporting a bug, please include the output of the following commands: -- `rustc --version` +- `cargo --version` - `rustlings --version` - `ls -la` - Your OS name and version - -### Pull Requests +## Pull Requests -Opening a pull request is as easy as forking the repository and committing your -changes. There's a couple of things to watch out for: +You are welcome to open a pull request, but unless it is small and trivial, **please open an issue to discuss your idea first** πŸ™πŸΌ -#### Write correct commit messages +Opening a pull request is as easy as forking the repository and committing your changes. +If you need any help with it or face any Git related problems, don't hesitate to ask for help πŸ€— -We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) -specification. -This means that you have to format your commit messages in a specific way. Say -you're working on adding a new exercise called `foobar1.rs`. You could write -the following commit message: +It may take time to review your pull request. +Please be patient πŸ˜‡ -``` -feat: add foobar1.rs exercise -``` - -If you're just fixing a bug, please use the `fix` type: +## Adding An Exercise -``` -fix(verify): make sure verify doesn't self-destruct -``` +- Name the file `exercises/yourTopic/yourTopicN.rs`. +- Make sure to put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`. +- Add a (possible) solution at `solutions/yourTopic/yourTopicN.rs` with comments and links explaining it. +- Add the [metadata for your exercise](#exercise-metadata) in the `info.toml` file. +- Make sure your exercise runs with `rustlings run yourTopicN`. +- [Open a pull request](#pull-requests). -The scope within the brackets is optional, but should be any of these: +### Exercise Metadata -- `installation` (for the installation script) -- `cli` (for general CLI changes) -- `verify` (for the verification source file) -- `watch` (for the watch functionality source) -- `run` (for the run functionality source) -- `EXERCISENAME` (if you're changing a specific exercise, or set of exercises, - substitute them here) +The exercise metadata should contain the following: -When the commit also happens to close an existing issue, link it in the message -body: - -``` -fix: update foobar - -closes #101029908 +```toml +[[exercises]] +name = "yourTopicN" +dir = "yourTopic" +hint = """A useful (multi-line) hint for your exercise.""" ``` -If you're doing simple changes, like updating a book link, use `chore`: - -``` -chore: update exercise1.rs book link -``` - -If you're updating documentation, use `docs`: - -``` -docs: add more information to Readme -``` - -If, and only if, you're absolutely sure you want to make a breaking change -(please discuss this beforehand!), add an exclamation mark to the type and -explain the breaking change in the message body: - -``` -fix!: completely change verification - -BREAKING CHANGE: This has to be done because lorem ipsum dolor -``` - -#### Pull Request Workflow - -Once you open a Pull Request, it may be reviewed or labeled (or both) until -the maintainers accept your change. Please be patient, it may take some time -for this to happen! +If your exercise doesn't contain any test, add `test = false` to the exercise metadata. +But adding tests is recommended. From be4dfe8be036e451e8763444e4de9e7428b88f40 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 01:49:36 +0200 Subject: [PATCH 0817/1432] Add hint about updating Rust --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ff111e8ff6..4b473990cd 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ The following command will download and compile Rustlings: cargo install rustlings@6.0.0-beta.3 --locked ``` +If the installation failes, make sure you have the latest Rust version by running `rustup update`. +Otherwise, please [report an issue](https://github.com/rust-lang/rustlings/issues/new). + ### Initialization After installing Rustlings, run the following command to initialize the `rustlings/` directory: From b7289e59aae11b294706b219df614efc2d852b60 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 01:55:44 +0200 Subject: [PATCH 0818/1432] Put --locked in the troubleshooting section --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4b473990cd..840391bffe 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,18 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.3 --locked +cargo install rustlings@6.0.0-beta.3 ``` -If the installation failes, make sure you have the latest Rust version by running `rustup update`. -Otherwise, please [report an issue](https://github.com/rust-lang/rustlings/issues/new). +#### Troubleshooting + +If the installation fails… + + + +- Make sure you have the latest Rust version by running `rustup update`. +- Try adding the `--locked` flag: `cargo install rustlings --locked` +- Otherwise, please [report an issue](https://github.com/rust-lang/rustlings/issues/new). ### Initialization From e63e668d867caa96045c3ce99e876ddcf425418f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 02:00:42 +0200 Subject: [PATCH 0819/1432] Use

--- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 840391bffe..edff62fb1c 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,8 @@ The following command will download and compile Rustlings: cargo install rustlings@6.0.0-beta.3 ``` -#### Troubleshooting - -If the installation fails… +
+πŸ› If the installation fails… @@ -45,6 +44,8 @@ If the installation fails… - Try adding the `--locked` flag: `cargo install rustlings --locked` - Otherwise, please [report an issue](https://github.com/rust-lang/rustlings/issues/new). +
+ ### Initialization After installing Rustlings, run the following command to initialize the `rustlings/` directory: From 29658673381f545a9d54d8b02f85f07ec269fc07 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 02:02:14 +0200 Subject: [PATCH 0820/1432] Add click to expand --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index edff62fb1c..3775e83c0e 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ cargo install rustlings@6.0.0-beta.3 ```
-πŸ› If the installation fails… +If the installation fails πŸ›β€¦ (click to expand) From 74ae5066031d2f8ee848a9394737a0599f145bda Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 03:17:35 +0200 Subject: [PATCH 0821/1432] Update README --- README.md | 73 +++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 3775e83c0e..170b62c65d 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,17 @@ cargo install rustlings@6.0.0-beta.3 ```
-If the installation fails πŸ›β€¦ (click to expand) + + +**If the installation fails…** (_click to expand_) + + - Make sure you have the latest Rust version by running `rustup update`. -- Try adding the `--locked` flag: `cargo install rustlings --locked` -- Otherwise, please [report an issue](https://github.com/rust-lang/rustlings/issues/new). +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.3 --locked` +- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new).
@@ -63,45 +67,48 @@ rustlings ## Doing exercises -The exercises are sorted by topic and can be found in the subdirectory `rustlings/exercises/`. -For every topic there is an additional README file with some resources to get you started on the topic. -We really recommend that you have a look at them before you start. +The exercises are sorted by topic and can be found in the subdirectory `exercises/`. +For every topic, there is an additional `README.md` file with some resources to get you started on the topic. +We highly recommend that you have a look at them before you start πŸ“šοΈ -The task is simple. Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! -Some exercises are also run as tests, but Rustlings handles them all the same. -To run the exercises in the recommended order, execute: +Some exercises contain tests that need to pass for the exercise to be done. + +### Watch Mode + +To run the exercises, launch Rustlings: ```bash rustlings ``` -This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). -It will also rerun automatically every time you change a file in the `exercises/` directory. +This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers). +It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory. -In case you want to go by your own order, or want to only verify a single exercise, you can run: +
+ -```bash -rustlings run EXERCISE_NAME -``` +**If detecting file changes in the `exercises/` directory fails…** (_click to expand_) -Or simply use the following command to run the next pending exercise in the course: + -```bash -rustlings run -``` +You can add the `--manual-run` flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode. -In case you get stuck, you can run the following command to get a hint for your exercise: +Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). -```bash -rustlings hint EXERCISE_NAME -``` +
-You can also get the hint for the next pending exercise with the following command: +### Exercise List -```bash -rustlings hint -``` +In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` or `list` to open the interactive exercise list. + +The list allows you to… + +- See the status of all exercises (done or pending) +- `c`: Continue at another exercise (temporarelly skip some exercises or go back to a previous one) +- `r`: Reset the status and file of an exercise (you need to reload/reopen its file in your editor afterwards) + +See the footer of the list for all possible keys. ## Continuing On @@ -112,15 +119,7 @@ Continue practicing your Rust skills by building your own projects, contributing ## Uninstalling Rustlings -If you want to remove Rustlings from your system, there are two steps. - -1️⃣ Remove the `rustlings` directory that was created by `rustlings init`: - -```bash -rm -r rustlings -``` - -2️⃣ Run `cargo uninstall` to remove the `rustlings` binary: +If you want to remove Rustlings from your system, run the following command: ```bash cargo uninstall rustlings @@ -130,7 +129,7 @@ That's it! ## Contributing -See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md). +See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md) πŸ”— ## Contributors ✨ From 9664f4357c85b6f3384ee697f56f1c6b458b1f0b Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 03:20:34 +0200 Subject: [PATCH 0822/1432] Use HTML in the summary --- README.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 170b62c65d..1c53e4e9ae 100644 --- a/README.md +++ b/README.md @@ -36,11 +36,7 @@ cargo install rustlings@6.0.0-beta.3 ```
- - -**If the installation fails…** (_click to expand_) - - +If the installation fails… (click to expand) @@ -86,11 +82,7 @@ This will start the _watch mode_ which walks you through the exercises in a pred It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory.
- - -**If detecting file changes in the `exercises/` directory fails…** (_click to expand_) - - +If detecting file changes in the exercises/ directory fails… (click to expand) You can add the `--manual-run` flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode. From 2f071c97b05a324cebe5fc91bd941b231597c7e1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 03:25:31 +0200 Subject: [PATCH 0823/1432] Update README.md --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1c53e4e9ae..3fd5d24801 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ After installing Rustlings, run the following command to initialize the `rustlin rustlings init ``` -Now, go into the newly initialized directory and run Rustlings for further instructions on getting started with the exercises: +Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises: ```bash cd rustlings/ @@ -72,11 +72,7 @@ Some exercises contain tests that need to pass for the exercise to be done. ### Watch Mode -To run the exercises, launch Rustlings: - -```bash -rustlings -``` +After [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`. This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers). It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory. From 37fcbeb596fa03e874aab9601e88425c95ccd598 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 03:29:05 +0200 Subject: [PATCH 0824/1432] Add indentation for details --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3fd5d24801..b407f053e9 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,9 @@ It will rerun the current exercise automatically every time you change the exerc
If detecting file changes in the exercises/ directory fails… (click to expand) -You can add the `--manual-run` flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode. - -Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). +> You can add the `--manual-run` flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode. +> +> Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL).
From 0ce5d9d4d7a58316e1864d2ae1abf8f89cb20c76 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 03:39:38 +0200 Subject: [PATCH 0825/1432] Update README.md --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b407f053e9..81585065d2 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ cargo install rustlings@6.0.0-beta.3 -- Make sure you have the latest Rust version by running `rustup update`. +- Make sure you have the latest Rust version by running `rustup update` - Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.3 --locked` -- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new). +- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
@@ -80,7 +80,7 @@ It will rerun the current exercise automatically every time you change the exerc
If detecting file changes in the exercises/ directory fails… (click to expand) -> You can add the `--manual-run` flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode. +> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode. > > Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). @@ -93,8 +93,8 @@ In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l The list allows you to… - See the status of all exercises (done or pending) -- `c`: Continue at another exercise (temporarelly skip some exercises or go back to a previous one) -- `r`: Reset the status and file of an exercise (you need to reload/reopen its file in your editor afterwards) +- `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one) +- `r`: Reset status and file of an exercise (you need to _reload/reopen_ its file in your editor afterwards) See the footer of the list for all possible keys. @@ -113,8 +113,6 @@ If you want to remove Rustlings from your system, run the following command: cargo uninstall rustlings ``` -That's it! - ## Contributing See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md) πŸ”— From dc5c72bc19951313e80f038961fb446bd6ea02f5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 26 Apr 2024 03:44:16 +0200 Subject: [PATCH 0826/1432] Update README.md --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 81585065d2..4edbaeffe8 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,12 @@ Before installing Rustlings, you need to have _Rust installed_. Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. This'll also install _Cargo_, Rust's package/project manager. -🐧 If you're on Linux, make sure you've installed `gcc` (for a linker). Deb: `sudo apt install build-essential gcc`. Dnf: `sudo dnf install gcc`. +> 🐧 If you're on Linux, make sure you've installed `gcc` (for a linker). +> +> Deb: `sudo apt install gcc`. +> Dnf: `sudo dnf install gcc`. -🍎 If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`. +> 🍎 If you're on MacOS, make sure you've installed Xcode and its developer tools by running `xcode-select --install`. ### Installing Rustlings @@ -68,7 +71,7 @@ For every topic, there is an additional `README.md` file with some resources to We highly recommend that you have a look at them before you start πŸ“šοΈ Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! -Some exercises contain tests that need to pass for the exercise to be done. +Some exercises contain tests that need to pass for the exercise to be done βœ… ### Watch Mode From c82c3673245ca11d455b067c97fadda4a8406cb9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:14:59 +0200 Subject: [PATCH 0827/1432] Respect the target-dir config and show tests' output --- Cargo.lock | 69 ++++----------------- Cargo.toml | 2 +- src/app_state.rs | 12 +++- src/cmd.rs | 70 ++++++++++++++++++++++ src/exercise.rs | 145 +++++++++++++++++---------------------------- src/main.rs | 29 +++++++-- src/run.rs | 2 +- src/watch/state.rs | 5 +- 8 files changed, 176 insertions(+), 158 deletions(-) create mode 100644 src/cmd.rs diff --git a/Cargo.lock b/Cargo.lock index 5767267e8d..f9b48bd051 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,16 +271,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "filetime" version = "0.2.23" @@ -333,15 +323,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "indexmap" version = "2.2.6" @@ -419,12 +400,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - [[package]] name = "lock_api" version = "0.4.11" @@ -664,19 +639,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "rustlings" version = "6.0.0-beta.3" @@ -692,8 +654,8 @@ dependencies = [ "ratatui", "rustlings-macros", "serde", + "serde_json", "toml_edit", - "which", ] [[package]] @@ -752,6 +714,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.5" @@ -935,18 +908,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "which" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" -dependencies = [ - "either", - "home", - "rustix", - "winsafe", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1126,12 +1087,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 1bc26c9038..a77c84f6c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,9 +56,9 @@ notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.3" } +serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true -which = "6.0.1" [dev-dependencies] assert_cmd = "2.0.14" diff --git a/src/app_state.rs b/src/app_state.rs index 476b5a9cb7..b980bdbaa9 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -7,7 +7,7 @@ use crossterm::{ use std::{ fs::{self, File}, io::{Read, StdoutLock, Write}, - path::Path, + path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -39,6 +39,7 @@ pub struct AppState { final_message: String, file_buf: Vec, official_exercises: bool, + target_dir: PathBuf, } impl AppState { @@ -90,6 +91,7 @@ impl AppState { pub fn new( exercise_infos: Vec, final_message: String, + target_dir: PathBuf, ) -> (Self, StateFileStatus) { let exercises = exercise_infos .into_iter() @@ -127,6 +129,7 @@ impl AppState { final_message, file_buf: Vec::with_capacity(2048), official_exercises: !Path::new("info.toml").exists(), + target_dir, }; let state_file_status = slf.update_from_file(); @@ -154,6 +157,11 @@ impl AppState { &self.exercises[self.current_exercise_ind] } + #[inline] + pub fn target_dir(&self) -> &Path { + &self.target_dir + } + pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { if ind >= self.exercises.len() { bail!(BAD_INDEX_ERR); @@ -313,7 +321,7 @@ impl AppState { write!(writer, "Running {exercise} ... ")?; writer.flush()?; - let success = exercise.run(&mut output)?; + let success = exercise.run(&mut output, &self.target_dir)?; if !success { writeln!(writer, "{}\n", "FAILED".red())?; diff --git a/src/cmd.rs b/src/cmd.rs new file mode 100644 index 0000000000..28f21c555f --- /dev/null +++ b/src/cmd.rs @@ -0,0 +1,70 @@ +use anyhow::{Context, Result}; +use std::{io::Read, path::Path, process::Command}; + +pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec) -> Result { + let (mut reader, writer) = os_pipe::pipe() + .with_context(|| format!("Failed to create a pipe to run the command `{description}``"))?; + + let writer_clone = writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{description}`") + })?; + + let mut handle = cmd + .stdout(writer_clone) + .stderr(writer) + .spawn() + .with_context(|| format!("Failed to run the command `{description}`"))?; + + // Prevent pipe deadlock. + drop(cmd); + + reader + .read_to_end(output) + .with_context(|| format!("Failed to read the output of the command `{description}`"))?; + + output.push(b'\n'); + + handle + .wait() + .with_context(|| format!("Failed to wait on the command `{description}` to exit")) + .map(|status| status.success()) +} + +pub struct CargoCmd<'a> { + pub subcommand: &'a str, + pub args: &'a [&'a str], + pub exercise_name: &'a str, + pub description: &'a str, + pub hide_warnings: bool, + pub target_dir: &'a Path, + pub output: &'a mut Vec, + pub dev: bool, +} + +impl<'a> CargoCmd<'a> { + pub fn run(&mut self) -> Result { + let mut cmd = Command::new("cargo"); + cmd.arg(self.subcommand); + + // A hack to make `cargo run` work when developing Rustlings. + if self.dev { + cmd.arg("--manifest-path") + .arg("dev/Cargo.toml") + .arg("--target-dir") + .arg(self.target_dir); + } + + cmd.arg("--color") + .arg("always") + .arg("-q") + .arg("--bin") + .arg(self.exercise_name) + .args(self.args); + + if self.hide_warnings { + cmd.env("RUSTFLAGS", "-A warnings"); + } + + run_cmd(cmd, self.description, self.output) + } +} diff --git a/src/exercise.rs b/src/exercise.rs index 50f360e90d..23dae6f5f5 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,57 +1,21 @@ -use anyhow::{Context, Result}; +use anyhow::Result; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, - io::{Read, Write}, - process::{Command, Stdio}, + io::Write, + path::{Path, PathBuf}, + process::Command, }; -use crate::{in_official_repo, terminal_link::TerminalFileLink, DEBUG_PROFILE}; +use crate::{ + cmd::{run_cmd, CargoCmd}, + in_official_repo, + terminal_link::TerminalFileLink, + DEBUG_PROFILE, +}; pub const OUTPUT_CAPACITY: usize = 1 << 14; -fn run_command( - mut cmd: Command, - cmd_description: &str, - output: &mut Vec, - stderr: bool, -) -> Result { - let (mut reader, writer) = os_pipe::pipe().with_context(|| { - format!("Failed to create a pipe to run the command `{cmd_description}``") - })?; - - let (stdout, stderr) = if stderr { - ( - Stdio::from(writer.try_clone().with_context(|| { - format!("Failed to clone the pipe writer for the command `{cmd_description}`") - })?), - Stdio::from(writer), - ) - } else { - (Stdio::from(writer), Stdio::null()) - }; - - let mut handle = cmd - .stdout(stdout) - .stderr(stderr) - .spawn() - .with_context(|| format!("Failed to run the command `{cmd_description}`"))?; - - // Prevent pipe deadlock. - drop(cmd); - - reader - .read_to_end(output) - .with_context(|| format!("Failed to read the output of the command `{cmd_description}`"))?; - - output.push(b'\n'); - - handle - .wait() - .with_context(|| format!("Failed to wait on the command `{cmd_description}` to exit")) - .map(|status| status.success()) -} - pub struct Exercise { pub dir: Option<&'static str>, // Exercise's unique name @@ -66,11 +30,16 @@ pub struct Exercise { } impl Exercise { - fn run_bin(&self, output: &mut Vec) -> Result { + fn run_bin(&self, output: &mut Vec, target_dir: &Path) -> Result { writeln!(output, "{}", "Output".underlined())?; - let bin_path = format!("target/debug/{}", self.name); - let success = run_command(Command::new(&bin_path), &bin_path, output, true)?; + let mut bin_path = + PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + self.name.len()); + bin_path.push(target_dir); + bin_path.push("debug"); + bin_path.push(self.name); + + let success = run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output)?; if !success { writeln!( @@ -85,43 +54,23 @@ impl Exercise { Ok(success) } - fn cargo_cmd( - &self, - command: &str, - args: &[&str], - cmd_description: &str, - output: &mut Vec, - dev: bool, - stderr: bool, - ) -> Result { - let mut cmd = Command::new("cargo"); - cmd.arg(command); - - // A hack to make `cargo run` work when developing Rustlings. - if dev { - cmd.arg("--manifest-path") - .arg("dev/Cargo.toml") - .arg("--target-dir") - .arg("target"); - } - - cmd.arg("--color") - .arg("always") - .arg("-q") - .arg("--bin") - .arg(self.name) - .args(args); - - run_command(cmd, cmd_description, output, stderr) - } - - pub fn run(&self, output: &mut Vec) -> Result { + pub fn run(&self, output: &mut Vec, target_dir: &Path) -> Result { output.clear(); // Developing the official Rustlings. let dev = DEBUG_PROFILE && in_official_repo(); - let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev, true)?; + let build_success = CargoCmd { + subcommand: "build", + args: &[], + exercise_name: self.name, + description: "cargo build …", + hide_warnings: false, + target_dir, + output, + dev, + } + .run()?; if !build_success { return Ok(false); } @@ -134,19 +83,28 @@ impl Exercise { } else { &["--profile", "test"] }; - let clippy_success = - self.cargo_cmd("clippy", clippy_args, "cargo clippy …", output, dev, true)?; + let clippy_success = CargoCmd { + subcommand: "clippy", + args: clippy_args, + exercise_name: self.name, + description: "cargo clippy …", + hide_warnings: false, + target_dir, + output, + dev, + } + .run()?; if !clippy_success { return Ok(false); } if !self.test { - return self.run_bin(output); + return self.run_bin(output, target_dir); } - let test_success = self.cargo_cmd( - "test", - &[ + let test_success = CargoCmd { + subcommand: "test", + args: &[ "--", "--color", "always", @@ -154,14 +112,17 @@ impl Exercise { "--format", "pretty", ], - "cargo test …", + exercise_name: self.name, + description: "cargo test …", + // Hide warnings because they are shown by Clippy. + hide_warnings: true, + target_dir, output, dev, - // Hide warnings because they are shown by Clippy. - false, - )?; + } + .run()?; - let run_success = self.run_bin(output)?; + let run_success = self.run_bin(output, target_dir)?; Ok(test_success && run_success) } diff --git a/src/main.rs b/src/main.rs index 7a142fdb63..b03aa5208d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,16 +5,18 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; +use serde::Deserialize; use std::{ io::{self, BufRead, Write}, - path::Path, - process::exit, + path::{Path, PathBuf}, + process::{exit, Command, Stdio}, }; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; mod app_state; mod cargo_toml; +mod cmd; mod dev; mod embedded; mod exercise; @@ -75,6 +77,11 @@ enum Subcommands { Dev(DevCommands), } +#[derive(Deserialize)] +struct CargoMetadata { + target_directory: PathBuf, +} + fn in_official_repo() -> bool { Path::new("dev/rustlings-repo.txt").exists() } @@ -86,7 +93,20 @@ fn main() -> Result<()> { bail!("{OLD_METHOD_ERR}"); } - which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; + let metadata_output = Command::new("cargo") + .arg("metadata") + .arg("-q") + .arg("--format-version") + .arg("1") + .arg("--no-deps") + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output() + .context(CARGO_METADATA_ERR)? + .stdout; + let target_dir = serde_json::de::from_slice::(&metadata_output) + .context("Failed to read the field `target_directory` from the `cargo metadata` output")? + .target_directory; match args.command { Some(Subcommands::Init) => { @@ -122,6 +142,7 @@ fn main() -> Result<()> { let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), + target_dir, ); if let Some(welcome_message) = info_file.welcome_message { @@ -198,7 +219,7 @@ The new method doesn't include cloning the Rustlings' repository. Please follow the instructions in the README: https://github.com/rust-lang/rustlings#getting-started"; -const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`. +const CARGO_METADATA_ERR: &str = "Failed to run the command `cargo metadata …` Did you already install Rust? Try running `cargo --version` to diagnose the problem."; diff --git a/src/run.rs b/src/run.rs index cbc9ad708c..9b5ddd3473 100644 --- a/src/run.rs +++ b/src/run.rs @@ -11,7 +11,7 @@ use crate::{ pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - let success = exercise.run(&mut output)?; + let success = exercise.run(&mut output, app_state.target_dir())?; let mut stdout = io::stdout().lock(); stdout.write_all(&output)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 40c01bfcb9..82b745a4c9 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -50,7 +50,10 @@ impl<'a> WatchState<'a> { pub fn run_current_exercise(&mut self) -> Result<()> { self.show_hint = false; - let success = self.app_state.current_exercise().run(&mut self.output)?; + let success = self + .app_state + .current_exercise() + .run(&mut self.output, self.app_state.target_dir())?; if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { From 2150d629b18b3ba2ccd05606e69dc8d171df1027 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:15:16 +0200 Subject: [PATCH 0828/1432] Use --show-output instead of --nocapture --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 23dae6f5f5..b62958b984 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -108,7 +108,7 @@ impl Exercise { "--", "--color", "always", - "--nocapture", + "--show-output", "--format", "pretty", ], From cb7ce006b54089fd46f36304ac5a143680c15a2a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:17:10 +0200 Subject: [PATCH 0829/1432] Bump version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4edbaeffe8..9747690c48 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.3 +cargo install rustlings@6.0.0-beta.4 ```
@@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.3 - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.3 --locked` +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.4 --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
From 181c81f0165b71b5eda5e35c04849d9ac30d0261 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:17:24 +0200 Subject: [PATCH 0830/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9b48bd051..e142dfc1f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,7 +641,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.3" +version = "6.0.0-beta.4" dependencies = [ "anyhow", "assert_cmd", @@ -660,7 +660,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.3" +version = "6.0.0-beta.4" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index a77c84f6c8..7e526080eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.3" +version = "6.0.0-beta.4" authors = [ "Liv ", "Mo Bitar ", @@ -55,7 +55,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.3" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.4" } serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true From c3a92b1248201e4d09b21ba2ff0791b1375f83f0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:21:29 +0200 Subject: [PATCH 0831/1432] Update deps --- Cargo.lock | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e142dfc1f0..0a35aa2dc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,7 +279,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -402,9 +402,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -516,15 +516,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -610,6 +610,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "regex" version = "1.10.4" @@ -867,9 +876,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "utf8parse" @@ -1080,9 +1089,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] From 12504b01e910cd9066a9d8a6c41896470033b0c2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:32:06 +0200 Subject: [PATCH 0832/1432] Disable unneeded features in deps --- Cargo.lock | 1 - Cargo.toml | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a35aa2dc2..6a47c314f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,7 +474,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43" dependencies = [ - "crossbeam-channel", "log", "notify", ] diff --git a/Cargo.toml b/Cargo.toml index 7e526080eb..38ebb39187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ toml_edit = { version = "0.22.12", default-features = false, features = ["parse" [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" -default-run = "rustlings" version.workspace = true authors.workspace = true repository.workspace = true @@ -52,9 +51,9 @@ 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" +notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" -ratatui = "0.26.2" +ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.4" } serde_json = "1.0.116" serde.workspace = true From cdeb8ce2292d5968e3866fd96cc422756f5a0ff4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 17:31:51 +0200 Subject: [PATCH 0833/1432] Fix initialization --- src/app_state.rs | 32 +++++++++++++++++++++++++++++--- src/main.rs | 32 +++----------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index b980bdbaa9..7683c142da 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -4,6 +4,7 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; +use serde::Deserialize; use std::{ fs::{self, File}, io::{Read, StdoutLock, Write}, @@ -32,6 +33,11 @@ pub enum StateFileStatus { NotRead, } +#[derive(Deserialize)] +struct CargoMetadata { + target_directory: PathBuf, +} + pub struct AppState { current_exercise_ind: usize, exercises: Vec, @@ -91,8 +97,24 @@ impl AppState { pub fn new( exercise_infos: Vec, final_message: String, - target_dir: PathBuf, - ) -> (Self, StateFileStatus) { + ) -> Result<(Self, StateFileStatus)> { + let metadata_output = Command::new("cargo") + .arg("metadata") + .arg("-q") + .arg("--format-version") + .arg("1") + .arg("--no-deps") + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output() + .context(CARGO_METADATA_ERR)? + .stdout; + let target_dir = serde_json::de::from_slice::(&metadata_output) + .context( + "Failed to read the field `target_directory` from the `cargo metadata` output", + )? + .target_directory; + let exercises = exercise_infos .into_iter() .map(|mut exercise_info| { @@ -134,7 +156,7 @@ impl AppState { let state_file_status = slf.update_from_file(); - (slf, state_file_status) + Ok((slf, state_file_status)) } #[inline] @@ -388,6 +410,10 @@ impl AppState { } } +const CARGO_METADATA_ERR: &str = "Failed to run the command `cargo metadata …` +Did you already install Rust? +Try running `cargo --version` to diagnose the problem."; + const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" All exercises seem to be done. Recompiling and running all exercises to make sure that all of them are actually done. diff --git a/src/main.rs b/src/main.rs index b03aa5208d..bb70a75783 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,10 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; -use serde::Deserialize; use std::{ io::{self, BufRead, Write}, - path::{Path, PathBuf}, - process::{exit, Command, Stdio}, + path::Path, + process::exit, }; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; @@ -77,11 +76,6 @@ enum Subcommands { Dev(DevCommands), } -#[derive(Deserialize)] -struct CargoMetadata { - target_directory: PathBuf, -} - fn in_official_repo() -> bool { Path::new("dev/rustlings-repo.txt").exists() } @@ -93,21 +87,6 @@ fn main() -> Result<()> { bail!("{OLD_METHOD_ERR}"); } - let metadata_output = Command::new("cargo") - .arg("metadata") - .arg("-q") - .arg("--format-version") - .arg("1") - .arg("--no-deps") - .stdin(Stdio::null()) - .stderr(Stdio::inherit()) - .output() - .context(CARGO_METADATA_ERR)? - .stdout; - let target_dir = serde_json::de::from_slice::(&metadata_output) - .context("Failed to read the field `target_directory` from the `cargo metadata` output")? - .target_directory; - match args.command { Some(Subcommands::Init) => { if DEBUG_PROFILE { @@ -142,8 +121,7 @@ fn main() -> Result<()> { let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), - target_dir, - ); + )?; if let Some(welcome_message) = info_file.welcome_message { match state_file_status { @@ -219,10 +197,6 @@ The new method doesn't include cloning the Rustlings' repository. Please follow the instructions in the README: https://github.com/rust-lang/rustlings#getting-started"; -const CARGO_METADATA_ERR: &str = "Failed to run the command `cargo metadata …` -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. From 016e6a014ee77d13ce5d7fc4e1a140276143c4b2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 17:32:42 +0200 Subject: [PATCH 0834/1432] Update serde --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a47c314f8..6cf6a305ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,18 +704,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 38ebb39187..e430cf13fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" edition = "2021" [workspace.dependencies] -serde = { version = "1.0.198", features = ["derive"] } +serde = { version = "1.0.199", features = ["derive"] } toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } [package] From edea76b5b9f2086b2a885c119cc56b04753f7eb5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 17:34:39 +0200 Subject: [PATCH 0835/1432] Bump version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9747690c48..2e513dcb9f 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.4 +cargo install rustlings@6.0.0-beta.5 ```
@@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.4 - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.4 --locked` +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.5 --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
From 89e0f642791cf9e66143c0904501333cefbb8a61 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 17:35:08 +0200 Subject: [PATCH 0836/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cf6a305ec..1c5463ff4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,7 +649,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.4" +version = "6.0.0-beta.5" dependencies = [ "anyhow", "assert_cmd", @@ -668,7 +668,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.4" +version = "6.0.0-beta.5" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index e430cf13fe..41ad49c33a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.4" +version = "6.0.0-beta.5" authors = [ "Liv ", "Mo Bitar ", @@ -54,7 +54,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.4" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.5" } serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true From 5658998c0c27d88e2129e52fec0aba5cacf26c30 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:24:09 +0200 Subject: [PATCH 0837/1432] Update welcome and final messages --- info.toml | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/info.toml b/info.toml index 14944728e5..a0248b5d44 100644 --- a/info.toml +++ b/info.toml @@ -1,32 +1,27 @@ 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: +welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners! +We are going to teach you a lot of things about Rust, but before we can +get started, here are some notes about how Rustlings operates: 1. The central concept behind Rustlings is that you solve exercises. These - exercises usually have some sort of syntax error in them, which will cause - them to fail compilation or testing. Sometimes there's a logic error instead - of a syntax error. No matter what error, it's your job to find it and fix it! - You'll know when you fixed it because then, the exercise will compile and - Rustlings will be able to move on to the next exercise. -2. If you run Rustlings in watch mode (which we recommend), it'll automatically - start with the first exercise. Don't get confused by an error message popping - up as soon as you run Rustlings! This is part of the exercise that you're - supposed to solve, so open the exercise file in an editor and start your - detective work! -3. If you're stuck on an exercise, there is a helpful hint you can view by typing - 'hint' (in watch mode), or running `rustlings hint exercise_name`. + exercises usually contain some syntax or logic errors which cause the + exercise to fail compilation or testing. It's your job to find all errors + and fix them! +2. Make sure to have your editor open in the `rustlings/` directory. Rustlings + will show you the path of the current exercise under the progress bar. Open + the exercise file in your editor, fix errors and save the file. Rustlings will + automatically detect the file change and rerun the exercise. If all errors are + fixed, Rustlings will ask you to move on to the next exercise. +3. If you're stuck on an exercise, you can request a hint by typing `h` or `hint` + followed by ENTER. 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, - and sometimes, other learners do too so you can help each other out! - -Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. -Make sure to have your editor open in the `rustlings` directory! + (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, + other learners do too so you can help each other out! """ final_message = """We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, please don't hesitate to report them to our repo. +If you noticed any issues, don't hesitate to report them on Github. You can also contribute your own exercises to help the greater community! Before reporting an issue or contributing, please read our guidelines: From de0befef9c780f9539458b21582f39f843d0bbc3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:37:17 +0200 Subject: [PATCH 0838/1432] Update intro1 --- exercises/00_intro/intro1.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 7000039295..62bf95f1cc 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,11 +1,10 @@ // We sometimes encourage you to keep trying things on a given exercise, even // after you already figured it out. If you got everything working and feel -// ready for the next exercise, remove the `I AM NOT DONE` comment below. +// ready for the next exercise, enter `n` (or `next`) in the terminal. // -// If you're running this using `rustlings watch`: The exercise file will be -// reloaded when you change one of the lines below! Try adding a `println!` -// line, or try changing what it outputs in your terminal. Try removing a -// semicolon and see what happens! +// The exercise file will be reloaded when you change one of the lines below! +// Try adding a new `println!`. +// Try removing a semicolon and see what happens in the terminal! fn main() { println!("Hello and"); @@ -22,5 +21,6 @@ fn main() { println!("solve the exercises. Good luck!"); println!(); println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!"); - println!("The current exercise path is shown under the progress bar in the watch mode."); + println!("The current exercise path will be always shown under the progress bar."); + println!("You can click on the path to open the exercise file in your editor."); } From 62a2c1a6d9e88c1f70bd9cbf2c174318eaac4677 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:37:44 +0200 Subject: [PATCH 0839/1432] Put long version in () --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e513dcb9f..1c145b938b 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ It will rerun the current exercise automatically every time you change the exerc
If detecting file changes in the exercises/ directory fails… (click to expand) -> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode. +> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` (or `run`) in the watch mode. > > Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). @@ -91,7 +91,7 @@ It will rerun the current exercise automatically every time you change the exerc ### Exercise List -In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` or `list` to open the interactive exercise list. +In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` (or `list`) to open the interactive exercise list. The list allows you to… From ee2b772dd5f84168d12e55c45330c777092948a6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:38:05 +0200 Subject: [PATCH 0840/1432] Update intro1 hint --- info.toml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/info.toml b/info.toml index a0248b5d44..4204f27433 100644 --- a/info.toml +++ b/info.toml @@ -5,7 +5,7 @@ We are going to teach you a lot of things about Rust, but before we can get started, here are some notes about how Rustlings operates: 1. The central concept behind Rustlings is that you solve exercises. These - exercises usually contain some syntax or logic errors which cause the + exercises usually contain some compiler or logic errors which cause the exercise to fail compilation or testing. It's your job to find all errors and fix them! 2. Make sure to have your editor open in the `rustlings/` directory. Rustlings @@ -13,8 +13,7 @@ get started, here are some notes about how Rustlings operates: the exercise file in your editor, fix errors and save the file. Rustlings will automatically detect the file change and rerun the exercise. If all errors are fixed, Rustlings will ask you to move on to the next exercise. -3. If you're stuck on an exercise, you can request a hint by typing `h` or `hint` - followed by ENTER. +3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, other learners do too so you can help each other out! @@ -36,9 +35,7 @@ name = "intro1" dir = "00_intro" test = false # TODO: Fix hint -hint = """ -Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file -to move on to the next exercise.""" +hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" [[exercises]] name = "intro2" From ea40804371d073730e7b5f6258d2a825c544c0b1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:38:26 +0200 Subject: [PATCH 0841/1432] Put long version in () --- src/watch.rs | 2 +- src/watch/state.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 5c3f1709b8..453d9a43d0 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -124,5 +124,5 @@ The automatic detection of exercise file changes failed :( Please try running `rustlings` again. If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher. -You need to manually trigger running the current exercise using `r` or `run` then. +You need to manually trigger running the current exercise using `r` (or `run`) then. "; diff --git a/src/watch/state.rs b/src/watch/state.rs index 82b745a4c9..e5364c3004 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -130,7 +130,7 @@ impl<'a> WatchState<'a> { self.writer, "{}\n", "Exercise done βœ“ -When you are done experimenting, enter `n` or `next` to go to the next exercise πŸ¦€" +When you are done experimenting, enter `n` (or `next`) to move on to the next exercise πŸ¦€" .bold() .green(), )?; From c45d2c3255b6b606debfd16ef135f64f31f604d7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:38:38 +0200 Subject: [PATCH 0842/1432] Remove the I AM NOT DONE check --- tests/integration_tests.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f81cc94b14..7d30467b1f 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,5 +1,4 @@ use assert_cmd::prelude::*; -use predicates::boolean::PredicateBooleanExt; use std::process::Command; #[test] @@ -110,8 +109,7 @@ fn run_compile_exercise_does_not_prompt() { .args(["run", "pending_exercise"]) .current_dir("tests/fixture/state") .assert() - .code(0) - .stdout(predicates::str::contains("I AM NOT DONE").not()); + .code(0); } #[test] @@ -121,8 +119,7 @@ fn run_test_exercise_does_not_prompt() { .args(["run", "pending_test_exercise"]) .current_dir("tests/fixture/state") .assert() - .code(0) - .stdout(predicates::str::contains("I AM NOT DONE").not()); + .code(0); } #[test] From 75e2804c8369f2414318f58573444fa8e49d03f2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:42:09 +0200 Subject: [PATCH 0843/1432] Esacpe the list with ESC --- src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index 790c02fe4e..40a069ab4a 100644 --- a/src/list.rs +++ b/src/list.rs @@ -42,7 +42,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> { ui_state.message.clear(); match key.code { - KeyCode::Char('q') => break, + KeyCode::Esc | KeyCode::Char('q') => break, KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(), KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), From aedeff8b243bad9205b84a657789b59928bf6524 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:45:26 +0200 Subject: [PATCH 0844/1432] Reorder the footer keys --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index 19a77fe03a..77f0936768 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -194,7 +194,7 @@ impl<'a> UiState<'a> { let message = if self.message.is_empty() { // Help footer. Span::raw( - "↓/j ↑/k home/g end/G β”‚ filter one/

ending β”‚ eset β”‚ ontinue at β”‚ uit", + "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset β”‚ filter one/

ending β”‚ uit", ) } else { self.message.as_str().light_blue() From 881d3e9441507a4f615699d1cd77f4d989d20872 Mon Sep 17 00:00:00 2001 From: allupeng Date: Sun, 28 Apr 2024 18:03:22 +0800 Subject: [PATCH 0845/1432] doc : add a dot in structs3.rs file --- exercises/07_structs/structs3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 7cda5af103..2d55dd7e63 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,7 +1,7 @@ // 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. +// 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 From 1508938fed4e3800dcf45c807f67e87ebe8ca30b Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 28 Apr 2024 23:21:13 +0200 Subject: [PATCH 0846/1432] Highlight the active filter --- src/list/state.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 77f0936768..0f2a1c82a2 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use ratatui::{ layout::{Constraint, Rect}, style::{Style, Stylize}, - text::Span, + text::{Line, Span}, widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, Frame, }; @@ -193,11 +193,25 @@ impl<'a> UiState<'a> { let message = if self.message.is_empty() { // Help footer. - Span::raw( - "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset β”‚ filter one/

ending β”‚ uit", - ) + let mut spans = Vec::with_capacity(4); + spans.push(Span::raw( + "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset β”‚ filter ", + )); + match self.filter { + Filter::Done => { + spans.push("one".underlined().magenta()); + spans.push(Span::raw("/

ending")); + } + Filter::Pending => { + spans.push(Span::raw("one/")); + spans.push("

ending".underlined().magenta()); + } + Filter::None => spans.push(Span::raw("one/

ending")), + } + spans.push(Span::raw(" β”‚ uit")); + Line::from(spans) } else { - self.message.as_str().light_blue() + Line::from(self.message.as_str().light_blue()) }; frame.render_widget( message, From 593f0e0916dab5d600d50208ba226786968026c3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 28 Apr 2024 23:22:11 +0200 Subject: [PATCH 0847/1432] Revert escaping with ESC in list to be able to clear the message --- src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index 40a069ab4a..790c02fe4e 100644 --- a/src/list.rs +++ b/src/list.rs @@ -42,7 +42,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> { ui_state.message.clear(); match key.code { - KeyCode::Esc | KeyCode::Char('q') => break, + KeyCode::Char('q') => break, KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(), KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), From 3c7e7368b20f7c5c4b3b561b9fef8e0182280878 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 28 Apr 2024 23:25:44 +0200 Subject: [PATCH 0848/1432] Add solutions to the initialized .gitignore --- src/init.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/init.rs b/src/init.rs index 8a9fb36554..cb3a6bc6f8 100644 --- a/src/init.rs +++ b/src/init.rs @@ -69,6 +69,7 @@ pub fn init() -> Result<()> { } const GITIGNORE: &[u8] = b".rustlings-state.txt +solutions Cargo.lock target .vscode From 8c60ac267e4483b952407dd5875475242438b42e Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 29 Apr 2024 00:26:53 +0200 Subject: [PATCH 0849/1432] Add working environment section --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 1c145b938b..52848fe511 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,21 @@ cd rustlings/ rustlings ``` +## Working environment + +### Editor + +Our general recommendation is [VS Code](https://code.visualstudio.com/) with the [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). +But any editor that supports [rust-analyzer](https://rust-analyzer.github.io/) should be enough for working on the exercises. + +### Terminal + +While working with Rustlings, please use a modern terminal for the best user experience. +The default terminal on Linux and Mac should be sufficient. +On Windows, we recommend the [Windows Terminal](https://aka.ms/terminal). + +If you use VS Code, the builtin terminal should also be fine. + ## Doing exercises The exercises are sorted by topic and can be found in the subdirectory `exercises/`. From 196d3c1a9820acc9c58134f62a08ec3671411777 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 29 Apr 2024 00:36:13 +0200 Subject: [PATCH 0850/1432] Bump version --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- README.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c5463ff4a..597ee06790 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", diff --git a/Cargo.toml b/Cargo.toml index 41ad49c33a..2054b770f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ include = [ anyhow = "1.0.82" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" -hashbrown = "0.14.3" +hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } diff --git a/README.md b/README.md index 52848fe511..3ba080f552 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.5 +cargo install rustlings@6.0.0-beta.6 ```

@@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.5 - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.5 --locked` +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.6 --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
From 7f73219041fc6659d2e8e944c4e1d0341e323478 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 29 Apr 2024 00:36:50 +0200 Subject: [PATCH 0851/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 597ee06790..271d09a4a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,7 +649,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.5" +version = "6.0.0-beta.6" dependencies = [ "anyhow", "assert_cmd", @@ -668,7 +668,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.5" +version = "6.0.0-beta.6" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 2054b770f4..efb15127df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.5" +version = "6.0.0-beta.6" authors = [ "Liv ", "Mo Bitar ", @@ -54,7 +54,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.5" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.6" } serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true From 8c3b8dcec47ae1ab08d88eaa4df522b4c30e14cc Mon Sep 17 00:00:00 2001 From: allupeng Date: Mon, 29 Apr 2024 14:18:04 +0800 Subject: [PATCH 0852/1432] doc : add a dot in hashmaps1.rs file to fill e.g. --- exercises/11_hashmaps/hashmaps1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index 80829eaa6c..02f4725ec7 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -3,7 +3,7 @@ // 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 -// types of fruits (e.g apple, banana, mango) in the basket and the total count +// types of fruits (e.g. apple, banana, mango) in the basket and the total count // of all the fruits should be at least five. // // Make me compile and pass the tests! From b6f40f2ec86abc70e7b8548996c948f6c5563f46 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 29 Apr 2024 17:01:47 +0200 Subject: [PATCH 0853/1432] Document main and app_state --- src/app_state.rs | 131 ++++++++++++++++++++++++++--------------------- src/dev.rs | 2 +- src/main.rs | 12 +++-- 3 files changed, 81 insertions(+), 64 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 7683c142da..9d12c9365f 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -33,6 +33,7 @@ pub enum StateFileStatus { NotRead, } +// Parses parts of the output of `cargo metadata`. #[derive(Deserialize)] struct CargoMetadata { target_directory: PathBuf, @@ -41,14 +42,18 @@ struct CargoMetadata { pub struct AppState { current_exercise_ind: usize, exercises: Vec, + // Caches the number of done exercises to avoid iterating over all exercises every time. n_done: u16, final_message: String, + // Preallocated buffer for reading and writing the state file. file_buf: Vec, official_exercises: bool, + // Cargo's target directory. target_dir: PathBuf, } impl AppState { + // Update the app state from the state file. fn update_from_file(&mut self) -> StateFileStatus { self.file_buf.clear(); self.n_done = 0; @@ -98,6 +103,7 @@ impl AppState { exercise_infos: Vec, final_message: String, ) -> Result<(Self, StateFileStatus)> { + // Get the target directory from Cargo. let metadata_output = Command::new("cargo") .arg("metadata") .arg("-q") @@ -115,6 +121,7 @@ impl AppState { )? .target_directory; + // Build exercises from their metadata in the info file. let exercises = exercise_infos .into_iter() .map(|mut exercise_info| { @@ -184,6 +191,36 @@ impl AppState { &self.target_dir } + // Write the state file. + // The file's format is very simple: + // - The first line is a comment. + // - The second line is an empty line. + // - The third line is the name of the current exercise. It must end with `\n` even if there + // are no done exercises. + // - The fourth line is an empty line. + // - All remaining lines are the names of done exercises. + fn write(&mut self) -> Result<()> { + self.file_buf.clear(); + + self.file_buf + .extend_from_slice(b"DON'T EDIT THIS FILE!\n\n"); + self.file_buf + .extend_from_slice(self.current_exercise().name.as_bytes()); + self.file_buf.push(b'\n'); + + for exercise in &self.exercises { + if exercise.done { + self.file_buf.push(b'\n'); + self.file_buf.extend_from_slice(exercise.name.as_bytes()); + } + } + + fs::write(STATE_FILE_NAME, &self.file_buf) + .with_context(|| format!("Failed to write the state file {STATE_FILE_NAME}"))?; + + Ok(()) + } + pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { if ind >= self.exercises.len() { bail!(BAD_INDEX_ERR); @@ -218,6 +255,8 @@ impl AppState { Ok(()) } + // Official exercises: Dump the original file from the binary. + // Third-party exercises: Reset the exercise file with `git stash`. fn reset(&self, ind: usize, dir_name: Option<&str>, path: &str) -> Result<()> { if self.official_exercises { return EMBEDDED_FILES @@ -271,6 +310,7 @@ impl AppState { Ok(exercise.path) } + // Return the index of the next pending exercise or `None` if all exercises are done. fn next_pending_exercise_ind(&self) -> Option { if self.current_exercise_ind == self.exercises.len() - 1 { // The last exercise is done. @@ -293,6 +333,8 @@ impl AppState { } } + // Official exercises: Dump the solution file form the binary and return its path. + // Third-party exercises: Check if a solution file exists and return its path in that case. pub fn current_solution_path(&self) -> Result> { if DEBUG_PROFILE { return Ok(None); @@ -328,6 +370,9 @@ impl AppState { } } + // Mark the current exercise as done and move on to the next pending exercise if one exists. + // If all exercises are marked as done, run all of them to make sure that they are actually + // done. If an exercise which is marked as done fails, mark it as pending and continue on it. pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { let exercise = &mut self.exercises[self.current_exercise_ind]; if !exercise.done { @@ -335,78 +380,48 @@ impl AppState { self.n_done += 1; } - let Some(ind) = self.next_pending_exercise_ind() else { - writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; + if let Some(ind) = self.next_pending_exercise_ind() { + self.set_current_exercise_ind(ind)?; - let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - for (exercise_ind, exercise) in self.exercises().iter().enumerate() { - write!(writer, "Running {exercise} ... ")?; - writer.flush()?; - - let success = exercise.run(&mut output, &self.target_dir)?; - if !success { - writeln!(writer, "{}\n", "FAILED".red())?; + return Ok(ExercisesProgress::Pending); + } - self.current_exercise_ind = exercise_ind; + writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; - // No check if the exercise is done before setting it to pending - // because no pending exercise was found. - self.exercises[exercise_ind].done = false; - self.n_done -= 1; + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + for (exercise_ind, exercise) in self.exercises().iter().enumerate() { + write!(writer, "Running {exercise} ... ")?; + writer.flush()?; - self.write()?; + let success = exercise.run(&mut output, &self.target_dir)?; + if !success { + writeln!(writer, "{}\n", "FAILED".red())?; - return Ok(ExercisesProgress::Pending); - } + self.current_exercise_ind = exercise_ind; - writeln!(writer, "{}", "ok".green())?; - } + // No check if the exercise is done before setting it to pending + // because no pending exercise was found. + self.exercises[exercise_ind].done = false; + self.n_done -= 1; - writer.execute(Clear(ClearType::All))?; - writer.write_all(FENISH_LINE.as_bytes())?; + self.write()?; - let final_message = self.final_message.trim(); - if !final_message.is_empty() { - writer.write_all(final_message.as_bytes())?; - writer.write_all(b"\n")?; + return Ok(ExercisesProgress::Pending); } - return Ok(ExercisesProgress::AllDone); - }; - - self.set_current_exercise_ind(ind)?; - - Ok(ExercisesProgress::Pending) - } - - // Write the state file. - // The file's format is very simple: - // - The first line is a comment. - // - The second line is an empty line. - // - The third line is the name of the current exercise. It must end with `\n` even if there - // are no done exercises. - // - The fourth line is an empty line. - // - All remaining lines are the names of done exercises. - fn write(&mut self) -> Result<()> { - self.file_buf.clear(); + writeln!(writer, "{}", "ok".green())?; + } - self.file_buf - .extend_from_slice(b"DON'T EDIT THIS FILE!\n\n"); - self.file_buf - .extend_from_slice(self.current_exercise().name.as_bytes()); - self.file_buf.push(b'\n'); + writer.execute(Clear(ClearType::All))?; + writer.write_all(FENISH_LINE.as_bytes())?; - for exercise in &self.exercises { - if exercise.done { - self.file_buf.push(b'\n'); - self.file_buf.extend_from_slice(exercise.name.as_bytes()); - } + let final_message = self.final_message.trim(); + if !final_message.is_empty() { + writer.write_all(final_message.as_bytes())?; + writer.write_all(b"\n")?; } - fs::write(STATE_FILE_NAME, &self.file_buf) - .with_context(|| format!("Failed to write the state file {STATE_FILE_NAME}"))?; - - Ok(()) + Ok(ExercisesProgress::AllDone) } } diff --git a/src/dev.rs b/src/dev.rs index 737de0de63..107d437658 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -15,7 +15,7 @@ pub enum DevCommands { New { /// The path to create the project in path: PathBuf, - /// Don't initialize a Git repository in the project directory + /// Don't try to initialize a Git repository in the project directory #[arg(long)] no_git: bool, }, diff --git a/src/main.rs b/src/main.rs index bb70a75783..c51f63caca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,11 @@ const DEBUG_PROFILE: bool = { debug_profile }; +// The current directory is the official Rustligns repository. +fn in_official_repo() -> bool { + Path::new("dev/rustlings-repo.txt").exists() +} + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -54,7 +59,7 @@ struct Args { #[derive(Subcommand)] enum Subcommands { - /// Initialize Rustlings + /// Initialize the official Rustlings exercises Init, /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified Run { @@ -76,10 +81,6 @@ enum Subcommands { Dev(DevCommands), } -fn in_official_repo() -> bool { - Path::new("dev/rustlings-repo.txt").exists() -} - fn main() -> Result<()> { let args = Args::parse(); @@ -123,6 +124,7 @@ fn main() -> Result<()> { info_file.final_message.unwrap_or_default(), )?; + // Show the welcome message if the state file doesn't exist yet. if let Some(welcome_message) = info_file.welcome_message { match state_file_status { StateFileStatus::NotRead => { From fef66b80ad0b90d7bbc6ebe704f34816a4b3173a Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 30 Apr 2024 01:39:31 +0200 Subject: [PATCH 0854/1432] Implement From for Exercise --- src/app_state.rs | 27 +-------------------------- src/exercise.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 9d12c9365f..6af104357c 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -121,34 +121,9 @@ impl AppState { )? .target_directory; - // Build exercises from their metadata in the info file. let exercises = exercise_infos .into_iter() - .map(|mut exercise_info| { - // Leaking to be able to borrow in the watch mode `Table`. - // Leaking is not a problem because the `AppState` instance lives until - // the end of the program. - let path = exercise_info.path().leak(); - - exercise_info.name.shrink_to_fit(); - let name = exercise_info.name.leak(); - let dir = exercise_info.dir.map(|mut dir| { - dir.shrink_to_fit(); - &*dir.leak() - }); - - let hint = exercise_info.hint.trim().to_owned(); - - Exercise { - dir, - name, - path, - test: exercise_info.test, - strict_clippy: exercise_info.strict_clippy, - hint, - done: false, - } - }) + .map(Exercise::from) .collect::>(); let mut slf = Self { diff --git a/src/exercise.rs b/src/exercise.rs index b62958b984..37d33b7a0e 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -10,6 +10,7 @@ use std::{ use crate::{ cmd::{run_cmd, CargoCmd}, in_official_repo, + info_file::ExerciseInfo, terminal_link::TerminalFileLink, DEBUG_PROFILE, }; @@ -132,6 +133,34 @@ impl Exercise { } } +impl From for Exercise { + fn from(mut exercise_info: ExerciseInfo) -> Self { + // Leaking to be able to borrow in the watch mode `Table`. + // Leaking is not a problem because the `AppState` instance lives until + // the end of the program. + let path = exercise_info.path().leak(); + + exercise_info.name.shrink_to_fit(); + let name = exercise_info.name.leak(); + let dir = exercise_info.dir.map(|mut dir| { + dir.shrink_to_fit(); + &*dir.leak() + }); + + let hint = exercise_info.hint.trim().to_owned(); + + Exercise { + dir, + name, + path, + test: exercise_info.test, + strict_clippy: exercise_info.strict_clippy, + hint, + done: false, + } + } +} + impl Display for Exercise { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.path.fmt(f) From 52c0f5b39efd7c71e63a3a680a1d91f3efc8eda5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 30 Apr 2024 01:41:08 +0200 Subject: [PATCH 0855/1432] Fix clearing the terminal --- src/app_state.rs | 9 +++------ src/main.rs | 14 +++++++------- src/watch/state.rs | 6 +++--- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 6af104357c..907c1282b6 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,9 +1,5 @@ use anyhow::{bail, Context, Result}; -use crossterm::{ - style::Stylize, - terminal::{Clear, ClearType}, - ExecutableCommand, -}; +use crossterm::style::Stylize; use serde::Deserialize; use std::{ fs::{self, File}, @@ -13,6 +9,7 @@ use std::{ }; use crate::{ + clear_terminal, embedded::EMBEDDED_FILES, exercise::{Exercise, OUTPUT_CAPACITY}, info_file::ExerciseInfo, @@ -387,7 +384,7 @@ impl AppState { writeln!(writer, "{}", "ok".green())?; } - writer.execute(Clear(ClearType::All))?; + clear_terminal(writer)?; writer.write_all(FENISH_LINE.as_bytes())?; let final_message = self.final_message.trim(); diff --git a/src/main.rs b/src/main.rs index c51f63caca..3e37ce2516 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,8 @@ use anyhow::{bail, Context, Result}; use app_state::StateFileStatus; use clap::{Parser, Subcommand}; -use crossterm::{ - terminal::{Clear, ClearType}, - ExecutableCommand, -}; use std::{ - io::{self, BufRead, Write}, + io::{self, BufRead, StdoutLock, Write}, path::Path, process::exit, }; @@ -45,6 +41,10 @@ fn in_official_repo() -> bool { Path::new("dev/rustlings-repo.txt").exists() } +fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { + stdout.write_all(b"\x1b[H\x1b[2J\x1b[3J") +} + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -129,7 +129,7 @@ fn main() -> Result<()> { match state_file_status { StateFileStatus::NotRead => { let mut stdout = io::stdout().lock(); - stdout.execute(Clear(ClearType::All))?; + clear_terminal(&mut stdout)?; let welcome_message = welcome_message.trim(); write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; @@ -137,7 +137,7 @@ fn main() -> Result<()> { io::stdin().lock().read_until(b'\n', &mut Vec::new())?; - stdout.execute(Clear(ClearType::All))?; + clear_terminal(&mut stdout)?; } StateFileStatus::Read => (), } diff --git a/src/watch/state.rs b/src/watch/state.rs index e5364c3004..2cf7521df9 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,13 +1,13 @@ use anyhow::Result; use crossterm::{ style::{style, Stylize}, - terminal::{size, Clear, ClearType}, - ExecutableCommand, + terminal::size, }; use std::io::{self, StdoutLock, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, + clear_terminal, exercise::OUTPUT_CAPACITY, progress_bar::progress_bar, terminal_link::TerminalFileLink, @@ -111,7 +111,7 @@ impl<'a> WatchState<'a> { // Prevent having the first line shifted. self.writer.write_all(b"\n")?; - self.writer.execute(Clear(ClearType::All))?; + clear_terminal(&mut self.writer)?; self.writer.write_all(&self.output)?; self.writer.write_all(b"\n")?; From 2b7ac915059a4baa2d9c86a583c73fc4f07a8775 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 30 Apr 2024 01:46:57 +0200 Subject: [PATCH 0856/1432] Add press_enter_prompt --- src/main.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3e37ce2516..15bcc8e37d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,11 @@ fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { stdout.write_all(b"\x1b[H\x1b[2J\x1b[3J") } +fn press_enter_prompt() -> io::Result<()> { + io::stdin().lock().read_until(b'\n', &mut Vec::new())?; + Ok(()) +} + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -98,7 +103,7 @@ fn main() -> Result<()> { let mut stdout = io::stdout().lock(); stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; stdout.flush()?; - io::stdin().lock().read_until(b'\n', &mut Vec::new())?; + press_enter_prompt()?; stdout.write_all(b"\n")?; } @@ -134,9 +139,7 @@ fn main() -> Result<()> { let welcome_message = welcome_message.trim(); write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; stdout.flush()?; - - io::stdin().lock().read_until(b'\n', &mut Vec::new())?; - + press_enter_prompt()?; clear_terminal(&mut stdout)?; } StateFileStatus::Read => (), From 563727f47f06cf79bbb40c4b4e7fda67b65fb40f Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 30 Apr 2024 02:14:20 +0200 Subject: [PATCH 0857/1432] test next_pending_exercise_ind --- src/app_state.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/app_state.rs b/src/app_state.rs index 907c1282b6..8cb3e4669a 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -428,3 +428,56 @@ const FENISH_LINE: &str = "+---------------------------------------------------- β–’β–’ β–’β–’ β–’β–’ β–’β–’\x1b[0m "; + +#[cfg(test)] +mod tests { + use super::*; + + fn dummy_exercise() -> Exercise { + Exercise { + dir: None, + name: "0", + path: "exercises/0.rs", + test: false, + strict_clippy: false, + hint: String::new(), + done: false, + } + } + + #[test] + fn next_pending_exercise() { + let mut app_state = AppState { + current_exercise_ind: 0, + exercises: vec![dummy_exercise(), dummy_exercise(), dummy_exercise()], + n_done: 0, + final_message: String::new(), + file_buf: Vec::new(), + official_exercises: true, + target_dir: PathBuf::new(), + }; + + let mut assert = |done: [bool; 3], expected: [Option; 3]| { + for (exercise, done) in app_state.exercises.iter_mut().zip(done) { + exercise.done = done; + } + for (ind, expected) in expected.into_iter().enumerate() { + app_state.current_exercise_ind = ind; + assert_eq!( + app_state.next_pending_exercise_ind(), + expected, + "done={done:?}, ind={ind}", + ); + } + }; + + assert([true, true, true], [None, None, None]); + assert([false, false, false], [Some(1), Some(2), Some(0)]); + assert([false, true, true], [None, Some(0), Some(0)]); + assert([true, false, true], [Some(1), None, Some(1)]); + assert([true, true, false], [Some(2), Some(2), None]); + assert([true, false, false], [Some(1), Some(2), Some(1)]); + assert([false, true, false], [Some(2), Some(2), Some(0)]); + assert([false, false, true], [Some(1), Some(0), Some(0)]); + } +} From 3ae6c208b275dd17bb05f7fcdbb0090e40ba1325 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 30 Apr 2024 02:43:51 +0200 Subject: [PATCH 0858/1432] Disable the pretty format because of `--show-output` --- src/exercise.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 37d33b7a0e..4edf378e77 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -105,14 +105,7 @@ impl Exercise { let test_success = CargoCmd { subcommand: "test", - args: &[ - "--", - "--color", - "always", - "--show-output", - "--format", - "pretty", - ], + args: &["--", "--color", "always", "--show-output"], exercise_name: self.name, description: "cargo test …", // Hide warnings because they are shown by Clippy. From 8e178ac60dd947dc4ae30126b9281106599ddbce Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 30 Apr 2024 02:48:56 +0200 Subject: [PATCH 0859/1432] Document and test cargo_toml --- src/cargo_toml.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index 2345a7ee9b..106e6a7ab0 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -2,6 +2,10 @@ use anyhow::{Context, Result}; use crate::info_file::ExerciseInfo; +// Return the start and end index of the content of the list `bin = […]`. +// bin = [xxxxxxxxxxxxxxxxx] +// |start_ind | +// |end_ind pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { let start_ind = cargo_toml .find("bin = [") @@ -16,6 +20,8 @@ pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { Ok((start_ind, end_ind)) } +// Generate and append the content of the `bin` list in `Cargo.toml`. +// The `exercise_path_prefix` is the prefix of the `path` field of every list entry. pub fn append_bins( buf: &mut Vec, exercise_infos: &[ExerciseInfo], @@ -37,6 +43,7 @@ pub fn append_bins( } } +// Update the `bin` list and leave everything else unchanged. pub fn updated_cargo_toml( exercise_infos: &[ExerciseInfo], current_cargo_toml: &str, @@ -55,3 +62,61 @@ pub fn updated_cargo_toml( Ok(updated_cargo_toml) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bins_start_end_ind() { + assert_eq!(bins_start_end_ind("").ok(), None); + assert_eq!(bins_start_end_ind("[]").ok(), None); + assert_eq!(bins_start_end_ind("bin = [").ok(), None); + assert_eq!(bins_start_end_ind("bin = ]").ok(), None); + assert_eq!(bins_start_end_ind("bin = []").ok(), Some((7, 7))); + assert_eq!(bins_start_end_ind("bin= []").ok(), None); + assert_eq!(bins_start_end_ind("bin =[]").ok(), None); + assert_eq!(bins_start_end_ind("bin=[]").ok(), None); + assert_eq!(bins_start_end_ind("bin = [\nxxx\n]").ok(), Some((7, 12))); + } + + #[test] + fn test_bins() { + let exercise_infos = [ + ExerciseInfo { + name: String::from("1"), + dir: None, + test: true, + strict_clippy: true, + hint: String::new(), + }, + ExerciseInfo { + name: String::from("2"), + dir: Some(String::from("d")), + test: false, + strict_clippy: false, + hint: String::new(), + }, + ]; + + let mut buf = Vec::with_capacity(128); + append_bins(&mut buf, &exercise_infos, b""); + assert_eq!( + buf, + br#" + { name = "1", path = "exercises/1.rs" }, + { name = "2", path = "exercises/d/2.rs" }, +"#, + ); + + assert_eq!( + updated_cargo_toml(&exercise_infos, "abc\nbin = [xxx]\n123", b"../").unwrap(), + br#"abc +bin = [ + { name = "1", path = "../exercises/1.rs" }, + { name = "2", path = "../exercises/d/2.rs" }, +] +123"#, + ); + } +} From 32415e1e6cca9e0fb9a3019ed8e75956c7f7f92e Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 1 May 2024 17:55:49 +0200 Subject: [PATCH 0860/1432] Document cmd --- src/cmd.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cmd.rs b/src/cmd.rs index 28f21c555f..e4bc112132 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,6 +1,8 @@ use anyhow::{Context, Result}; use std::{io::Read, path::Path, process::Command}; +// Run a command with a description for a possible error and append the merged stdout and stderr. +// The boolean in the returned `Result` is true if the command's exit status is success. pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec) -> Result { let (mut reader, writer) = os_pipe::pipe() .with_context(|| format!("Failed to create a pipe to run the command `{description}``"))?; @@ -35,13 +37,18 @@ pub struct CargoCmd<'a> { pub args: &'a [&'a str], pub exercise_name: &'a str, pub description: &'a str, + // RUSTFLAGS="-A warnings" pub hide_warnings: bool, + // Added as `--target-dir` if `Self::dev` is true. pub target_dir: &'a Path, + // The output buffer to append the merged stdout and stderr. pub output: &'a mut Vec, + // true while developing Rustlings. pub dev: bool, } impl<'a> CargoCmd<'a> { + // Run `cargo SUBCOMMAND --bin EXERCISE_NAME … ARGS`. pub fn run(&mut self) -> Result { let mut cmd = Command::new("cargo"); cmd.arg(self.subcommand); From d425dbe203c17166e2e0b5692695448f0cb85513 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 1 May 2024 18:08:18 +0200 Subject: [PATCH 0861/1432] Test run_cmd --- src/cmd.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/cmd.rs b/src/cmd.rs index e4bc112132..9762cf856d 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -75,3 +75,19 @@ impl<'a> CargoCmd<'a> { run_cmd(cmd, self.description, self.output) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_run_cmd() { + let mut cmd = Command::new("echo"); + cmd.arg("Hello"); + + let mut output = Vec::with_capacity(8); + run_cmd(cmd, "echo …", &mut output).unwrap(); + + assert_eq!(output, b"Hello\n\n"); + } +} From 74180ba1cccb69cecb94932795ebd8743fa42a6c Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 1 May 2024 19:16:59 +0200 Subject: [PATCH 0862/1432] Check for tests while test=false --- src/dev/check.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index b6e6f31fed..143037c04f 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -22,22 +22,17 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result Result Result Date: Wed, 1 May 2024 19:47:35 +0200 Subject: [PATCH 0863/1432] Document dev --- src/dev.rs | 3 +-- src/dev/check.rs | 15 ++++++++++----- src/dev/new.rs | 31 ++++++++++++++++--------------- src/dev/update.rs | 5 +++-- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 107d437658..fada8b330a 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,7 +1,6 @@ -use std::path::PathBuf; - use anyhow::{bail, Context, Result}; use clap::Subcommand; +use std::path::PathBuf; use crate::DEBUG_PROFILE; diff --git a/src/dev/check.rs b/src/dev/check.rs index 143037c04f..81d05ce633 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -12,10 +12,12 @@ use crate::{ CURRENT_FORMAT_VERSION, DEBUG_PROFILE, }; +// Find a char that isn't allowed in the exercise's `name` or `dir`. fn forbidden_char(input: &str) -> Option { input.chars().find(|c| *c != '_' && !c.is_alphanumeric()) } +// Check the info of all exercises and return their paths in a set. fn check_info_file_exercises(info_file: &InfoFile) -> Result> { let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); @@ -72,11 +74,12 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result 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()) -} +// Check the `exercises` directory for unexpected files. +fn check_unexpected_files(info_file_paths: &hashbrown::HashSet) -> Result<()> { + 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) -> Result<()> { for entry in read_dir("exercises").context("Failed to open the `exercises` directory")? { let entry = entry.context("Failed to read the `exercises` directory")?; @@ -128,11 +131,12 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { } let info_file_paths = check_info_file_exercises(info_file)?; - check_exercise_dir_files(&info_file_paths)?; + check_unexpected_files(&info_file_paths)?; Ok(()) } +// Check that the Cargo.toml file is up-to-date. fn check_cargo_toml( exercise_infos: &[ExerciseInfo], current_cargo_toml: &str, @@ -159,6 +163,7 @@ pub fn check() -> Result<()> { let info_file = InfoFile::parse()?; check_exercises(&info_file)?; + // A hack to make `cargo run -- dev check` work when developing Rustlings. if DEBUG_PROFILE { check_cargo_toml( &info_file.exercises, diff --git a/src/dev/new.rs b/src/dev/new.rs index 44487abfb2..fefc4fc1a3 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -8,6 +8,7 @@ use std::{ use crate::CURRENT_FORMAT_VERSION; +// Create a directory relative to the current directory and print its path. fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> { create_dir(dir_name) .with_context(|| format!("Failed to create the directory {current_dir}/{dir_name}"))?; @@ -15,6 +16,7 @@ fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> { Ok(()) } +// Write a file relative to the current directory and print its path. fn write_rel_file(file_name: &str, current_dir: &str, content: C) -> Result<()> where C: AsRef<[u8]>, @@ -27,13 +29,13 @@ where } pub fn new(path: &Path, no_git: bool) -> Result<()> { - let dir_name = path.to_string_lossy(); + let dir_path_str = path.to_string_lossy(); - create_dir(path).with_context(|| format!("Failed to create the directory {dir_name}"))?; - println!("Created the directory {dir_name}"); + create_dir(path).with_context(|| format!("Failed to create the directory {dir_path_str}"))?; + println!("Created the directory {dir_path_str}"); set_current_dir(path) - .with_context(|| format!("Failed to set {dir_name} as the current directory"))?; + .with_context(|| format!("Failed to set {dir_path_str} as the current directory"))?; if !no_git && !Command::new("git") @@ -42,28 +44,28 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { .context("Failed to run `git init`")? .success() { - bail!("`git init` didn't run successfully. See the error message above"); + bail!("`git init` didn't run successfully. See the possible error message above"); } - write_rel_file(".gitignore", &dir_name, GITIGNORE)?; + write_rel_file(".gitignore", &dir_path_str, GITIGNORE)?; - create_rel_dir("exercises", &dir_name)?; - create_rel_dir("solutions", &dir_name)?; + create_rel_dir("exercises", &dir_path_str)?; + create_rel_dir("solutions", &dir_path_str)?; write_rel_file( "info.toml", - &dir_name, + &dir_path_str, format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"), )?; - write_rel_file("Cargo.toml", &dir_name, CARGO_TOML)?; + write_rel_file("Cargo.toml", &dir_path_str, CARGO_TOML)?; - write_rel_file("README.md", &dir_name, README)?; + write_rel_file("README.md", &dir_path_str, README)?; - create_rel_dir(".vscode", &dir_name)?; + create_rel_dir(".vscode", &dir_path_str)?; write_rel_file( ".vscode/extensions.json", - &dir_name, + &dir_path_str, crate::init::VS_CODE_EXTENSIONS_JSON, )?; @@ -137,8 +139,7 @@ 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) βœ… +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 πŸš€ "; diff --git a/src/dev/update.rs b/src/dev/update.rs index fe7622c076..66efe3d05b 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -1,6 +1,5 @@ -use std::fs; - use anyhow::{Context, Result}; +use std::fs; use crate::{ cargo_toml::updated_cargo_toml, @@ -8,6 +7,7 @@ use crate::{ DEBUG_PROFILE, }; +// Update the `Cargo.toml` file. fn update_cargo_toml( exercise_infos: &[ExerciseInfo], current_cargo_toml: &str, @@ -26,6 +26,7 @@ fn update_cargo_toml( pub fn update() -> Result<()> { let info_file = InfoFile::parse()?; + // A hack to make `cargo run -- dev update` work when developing Rustlings. if DEBUG_PROFILE { update_cargo_toml( &info_file.exercises, From 2d0497bf3b6e9cf7edcf1a6d1899e0e5364fc49b Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 2 May 2024 17:08:39 +0200 Subject: [PATCH 0864/1432] Fix errors --- src/embedded.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index d7952a1f4f..a84e332bb2 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -25,9 +25,9 @@ impl WriteStrategy { .open(path), }; - file.context("Failed to open the file `{path}` in write mode")? + file.with_context(|| format!("Failed to open the file `{path}` in write mode"))? .write_all(content) - .context("Failed to write the file {path}") + .with_context(|| format!("Failed to write the file {path}")) } } From da9f97b0e0a54747202c43c6aed8346cc784cb02 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 12 May 2024 01:35:30 +0200 Subject: [PATCH 0865/1432] Update deps --- Cargo.lock | 91 +++++++++++++++++++++++++++++------------------------- Cargo.toml | 4 +-- 2 files changed, 51 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 271d09a4a9..7a6049edab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,47 +31,48 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -79,9 +80,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "assert_cmd" @@ -100,9 +101,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" @@ -190,9 +191,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "compact_str" @@ -359,6 +360,12 @@ dependencies = [ "libc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -396,9 +403,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "lock_api" @@ -480,9 +487,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -528,9 +535,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "predicates" @@ -564,9 +571,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -677,15 +684,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -704,18 +711,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -724,9 +731,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -824,9 +831,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1088,27 +1095,27 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index efb15127df..bc10d02810 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ include = [ ] [dependencies] -anyhow = "1.0.82" +anyhow = "1.0.83" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" @@ -55,7 +55,7 @@ notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.6" } -serde_json = "1.0.116" +serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true From d9df809838191962a82e98ff01aaaa73950ba670 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 12 May 2024 17:40:53 +0200 Subject: [PATCH 0866/1432] Optimize embedded dirs --- rustlings-macros/src/lib.rs | 22 +++++++-- src/app_state.rs | 42 +++++++---------- src/embedded.rs | 90 ++++++++++++++++++++++++++----------- 3 files changed, 98 insertions(+), 56 deletions(-) diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index fc2bcf142f..4417a4f90e 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -25,14 +25,28 @@ pub fn include_files(_: TokenStream) -> TokenStream { let solution_files = exercises .iter() .map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name)); - let dirs = exercises.iter().map(|exercise| &exercise.dir); - let readmes = exercises + + let mut dirs = Vec::with_capacity(32); + let mut dir_inds = vec![0; exercises.len()]; + + for (exercise, dir_ind) in exercises.iter().zip(&mut dir_inds) { + // The directory is often the last one inserted. + if let Some(ind) = dirs.iter().rev().position(|dir| *dir == exercise.dir) { + *dir_ind = dirs.len() - 1 - ind; + continue; + } + + dirs.push(exercise.dir.as_str()); + *dir_ind = dirs.len() - 1; + } + + let readmes = dirs .iter() - .map(|exercise| format!("../exercises/{}/README.md", exercise.dir)); + .map(|dir| format!("../exercises/{dir}/README.md")); quote! { EmbeddedFiles { - exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files) }),*], + exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files), dir_ind: #dir_inds }),*], exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*] } } diff --git a/src/app_state.rs b/src/app_state.rs index 8cb3e4669a..492be345a8 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -193,12 +193,12 @@ impl AppState { Ok(()) } - pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { - if ind >= self.exercises.len() { + pub fn set_current_exercise_ind(&mut self, exercise_ind: usize) -> Result<()> { + if exercise_ind >= self.exercises.len() { bail!(BAD_INDEX_ERR); } - self.current_exercise_ind = ind; + self.current_exercise_ind = exercise_ind; self.write() } @@ -215,8 +215,11 @@ impl AppState { self.write() } - pub fn set_pending(&mut self, ind: usize) -> Result<()> { - let exercise = self.exercises.get_mut(ind).context(BAD_INDEX_ERR)?; + pub fn set_pending(&mut self, exercise_ind: usize) -> Result<()> { + let exercise = self + .exercises + .get_mut(exercise_ind) + .context(BAD_INDEX_ERR)?; if exercise.done { exercise.done = false; @@ -229,16 +232,10 @@ impl AppState { // Official exercises: Dump the original file from the binary. // Third-party exercises: Reset the exercise file with `git stash`. - fn reset(&self, ind: usize, dir_name: Option<&str>, path: &str) -> Result<()> { + fn reset(&self, exercise_ind: usize, path: &str) -> Result<()> { if self.official_exercises { return EMBEDDED_FILES - .write_exercise_to_disk( - ind, - dir_name.context( - "Official exercises must be nested in the `exercises` directory", - )?, - path, - ) + .write_exercise_to_disk(exercise_ind, path) .with_context(|| format!("Failed to reset the exercise {path}")); } @@ -265,7 +262,7 @@ impl AppState { pub fn reset_current_exercise(&mut self) -> Result<&'static str> { self.set_pending(self.current_exercise_ind)?; let exercise = self.current_exercise(); - self.reset(self.current_exercise_ind, exercise.dir, exercise.path)?; + self.reset(self.current_exercise_ind, exercise.path)?; Ok(exercise.path) } @@ -277,7 +274,7 @@ impl AppState { self.set_pending(exercise_ind)?; let exercise = &self.exercises[exercise_ind]; - self.reset(exercise_ind, exercise.dir, exercise.path)?; + self.reset(exercise_ind, exercise.path)?; Ok(exercise.path) } @@ -315,18 +312,9 @@ impl AppState { let current_exercise = self.current_exercise(); if self.official_exercises { - let dir_name = current_exercise - .dir - .context("Official exercises must be nested in the `exercises` directory")?; - let solution_path = format!("solutions/{dir_name}/{}.rs", current_exercise.name); - - EMBEDDED_FILES.write_solution_to_disk( - self.current_exercise_ind, - dir_name, - &solution_path, - )?; - - Ok(Some(solution_path)) + EMBEDDED_FILES + .write_solution_to_disk(self.current_exercise_ind, current_exercise.name) + .map(Some) } else { let solution_path = if let Some(dir) = current_exercise.dir { format!("solutions/{dir}/{}.rs", current_exercise.name) diff --git a/src/embedded.rs b/src/embedded.rs index a84e332bb2..23c8d6e40c 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Error, Result}; +use anyhow::{Context, Error, Result}; use std::{ fs::{create_dir, create_dir_all, OpenOptions}, io::{self, Write}, @@ -34,6 +34,7 @@ impl WriteStrategy { struct ExerciseFiles { exercise: &'static [u8], solution: &'static [u8], + dir_ind: usize, } struct ExerciseDir { @@ -43,11 +44,10 @@ struct ExerciseDir { impl ExerciseDir { fn init_on_disk(&self) -> Result<()> { - let path_prefix = "exercises/"; - let readme_path_postfix = "/README.md"; - let mut dir_path = - String::with_capacity(path_prefix.len() + self.name.len() + readme_path_postfix.len()); - dir_path.push_str(path_prefix); + // 20 = 10 + 10 + // exercises/ + /README.md + let mut dir_path = String::with_capacity(20 + self.name.len()); + dir_path.push_str("exercises/"); dir_path.push_str(self.name); if let Err(e) = create_dir(&dir_path) { @@ -60,10 +60,9 @@ impl ExerciseDir { ); } - let readme_path = { - dir_path.push_str(readme_path_postfix); - dir_path - }; + let mut readme_path = dir_path; + readme_path.push_str("/README.md"); + WriteStrategy::Overwrite.write(&readme_path, self.readme)?; Ok(()) @@ -95,30 +94,71 @@ impl EmbeddedFiles { Ok(()) } - pub fn write_exercise_to_disk( - &self, - exercise_ind: usize, - dir_name: &str, - path: &str, - ) -> Result<()> { - let Some(dir) = self.exercise_dirs.iter().find(|dir| dir.name == dir_name) else { - bail!("`{dir_name}` not found in the embedded directories"); - }; + pub fn write_exercise_to_disk(&self, exercise_ind: usize, path: &str) -> Result<()> { + let exercise_files = &EMBEDDED_FILES.exercise_files[exercise_ind]; + let dir = &EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind]; dir.init_on_disk()?; - WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].exercise) + WriteStrategy::Overwrite.write(path, exercise_files.exercise) } + // Write the solution file to disk and return its path. pub fn write_solution_to_disk( &self, exercise_ind: usize, - dir_name: &str, - path: &str, - ) -> Result<()> { - let dir_path = format!("solutions/{dir_name}"); + exercise_name: &str, + ) -> Result { + let exercise_files = &EMBEDDED_FILES.exercise_files[exercise_ind]; + let dir = &EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind]; + + // 14 = 10 + 1 + 3 + // solutions/ + / + .rs + let mut dir_path = String::with_capacity(14 + dir.name.len() + exercise_name.len()); + dir_path.push_str("solutions/"); + dir_path.push_str(dir.name); create_dir_all(&dir_path) .with_context(|| format!("Failed to create the directory {dir_path}"))?; - WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].solution) + let mut solution_path = dir_path; + solution_path.push('/'); + solution_path.push_str(exercise_name); + solution_path.push_str(".rs"); + + WriteStrategy::Overwrite.write(&solution_path, exercise_files.solution)?; + + Ok(solution_path) + } +} + +#[cfg(test)] +mod tests { + use serde::Deserialize; + + use super::*; + + #[derive(Deserialize)] + struct ExerciseInfo { + dir: String, + } + + #[derive(Deserialize)] + struct InfoFile { + exercises: Vec, + } + + #[test] + fn dirs() { + let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) + .expect("Failed to parse `info.toml`") + .exercises; + + assert_eq!(exercises.len(), EMBEDDED_FILES.exercise_files.len()); + + for (exercise, exercise_files) in exercises.iter().zip(EMBEDDED_FILES.exercise_files) { + assert_eq!( + exercise.dir, + EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind].name, + ); + } } } From baca8c96672e3fe414eadfb8c19f2ac38e7cac2b Mon Sep 17 00:00:00 2001 From: iamcult <101368650+iamcult@users.noreply.github.com> Date: Sun, 12 May 2024 14:48:06 -0400 Subject: [PATCH 0867/1432] chore: update flake.lock --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 15238981d8..6592dd8944 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -21,11 +21,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1692799911, - "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -36,11 +36,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1694183432, - "narHash": "sha256-YyPGNapgZNNj51ylQMw9lAgvxtM2ai1HZVUu3GS8Fng=", + "lastModified": 1715447595, + "narHash": "sha256-VsVAUQOj/cS1LCOmMjAGeRksXIAdPnFIjCQ0XLkCsT0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "db9208ab987cdeeedf78ad9b4cf3c55f5ebd269b", + "rev": "062ca2a9370a27a35c524dc82d540e6e9824b652", "type": "github" }, "original": { From 01509a2a84498e2814505e650994ac03062ffd0c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 12 May 2024 22:44:46 +0200 Subject: [PATCH 0868/1432] Remove comma --- exercises/07_structs/structs3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 2d55dd7e63..7cda5af103 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,7 +1,7 @@ // 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. +// 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 From 01a78531ad40982d25a70c0a2393e39e21f666f2 Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Sun, 12 May 2024 15:10:50 -0700 Subject: [PATCH 0869/1432] refactor: remove `referent` to improve readability --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index 5690701d55..9fdc9a4f50 100644 --- a/info.toml +++ b/info.toml @@ -378,7 +378,7 @@ path = "exercises/06_move_semantics/move_semantics5.rs" mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in -scope. Does it help to update the value of referent (`x`) immediately after +scope. Does it help to update the value of `x` immediately after the mutable reference is taken? Read more about 'Mutable References' in the book's section 'References and Borrowing': https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. From 11fda5d70f568e0f528d91dd573447719abe05f4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 01:25:38 +0200 Subject: [PATCH 0870/1432] Move info.toml to rustlings-macros/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the experience for contributors on Windows becuase Windows can't deal with git symbolic links out of the box… --- CONTRIBUTING.md | 2 +- Cargo.toml | 1 - info.toml | 1286 ---------------------------------- rustlings-macros/info.toml | 1287 ++++++++++++++++++++++++++++++++++- rustlings-macros/src/lib.rs | 4 +- src/embedded.rs | 3 +- src/info_file.rs | 4 +- 7 files changed, 1295 insertions(+), 1292 deletions(-) delete mode 100644 info.toml mode change 120000 => 100644 rustlings-macros/info.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6a2d176d4..bc00a6b043 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ Please be patient πŸ˜‡ - Name the file `exercises/yourTopic/yourTopicN.rs`. - Make sure to put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`. - Add a (possible) solution at `solutions/yourTopic/yourTopicN.rs` with comments and links explaining it. -- Add the [metadata for your exercise](#exercise-metadata) in the `info.toml` file. +- Add the [metadata for your exercise](#exercise-metadata) in the `rustlings-macros/info.toml` file. - Make sure your exercise runs with `rustlings run yourTopicN`. - [Open a pull request](#pull-requests). diff --git a/Cargo.toml b/Cargo.toml index bc10d02810..f2015cb3b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ include = [ "/src/", "/exercises/", "/solutions/", - "/info.toml", # A symlink to be able to include `dev/Cargo.toml` although `dev` is excluded. "/dev-Cargo.toml", "/README.md", diff --git a/info.toml b/info.toml deleted file mode 100644 index 4204f27433..0000000000 --- a/info.toml +++ /dev/null @@ -1,1286 +0,0 @@ -format_version = 1 - -welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners! -We are going to teach you a lot of things about Rust, but before we can -get started, here are some notes about how Rustlings operates: - -1. The central concept behind Rustlings is that you solve exercises. These - exercises usually contain some compiler or logic errors which cause the - exercise to fail compilation or testing. It's your job to find all errors - and fix them! -2. Make sure to have your editor open in the `rustlings/` directory. Rustlings - will show you the path of the current exercise under the progress bar. Open - the exercise file in your editor, fix errors and save the file. Rustlings will - automatically detect the file change and rerun the exercise. If all errors are - fixed, Rustlings will ask you to move on to the next exercise. -3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. -4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, - other learners do too so you can help each other out! -""" - -final_message = """We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, don't hesitate to report them on Github. -You can also contribute your own exercises to help the greater community! - -Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md -""" - -# INTRO - -# TODO: Update exercise -[[exercises]] -name = "intro1" -dir = "00_intro" -test = false -# TODO: Fix hint -hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" - -[[exercises]] -name = "intro2" -dir = "00_intro" -test = false -hint = """ -The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" - -# VARIABLES - -[[exercises]] -name = "variables1" -dir = "01_variables" -test = false -hint = """ -The declaration in the first line in the main function is missing a keyword -that is needed in Rust to create a new variable binding.""" - -[[exercises]] -name = "variables2" -dir = "01_variables" -test = false -hint = """ -The compiler message is saying that Rust cannot infer the type that the -variable binding `x` has with what is given here. - -What happens if you annotate the first line in the main function with a type -annotation? - -What if you give `x` a value? - -What if you do both? - -What type should `x` be, anyway? - -What if `x` is the same type as `10`? What if it's a different type?""" - -[[exercises]] -name = "variables3" -dir = "01_variables" -test = false -hint = """ -Oops! In this exercise, we have a variable binding that we've created on in the -first line in the `main` function, and we're trying to use it in the next line, -but we haven't given it a value. - -We can't print out something that isn't there; try giving `x` a value! - -This is an error that can cause bugs that's very easy to make in any -programming language -- thankfully the Rust compiler has caught this for us!""" - -[[exercises]] -name = "variables4" -dir = "01_variables" -test = false -hint = """ -In Rust, variable bindings are immutable by default. But here we're trying -to reassign a different value to `x`! There's a keyword we can use to make -a variable binding mutable instead.""" - -[[exercises]] -name = "variables5" -dir = "01_variables" -test = false -hint = """ -In `variables4` we already learned how to make an immutable variable mutable -using a special keyword. Unfortunately this doesn't help us much in this -exercise because we want to assign a different typed value to an existing -variable. Sometimes you may also like to reuse existing variable names because -you are just converting values to different types like in this exercise. - -Fortunately Rust has a powerful solution to this problem: 'Shadowing'! -You can read more about 'Shadowing' in the book's section 'Variables and -Mutability': -https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing - -Try to solve this exercise afterwards using this technique.""" - -[[exercises]] -name = "variables6" -dir = "01_variables" -test = false -hint = """ -We know about variables and mutability, but there is another important type of -variable available: constants. - -Constants are always immutable and they are declared with keyword `const` rather -than keyword `let`. - -Constants types must also always be annotated. - -Read more about constants and the differences between variables and constants -under 'Constants' in the book's section 'Variables and Mutability': -https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants -""" - -# FUNCTIONS - -[[exercises]] -name = "functions1" -dir = "02_functions" -test = false -hint = """ -This main function is calling a function that it expects to exist, but the -function doesn't exist. It expects this function to have the name `call_me`. -It expects this function to not take any arguments and not return a value. -Sounds a lot like `main`, doesn't it?""" - -[[exercises]] -name = "functions2" -dir = "02_functions" -test = false -hint = """ -Rust requires that all parts of a function's signature have type annotations, -but `call_me` is missing the type annotation of `num`.""" - -[[exercises]] -name = "functions3" -dir = "02_functions" -test = false -hint = """ -This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function.""" - -[[exercises]] -name = "functions4" -dir = "02_functions" -test = false -hint = """ -The error message points to the function `sale_price` and says it expects a type -after the `->`. This is where the function's return type should be -- take a -look at the `is_even` function for an example!""" - -[[exercises]] -name = "functions5" -dir = "02_functions" -test = false -hint = """ -This is a really common error that can be fixed by removing one character. -It happens because Rust distinguishes between expressions and statements: -expressions return a value based on their operand(s), and statements simply -return a `()` type which behaves just like `void` in C/C++ language. - -We want to return a value of `i32` type from the `square` function, but it is -returning a `()` type... - -They are not the same. There are two solutions: -1. Add a `return` ahead of `num * num;` -2. remove `;`, make it to be `num * num`""" - -# IF - -[[exercises]] -name = "if1" -dir = "03_if" -hint = """ -It's possible to do this in one line if you would like! - -Some similar examples from other languages: -- In C(++) this would be: `a > b ? a : b` -- In Python this would be: `a if a > b else b` - -Remember in Rust that: -- the `if` condition does not need to be surrounded by parentheses -- `if`/`else` conditionals are expressions -- Each condition is followed by a `{}` block.""" - -[[exercises]] -name = "if2" -dir = "03_if" -hint = """ -For that first compiler error, it's important in Rust that each conditional -block returns the same type! To get the tests passing, you will need a couple -conditions checking different input values.""" - -[[exercises]] -name = "if3" -dir = "03_if" -hint = """ -In Rust, every arm of an `if` expression has to return the same type of value. -Make sure the type is consistent across all arms.""" - -# QUIZ 1 - -[[exercises]] -name = "quiz1" -dir = "quizzes" -hint = "No hints this time ;)" - -# PRIMITIVE TYPES - -[[exercises]] -name = "primitive_types1" -dir = "04_primitive_types" -test = false -hint = "No hints this time ;)" - -[[exercises]] -name = "primitive_types2" -dir = "04_primitive_types" -test = false -hint = "No hints this time ;)" - -[[exercises]] -name = "primitive_types3" -dir = "04_primitive_types" -test = false -hint = """ -There's a shorthand to initialize Arrays with a certain size that does not -require you to type in 100 items (but you certainly can if you want!). - -For example, you can do: -``` -let array = ["Are we there yet?"; 10]; -``` - -Bonus: what are some other things you could have that would return `true` -for `a.len() >= 100`?""" - -[[exercises]] -name = "primitive_types4" -dir = "04_primitive_types" -hint = """ -Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section -of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the -starting and ending (plus one) indices of the items in the `Array` that you -want to end up in the slice. - -If you're curious why the first argument of `assert_eq!` does not have an -ampersand for a reference since the second argument is a reference, take a look -at the coercion chapter of the nomicon: -https://doc.rust-lang.org/nomicon/coercions.html""" - -[[exercises]] -name = "primitive_types5" -dir = "04_primitive_types" -test = false -hint = """ -Take a look at the 'Data Types -> The Tuple Type' section of the book: -https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Particularly the part about destructuring (second to last example in the -section). - -You'll need to make a pattern to bind `name` and `age` to the appropriate parts -of the tuple. You can do it!!""" - -[[exercises]] -name = "primitive_types6" -dir = "04_primitive_types" -hint = """ -While you could use a destructuring `let` for the tuple here, try -indexing into it instead, as explained in the last example of the -'Data Types -> The Tuple Type' section of the book: -https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Now you have another tool in your toolbox!""" - -# VECS - -[[exercises]] -name = "vecs1" -dir = "05_vecs" -hint = """ -In Rust, there are two ways to define a Vector. -1. One way is to use the `Vec::new()` function to create a new vector - and fill it with the `push()` method. -2. The second way, which is simpler is to use the `vec![]` macro and - define your elements inside the square brackets. - -Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html -of the Rust book to learn more. -""" - -[[exercises]] -name = "vecs2" -dir = "05_vecs" -hint = """ -In the first function we are looping over the Vector and getting a reference to -one `element` at a time. - -To modify the value of that `element` we need to use the `*` dereference -operator. You can learn more in this chapter of the Rust book: -https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector - -In the second function this dereferencing is not necessary, because the `map` -function expects the new value to be returned. - -After you've completed both functions, decide for yourself which approach you -like better. - -What do you think is the more commonly used pattern under Rust developers? -""" - -# MOVE SEMANTICS - -[[exercises]] -name = "move_semantics1" -dir = "06_move_semantics" -hint = """ -So you've got the "cannot borrow immutable local variable `vec` as mutable" -error on the line where we push an element to the vector, right? - -The fix for this is going to be adding one keyword, and the addition is NOT on -the line where we push to the vector (where the error is). - -Also: Try accessing `vec0` after having called `fill_vec()`. See what -happens!""" - -[[exercises]] -name = "move_semantics2" -dir = "06_move_semantics" -hint = """ -When running this exercise for the first time, you'll notice an error about -"borrow of moved value". In Rust, when an argument is passed to a function and -it's not explicitly returned, you can't use the original variable anymore. -We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's -being "moved" into `vec1`, meaning we can't access `vec0` anymore after the -fact. - -Rust provides a couple of different ways to mitigate this issue, feel free to -try them all: -1. You could make another, separate version of the data that's in `vec0` and - pass that to `fill_vec` instead. -2. Make `fill_vec` borrow its argument instead of taking ownership of it, - and then copy the data within the function (`vec.clone()`) in order to - return an owned `Vec`. -""" - -[[exercises]] -name = "move_semantics3" -dir = "06_move_semantics" -hint = """ -The difference between this one and the previous ones is that the first line -of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, -instead of adding that line back, add `mut` in one place that will change -an existing binding to be a mutable binding instead of an immutable one :)""" - -[[exercises]] -name = "move_semantics4" -dir = "06_move_semantics" -hint = """ -Stop reading whenever you feel like you have enough direction :) Or try -doing one step and then fixing the compiler errors that result! -So the end goal is to: - - get rid of the first line in main that creates the new vector - - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` - - `fill_vec` has had its signature changed, which our call should reflect - - since we're not creating a new vec in `main` anymore, we need to create - a new vec in `fill_vec`, and fill it with the expected values""" - -[[exercises]] -name = "move_semantics5" -dir = "06_move_semantics" -hint = """ -Carefully reason about the range in which each mutable reference is in -scope. Does it help to update the value of referent (`x`) immediately after -the mutable reference is taken? Read more about 'Mutable References' -in the book's section 'References and Borrowing': -https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. -""" - -[[exercises]] -name = "move_semantics6" -dir = "06_move_semantics" -test = false -hint = """ -To find the answer, you can consult the book section "References and Borrowing": -https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html - -The first problem is that `get_char` is taking ownership of the string. So -`data` is moved and can't be used for `string_uppercase`. `data` is moved to -`get_char` first, meaning that `string_uppercase` cannot manipulate the data. - -Once you've fixed that, `string_uppercase`'s function signature will also need -to be adjusted. - -Can you figure out how? - -Another hint: it has to do with the `&` character.""" - -# STRUCTS - -[[exercises]] -name = "structs1" -dir = "07_structs" -hint = """ -Rust has more than one type of struct. Three actually, all variants are used to -package related data together. - -There are normal (or classic) structs. These are named collections of related -data stored in fields. - -Tuple structs are basically just named tuples. - -Finally, Unit-like structs. These don't have any fields and are useful for -generics. - -In this exercise you need to complete and implement one of each kind. -Read more about structs in The Book: -https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" - -[[exercises]] -name = "structs2" -dir = "07_structs" -hint = """ -Creating instances of structs is easy, all you need to do is assign some values -to its fields. - -There are however some shortcuts that can be taken when instantiating structs. -Have a look in The Book, to find out more: -https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" - -[[exercises]] -name = "structs3" -dir = "07_structs" -hint = """ -For `is_international`: What makes a package international? Seems related to -the places it goes through right? - -For `get_fees`: This method takes an additional argument, is there a field in -the `Package` struct that this relates to? - -Have a look in The Book, to find out more about method implementations: -https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" - -# ENUMS - -[[exercises]] -name = "enums1" -dir = "08_enums" -test = false -hint = "No hints this time ;)" - -[[exercises]] -name = "enums2" -dir = "08_enums" -test = false -hint = """ -You can create enumerations that have different variants with different types -such as no data, anonymous structs, a single string, tuples, ...etc""" - -[[exercises]] -name = "enums3" -dir = "08_enums" -hint = """ -As a first step, you can define enums to compile this code without errors. - -And then create a match expression in `process()`. - -Note that you need to deconstruct some message variants in the match expression -to get value in the variant.""" - -# STRINGS - -[[exercises]] -name = "strings1" -dir = "09_strings" -test = false -hint = """ -The `current_favorite_color` function is currently returning a string slice -with the `'static` lifetime. We know this because the data of the string lives -in our code itself -- it doesn't come from a file or user input or another -program -- so it will live as long as our program lives. - -But it is still a string slice. There's one way to create a `String` by -converting a string slice covered in the Strings chapter of the book, and -another way that uses the `From` trait.""" - -[[exercises]] -name = "strings2" -dir = "09_strings" -test = false -hint = """ -Yes, it would be really easy to fix this by just changing the value bound to -`word` to be a string slice instead of a `String`, wouldn't it?? There is a way -to add one character to the `if` statement, though, that will coerce the -`String` into a string slice. - -Side note: If you're interested in learning about how this kind of reference -conversion works, you can jump ahead in the book and read this part in the -smart pointers chapter: -https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" - -[[exercises]] -name = "strings3" -dir = "09_strings" -hint = """ -There's tons of useful standard library functions for strings. Let's try and use some of them: -https://doc.rust-lang.org/std/string/struct.String.html#method.trim - -For the `compose_me` method: You can either use the `format!` macro, or convert -the string slice into an owned string, which you can then freely extend.""" - -[[exercises]] -name = "strings4" -dir = "09_strings" -test = false -hint = "No hints this time ;)" - -# MODULES - -[[exercises]] -name = "modules1" -dir = "10_modules" -test = false -hint = """ -Everything is private in Rust by default-- but there's a keyword we can use -to make something public! The compiler error should point to the thing that -needs to be public.""" - -[[exercises]] -name = "modules2" -dir = "10_modules" -test = false -hint = """ -The delicious_snacks module is trying to present an external interface that is -different than its internal structure (the `fruits` and `veggies` modules and -associated constants). Complete the `use` statements to fit the uses in main and -find the one keyword missing for both constants. - -Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" - -[[exercises]] -name = "modules3" -dir = "10_modules" -test = false -hint = """ -`UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a -`use` statement for these two to bring them into scope. You can use nested -paths or the glob operator to bring these two in using only one line.""" - -# HASHMAPS - -[[exercises]] -name = "hashmaps1" -dir = "11_hashmaps" -hint = """ -Hint 1: Take a look at the return type of the function to figure out - the type for the `basket`. - -Hint 2: Number of fruits should be at least 5. And you have to put - at least three different types of fruits. -""" - -[[exercises]] -name = "hashmaps2" -dir = "11_hashmaps" -hint = """ -Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value -""" - -[[exercises]] -name = "hashmaps3" -dir = "11_hashmaps" -hint = """ -Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert - entries corresponding to each team in the scores table. - -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value - -Hint 2: If there is already an entry for a given key, the value returned by - `entry()` can be updated based on the existing value. - -Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value -""" - -# QUIZ 2 - -[[exercises]] -name = "quiz2" -dir = "quizzes" -hint = "No hints this time ;)" - -# OPTIONS - -[[exercises]] -name = "options1" -dir = "12_options" -hint = """ -Options can have a `Some` value, with an inner value, or a `None` value, -without an inner value. - -There's multiple ways to get at the inner value, you can use `unwrap`, or -pattern match. Unwrapping is the easiest, but how do you do it safely so that -it doesn't panic in your face later?""" - -[[exercises]] -name = "options2" -dir = "12_options" -hint = """ -Check out: - -- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html -- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html - -Remember that `Option`s can be stacked in `if let` and `while let`. - -For example: `Some(Some(variable)) = variable2` - -Also see `Option::flatten` -""" - -[[exercises]] -name = "options3" -dir = "12_options" -test = false -hint = """ -The compiler says a partial move happened in the `match` statement. How can -this be avoided? The compiler shows the correction needed. - -After making the correction as suggested by the compiler, do read: -https://doc.rust-lang.org/std/keyword.ref.html""" - -# ERROR HANDLING - -[[exercises]] -name = "errors1" -dir = "13_error_handling" -hint = """ -`Ok` and `Err` are the two variants of `Result`, so what the tests are saying -is that `generate_nametag_text` should return a `Result` instead of an `Option`. - -To make this change, you'll need to: - - update the return type in the function signature to be a `Result` that could be the variants `Ok(String)` and `Err(String)` - - change the body of the function to return `Ok(stuff)` where it currently - returns `Some(stuff)` - - change the body of the function to return `Err(error message)` where it - currently returns `None`""" - -[[exercises]] -name = "errors2" -dir = "13_error_handling" -hint = """ -One way to handle this is using a `match` statement on -`item_quantity.parse::()` where the cases are `Ok(something)` and -`Err(something)`. - -This pattern is very common in Rust, though, so there's a `?` operator that -does pretty much what you would make that match statement do for you! - -Take a look at this section of the 'Error Handling' chapter: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -and give it a try!""" - -[[exercises]] -name = "errors3" -dir = "13_error_handling" -test = false -hint = """ -If other functions can return a `Result`, why shouldn't `main`? It's a fairly -common convention to return something like `Result<(), ErrorType>` from your -main function. - -The unit (`()`) type is there because nothing is really needed in terms of -positive results.""" - -[[exercises]] -name = "errors4" -dir = "13_error_handling" -hint = """ -`PositiveNonzeroInteger::new` is always creating a new instance and returning -an `Ok` result. - -It should be doing some checking, returning an `Err` result if those checks -fail, and only returning an `Ok` result if those checks determine that -everything is... okay :)""" - -[[exercises]] -name = "errors5" -dir = "13_error_handling" -test = false -hint = """ -There are two different possible `Result` types produced within `main()`, which -are propagated using `?` operators. How do we declare a return type from -`main()` that allows both? - -Under the hood, the `?` operator calls `From::from` on the error value to -convert it to a boxed trait object, a `Box`. This boxed trait -object is polymorphic, and since all errors implement the `error::Error` trait, -we can capture lots of different errors in one "Box" object. - -Check out this section of the book: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator - -Read more about boxing errors: -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html - -Read more about using the `?` operator with boxed errors: -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" - -[[exercises]] -name = "errors6" -dir = "13_error_handling" -hint = """ -This exercise uses a completed version of `PositiveNonzeroInteger` from -errors4. - -Below the line that `TODO` asks you to change, there is an example of using -the `map_err()` method on a `Result` to transform one type of error into -another. Try using something similar on the `Result` from `parse()`. You -might use the `?` operator to return early from the function, or you might -use a `match` expression, or maybe there's another way! - -You can create another function inside `impl ParsePosNonzeroError` to use -with `map_err()`. - -Read more about `map_err()` in the `std::result` documentation: -https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" - -# Generics - -[[exercises]] -name = "generics1" -dir = "14_generics" -test = false -hint = """ -Vectors in Rust make use of generics to create dynamically sized arrays of any -type. - -You need to tell the compiler what type we are pushing onto this vector.""" - -[[exercises]] -name = "generics2" -dir = "14_generics" -hint = """ -Currently we are wrapping only values of type `u32`. - -Maybe we could update the explicit references to this data type somehow? - -If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions -""" - -# TRAITS - -[[exercises]] -name = "traits1" -dir = "15_traits" -hint = """ -A discussion about Traits in Rust can be found at: -https://doc.rust-lang.org/book/ch10-02-traits.html -""" - -[[exercises]] -name = "traits2" -dir = "15_traits" -hint = """ -Notice how the trait takes ownership of `self`, and returns `Self`. - -Try mutating the incoming string vector. Have a look at the tests to see -what the result should look like! - -Vectors provide suitable methods for adding an element at the end. See -the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" - -[[exercises]] -name = "traits3" -dir = "15_traits" -hint = """ -Traits can have a default implementation for functions. Structs that implement -the trait can then use the default version of these functions if they choose not -to implement the function themselves. - -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations -""" - -[[exercises]] -name = "traits4" -dir = "15_traits" -hint = """ -Instead of using concrete types as parameters you can use traits. Try replacing -the '??' with 'impl ' - -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters -""" - -[[exercises]] -name = "traits5" -dir = "15_traits" -test = false -hint = """ -To ensure a parameter implements multiple traits use the '+ syntax'. Try -replacing the '??' with 'impl <> + <>'. - -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax -""" - -# QUIZ 3 - -[[exercises]] -name = "quiz3" -dir = "quizzes" -hint = """ -To find the best solution to this challenge you're going to need to think back -to your knowledge of traits, specifically 'Trait Bound Syntax' - -You may also need this: `use std::fmt::Display;`.""" - -# LIFETIMES - -[[exercises]] -name = "lifetimes1" -dir = "16_lifetimes" -test = false -hint = """ -Let the compiler guide you. Also take a look at the book if you need help: -https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" - -[[exercises]] -name = "lifetimes2" -dir = "16_lifetimes" -test = false -hint = """ -Remember that the generic lifetime `'a` will get the concrete lifetime that is -equal to the smaller of the lifetimes of `x` and `y`. - -You can take at least two paths to achieve the desired result while keeping the -inner block: -1. Move the `string2` declaration to make it live as long as `string1` (how is - `result` declared?) -2. Move `println!` into the inner block""" - -[[exercises]] -name = "lifetimes3" -dir = "16_lifetimes" -test = false -hint = """ -If you use a lifetime annotation in a struct's fields, where else does it need -to be added?""" - -# TESTS - -[[exercises]] -name = "tests1" -dir = "17_tests" -hint = """ -You don't even need to write any code to test -- you can just test values and -run that, even though you wouldn't do that in real life. :) - -`assert!` is a macro that needs an argument. Depending on the value of the -argument, `assert!` will do nothing (in which case the test will pass) or -`assert!` will panic (in which case the test will fail). - -So try giving different values to `assert!` and see which ones compile, which -ones pass, and which ones fail :)""" - -[[exercises]] -name = "tests2" -dir = "17_tests" -hint = """ -Like the previous exercise, you don't need to write any code to get this test -to compile and run. - -`assert_eq!` is a macro that takes two arguments and compares them. Try giving -it two values that are equal! Try giving it two arguments that are different! -Try giving it two values that are of different types! Try switching which -argument comes first and which comes second!""" - -[[exercises]] -name = "tests3" -dir = "17_tests" -hint = """ -You can call a function right where you're passing arguments to `assert!`. So -you could do something like `assert!(having_fun())`. - -If you want to check that you indeed get `false`, you can negate the result of -what you're doing using `!`, like `assert!(!having_fun())`.""" - -[[exercises]] -name = "tests4" -dir = "17_tests" -hint = """ -We expect method `Rectangle::new()` to panic for negative values. - -To handle that you need to add a special attribute to the test function. - -You can refer to the docs: -https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" - -# STANDARD LIBRARY TYPES - -[[exercises]] -name = "iterators1" -dir = "18_iterators" -hint = """ -Step 1: - -We need to apply something to the collection `my_fav_fruits` before we start to -go through it. What could that be? Take a look at the struct definition for a -vector for inspiration: -https://doc.rust-lang.org/std/vec/struct.Vec.html - -Step 2 & step 3: - -Very similar to the lines above and below. You've got this! - -Step 4: - -An iterator goes through all elements in a collection, but what if we've run -out of elements? What should we expect here? If you're stuck, take a look at -https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. -""" - -[[exercises]] -name = "iterators2" -dir = "18_iterators" -hint = """ -Step 1: - -The variable `first` is a `char`. It needs to be capitalized and added to the -remaining characters in `c` in order to return the correct `String`. - -The remaining characters in `c` can be viewed as a string slice using the -`as_str` method. - -The documentation for `char` contains many useful methods. -https://doc.rust-lang.org/std/primitive.char.html - -Step 2: - -Create an iterator from the slice. Transform the iterated values by applying -the `capitalize_first` function. Remember to `collect` the iterator. - -Step 3: - -This is surprisingly similar to the previous solution. `collect` is very -powerful and very general. Rust just needs to know the desired type.""" - -[[exercises]] -name = "iterators3" -dir = "18_iterators" -hint = """ -The `divide` function needs to return the correct error when even division is -not possible. - -The `division_results` variable needs to be collected into a collection type. - -The `result_with_list` function needs to return a single `Result` where the -success case is a vector of integers and the failure case is a `DivisionError`. - -The `list_of_results` function needs to return a vector of results. - -See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for -how the `FromIterator` trait is used in `collect()`. This trait is REALLY -powerful! It can make the solution to this exercise infinitely easier.""" - -[[exercises]] -name = "iterators4" -dir = "18_iterators" -hint = """ -In an imperative language, you might write a `for` loop that updates a mutable -variable. Or, you might write code utilizing recursion and a match clause. In -Rust you can take another functional approach, computing the factorial -elegantly with ranges and iterators. - -Hint 2: Check out the `fold` and `rfold` methods!""" - -[[exercises]] -name = "iterators5" -dir = "18_iterators" -hint = """ -The documentation for the `std::iter::Iterator` trait contains numerous methods -that would be helpful here. - -The `collection` variable in `count_collection_iterator` is a slice of -`HashMap`s. It needs to be converted into an iterator in order to use the -iterator methods. - -The `fold` method can be useful in the `count_collection_iterator` function. - -For a further challenge, consult the documentation for `Iterator` to find -a different method that could make your code more compact than using `fold`.""" - -# SMART POINTERS - -[[exercises]] -name = "box1" -dir = "19_smart_pointers" -hint = """ -Step 1: - -The compiler's message should help: since we cannot store the value of the -actual type when working with recursive types, we need to store a reference -(pointer) to its value. - -We should, therefore, place our `List` inside a `Box`. More details in the book -here: https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2: - -Creating an empty list should be fairly straightforward (hint: peek at the -assertions). - -For a non-empty list keep in mind that we want to use our `Cons` "list builder". -Although the current list is one of integers (`i32`), feel free to change the -definition and try other types! -""" - -[[exercises]] -name = "rc1" -dir = "19_smart_pointers" -hint = """ -This is a straightforward exercise to use the `Rc` type. Each `Planet` has -ownership of the `Sun`, and uses `Rc::clone()` to increment the reference count -of the `Sun`. - -After using `drop()` to move the `Planet`s out of scope individually, the -reference count goes down. - -In the end the `Sun` only has one reference again, to itself. - -See more at: https://doc.rust-lang.org/book/ch15-04-rc.html - -* Unfortunately Pluto is no longer considered a planet :( -""" - -[[exercises]] -name = "arc1" -dir = "19_smart_pointers" -test = false -hint = """ -Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order -to avoid creating a copy of `numbers`, you'll need to create `child_numbers` -inside the loop but still in the main thread. - -`child_numbers` should be a clone of the `Arc` of the numbers instead of a -thread-local copy of the numbers. - -This is a simple exercise if you understand the underlying concepts, but if this -is too much of a struggle, consider reading through all of Chapter 16 in the -book: -https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html -""" - -[[exercises]] -name = "cow1" -dir = "19_smart_pointers" -hint = """ -If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is -called. - -Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation -on the `Cow` type. -""" - -# THREADS - -[[exercises]] -name = "threads1" -dir = "20_threads" -test = false -hint = """ -`JoinHandle` is a struct that is returned from a spawned thread: -https://doc.rust-lang.org/std/thread/fn.spawn.html - -A challenge with multi-threaded applications is that the main thread can -finish before the spawned threads are completed. -https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles - -Use the `JoinHandle`s to wait for each thread to finish and collect their -results. - -https://doc.rust-lang.org/std/thread/struct.JoinHandle.html -""" - -[[exercises]] -name = "threads2" -dir = "20_threads" -test = false -hint = """ -`Arc` is an Atomic Reference Counted pointer that allows safe, shared access -to **immutable** data. But we want to *change* the number of `jobs_completed` -so we'll need to also use another type that will only allow one thread to -mutate the data at a time. Take a look at this section of the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct - -Keep reading if you'd like more hints :) - -Do you now have an `Arc>` at the beginning of `main`? Like: -``` -let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); -``` - -Similar to the code in the following example in the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads -""" - -[[exercises]] -name = "threads3" -dir = "20_threads" -hint = """ -An alternate way to handle concurrency between threads is to use an `mpsc` -(multiple producer, single consumer) channel to communicate. - -With both a sending end and a receiving end, it's possible to send values in -one thread and receive them in another. - -Multiple producers are possible by using clone() to create a duplicate of the -original sending end. - -See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. -""" - -# MACROS - -[[exercises]] -name = "macros1" -dir = "21_macros" -test = false -hint = """ -When you call a macro, you need to add something special compared to a -regular function call. If you're stuck, take a look at what's inside -`my_macro`.""" - -[[exercises]] -name = "macros2" -dir = "21_macros" -test = false -hint = """ -Macros don't quite play by the same rules as the rest of Rust, in terms of -what's available where. - -Unlike other things in Rust, the order of "where you define a macro" versus -"where you use it" actually matters.""" - -[[exercises]] -name = "macros3" -dir = "21_macros" -test = false -hint = """ -In order to use a macro outside of its module, you need to do something -special to the module to lift the macro out into its parent. - -The same trick also works on "extern crate" statements for crates that have -exported macros, if you've seen any of those around.""" - -[[exercises]] -name = "macros4" -dir = "21_macros" -test = false -hint = """ -You only need to add a single character to make this compile. - -The way macros are written, it wants to see something between each "macro arm", -so it can separate them. - -That's all the macro exercises we have in here, but it's barely even scratching -the surface of what you can do with Rust's macros. For a more thorough -introduction, you can have a read through 'The Little Book of Rust Macros': -https://veykril.github.io/tlborm/""" - -# CLIPPY - -[[exercises]] -name = "clippy1" -dir = "22_clippy" -test = false -strict_clippy = true -hint = """ -Rust stores the highest precision version of any long or infinite precision -mathematical constants in the Rust standard library: -https://doc.rust-lang.org/stable/std/f32/consts/index.html - -We may be tempted to use our own approximations for certain mathematical -constants, but clippy recognizes those imprecise mathematical constants as a -source of potential error. - -See the suggestions of the clippy warning in compile output and use the -appropriate replacement constant from `std::f32::consts`...""" - -[[exercises]] -name = "clippy2" -dir = "22_clippy" -test = false -strict_clippy = true -hint = """ -`for` loops over `Option` values are more clearly expressed as an `if let`""" - -[[exercises]] -name = "clippy3" -dir = "22_clippy" -test = false -strict_clippy = true -hint = "No hints this time!" - -# TYPE CONVERSIONS - -[[exercises]] -name = "using_as" -dir = "23_conversions" -hint = """ -Use the `as` operator to cast one of the operands in the last line of the -`average` function into the expected return type.""" - -[[exercises]] -name = "from_into" -dir = "23_conversions" -hint = """ -Follow the steps provided right before the `From` implementation""" - -[[exercises]] -name = "from_str" -dir = "23_conversions" -hint = """ -The implementation of `FromStr` should return an `Ok` with a `Person` object, -or an `Err` with an error if the string is not valid. - -This is almost like the `from_into` exercise, but returning errors instead -of falling back to a default value. - -Look at the test cases to see which error variants to return. - -Another hint: You can use the `map_err` method of `Result` with a function -or a closure to wrap the error from `parse::`. - -Yet another hint: If you would like to propagate errors by using the `?` -operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" - -[[exercises]] -name = "try_from_into" -dir = "23_conversions" -hint = """ -Follow the steps provided right before the `TryFrom` implementation. -You can also use the example at -https://doc.rust-lang.org/std/convert/trait.TryFrom.html - -Is there an implementation of `TryFrom` in the standard library that -can both do the required integer conversion and check the range of the input? - -Another hint: Look at the test cases to see which error variants to return. - -Yet another hint: You can use the `map_err` or `or` methods of `Result` to -convert errors. - -Yet another hint: If you would like to propagate errors by using the `?` -operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html - -Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" - -[[exercises]] -name = "as_ref_mut" -dir = "23_conversions" -hint = """ -Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml deleted file mode 120000 index 3795291236..0000000000 --- a/rustlings-macros/info.toml +++ /dev/null @@ -1 +0,0 @@ -../info.toml \ No newline at end of file diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml new file mode 100644 index 0000000000..4204f27433 --- /dev/null +++ b/rustlings-macros/info.toml @@ -0,0 +1,1286 @@ +format_version = 1 + +welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners! +We are going to teach you a lot of things about Rust, but before we can +get started, here are some notes about how Rustlings operates: + +1. The central concept behind Rustlings is that you solve exercises. These + exercises usually contain some compiler or logic errors which cause the + exercise to fail compilation or testing. It's your job to find all errors + and fix them! +2. Make sure to have your editor open in the `rustlings/` directory. Rustlings + will show you the path of the current exercise under the progress bar. Open + the exercise file in your editor, fix errors and save the file. Rustlings will + automatically detect the file change and rerun the exercise. If all errors are + fixed, Rustlings will ask you to move on to the next exercise. +3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. +4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! + (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, + other learners do too so you can help each other out! +""" + +final_message = """We hope you enjoyed learning about the various aspects of Rust! +If you noticed any issues, don't hesitate to report them on Github. +You can also contribute your own exercises to help the greater community! + +Before reporting an issue or contributing, please read our guidelines: +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md +""" + +# INTRO + +# TODO: Update exercise +[[exercises]] +name = "intro1" +dir = "00_intro" +test = false +# TODO: Fix hint +hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" + +[[exercises]] +name = "intro2" +dir = "00_intro" +test = false +hint = """ +The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" + +# VARIABLES + +[[exercises]] +name = "variables1" +dir = "01_variables" +test = false +hint = """ +The declaration in the first line in the main function is missing a keyword +that is needed in Rust to create a new variable binding.""" + +[[exercises]] +name = "variables2" +dir = "01_variables" +test = false +hint = """ +The compiler message is saying that Rust cannot infer the type that the +variable binding `x` has with what is given here. + +What happens if you annotate the first line in the main function with a type +annotation? + +What if you give `x` a value? + +What if you do both? + +What type should `x` be, anyway? + +What if `x` is the same type as `10`? What if it's a different type?""" + +[[exercises]] +name = "variables3" +dir = "01_variables" +test = false +hint = """ +Oops! In this exercise, we have a variable binding that we've created on in the +first line in the `main` function, and we're trying to use it in the next line, +but we haven't given it a value. + +We can't print out something that isn't there; try giving `x` a value! + +This is an error that can cause bugs that's very easy to make in any +programming language -- thankfully the Rust compiler has caught this for us!""" + +[[exercises]] +name = "variables4" +dir = "01_variables" +test = false +hint = """ +In Rust, variable bindings are immutable by default. But here we're trying +to reassign a different value to `x`! There's a keyword we can use to make +a variable binding mutable instead.""" + +[[exercises]] +name = "variables5" +dir = "01_variables" +test = false +hint = """ +In `variables4` we already learned how to make an immutable variable mutable +using a special keyword. Unfortunately this doesn't help us much in this +exercise because we want to assign a different typed value to an existing +variable. Sometimes you may also like to reuse existing variable names because +you are just converting values to different types like in this exercise. + +Fortunately Rust has a powerful solution to this problem: 'Shadowing'! +You can read more about 'Shadowing' in the book's section 'Variables and +Mutability': +https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing + +Try to solve this exercise afterwards using this technique.""" + +[[exercises]] +name = "variables6" +dir = "01_variables" +test = false +hint = """ +We know about variables and mutability, but there is another important type of +variable available: constants. + +Constants are always immutable and they are declared with keyword `const` rather +than keyword `let`. + +Constants types must also always be annotated. + +Read more about constants and the differences between variables and constants +under 'Constants' in the book's section 'Variables and Mutability': +https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants +""" + +# FUNCTIONS + +[[exercises]] +name = "functions1" +dir = "02_functions" +test = false +hint = """ +This main function is calling a function that it expects to exist, but the +function doesn't exist. It expects this function to have the name `call_me`. +It expects this function to not take any arguments and not return a value. +Sounds a lot like `main`, doesn't it?""" + +[[exercises]] +name = "functions2" +dir = "02_functions" +test = false +hint = """ +Rust requires that all parts of a function's signature have type annotations, +but `call_me` is missing the type annotation of `num`.""" + +[[exercises]] +name = "functions3" +dir = "02_functions" +test = false +hint = """ +This time, the function *declaration* is okay, but there's something wrong +with the place where we're calling the function.""" + +[[exercises]] +name = "functions4" +dir = "02_functions" +test = false +hint = """ +The error message points to the function `sale_price` and says it expects a type +after the `->`. This is where the function's return type should be -- take a +look at the `is_even` function for an example!""" + +[[exercises]] +name = "functions5" +dir = "02_functions" +test = false +hint = """ +This is a really common error that can be fixed by removing one character. +It happens because Rust distinguishes between expressions and statements: +expressions return a value based on their operand(s), and statements simply +return a `()` type which behaves just like `void` in C/C++ language. + +We want to return a value of `i32` type from the `square` function, but it is +returning a `()` type... + +They are not the same. There are two solutions: +1. Add a `return` ahead of `num * num;` +2. remove `;`, make it to be `num * num`""" + +# IF + +[[exercises]] +name = "if1" +dir = "03_if" +hint = """ +It's possible to do this in one line if you would like! + +Some similar examples from other languages: +- In C(++) this would be: `a > b ? a : b` +- In Python this would be: `a if a > b else b` + +Remember in Rust that: +- the `if` condition does not need to be surrounded by parentheses +- `if`/`else` conditionals are expressions +- Each condition is followed by a `{}` block.""" + +[[exercises]] +name = "if2" +dir = "03_if" +hint = """ +For that first compiler error, it's important in Rust that each conditional +block returns the same type! To get the tests passing, you will need a couple +conditions checking different input values.""" + +[[exercises]] +name = "if3" +dir = "03_if" +hint = """ +In Rust, every arm of an `if` expression has to return the same type of value. +Make sure the type is consistent across all arms.""" + +# QUIZ 1 + +[[exercises]] +name = "quiz1" +dir = "quizzes" +hint = "No hints this time ;)" + +# PRIMITIVE TYPES + +[[exercises]] +name = "primitive_types1" +dir = "04_primitive_types" +test = false +hint = "No hints this time ;)" + +[[exercises]] +name = "primitive_types2" +dir = "04_primitive_types" +test = false +hint = "No hints this time ;)" + +[[exercises]] +name = "primitive_types3" +dir = "04_primitive_types" +test = false +hint = """ +There's a shorthand to initialize Arrays with a certain size that does not +require you to type in 100 items (but you certainly can if you want!). + +For example, you can do: +``` +let array = ["Are we there yet?"; 10]; +``` + +Bonus: what are some other things you could have that would return `true` +for `a.len() >= 100`?""" + +[[exercises]] +name = "primitive_types4" +dir = "04_primitive_types" +hint = """ +Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section +of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the +starting and ending (plus one) indices of the items in the `Array` that you +want to end up in the slice. + +If you're curious why the first argument of `assert_eq!` does not have an +ampersand for a reference since the second argument is a reference, take a look +at the coercion chapter of the nomicon: +https://doc.rust-lang.org/nomicon/coercions.html""" + +[[exercises]] +name = "primitive_types5" +dir = "04_primitive_types" +test = false +hint = """ +Take a look at the 'Data Types -> The Tuple Type' section of the book: +https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type +Particularly the part about destructuring (second to last example in the +section). + +You'll need to make a pattern to bind `name` and `age` to the appropriate parts +of the tuple. You can do it!!""" + +[[exercises]] +name = "primitive_types6" +dir = "04_primitive_types" +hint = """ +While you could use a destructuring `let` for the tuple here, try +indexing into it instead, as explained in the last example of the +'Data Types -> The Tuple Type' section of the book: +https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type +Now you have another tool in your toolbox!""" + +# VECS + +[[exercises]] +name = "vecs1" +dir = "05_vecs" +hint = """ +In Rust, there are two ways to define a Vector. +1. One way is to use the `Vec::new()` function to create a new vector + and fill it with the `push()` method. +2. The second way, which is simpler is to use the `vec![]` macro and + define your elements inside the square brackets. + +Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html +of the Rust book to learn more. +""" + +[[exercises]] +name = "vecs2" +dir = "05_vecs" +hint = """ +In the first function we are looping over the Vector and getting a reference to +one `element` at a time. + +To modify the value of that `element` we need to use the `*` dereference +operator. You can learn more in this chapter of the Rust book: +https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector + +In the second function this dereferencing is not necessary, because the `map` +function expects the new value to be returned. + +After you've completed both functions, decide for yourself which approach you +like better. + +What do you think is the more commonly used pattern under Rust developers? +""" + +# MOVE SEMANTICS + +[[exercises]] +name = "move_semantics1" +dir = "06_move_semantics" +hint = """ +So you've got the "cannot borrow immutable local variable `vec` as mutable" +error on the line where we push an element to the vector, right? + +The fix for this is going to be adding one keyword, and the addition is NOT on +the line where we push to the vector (where the error is). + +Also: Try accessing `vec0` after having called `fill_vec()`. See what +happens!""" + +[[exercises]] +name = "move_semantics2" +dir = "06_move_semantics" +hint = """ +When running this exercise for the first time, you'll notice an error about +"borrow of moved value". In Rust, when an argument is passed to a function and +it's not explicitly returned, you can't use the original variable anymore. +We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's +being "moved" into `vec1`, meaning we can't access `vec0` anymore after the +fact. + +Rust provides a couple of different ways to mitigate this issue, feel free to +try them all: +1. You could make another, separate version of the data that's in `vec0` and + pass that to `fill_vec` instead. +2. Make `fill_vec` borrow its argument instead of taking ownership of it, + and then copy the data within the function (`vec.clone()`) in order to + return an owned `Vec`. +""" + +[[exercises]] +name = "move_semantics3" +dir = "06_move_semantics" +hint = """ +The difference between this one and the previous ones is that the first line +of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, +instead of adding that line back, add `mut` in one place that will change +an existing binding to be a mutable binding instead of an immutable one :)""" + +[[exercises]] +name = "move_semantics4" +dir = "06_move_semantics" +hint = """ +Stop reading whenever you feel like you have enough direction :) Or try +doing one step and then fixing the compiler errors that result! +So the end goal is to: + - get rid of the first line in main that creates the new vector + - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` + - `fill_vec` has had its signature changed, which our call should reflect + - since we're not creating a new vec in `main` anymore, we need to create + a new vec in `fill_vec`, and fill it with the expected values""" + +[[exercises]] +name = "move_semantics5" +dir = "06_move_semantics" +hint = """ +Carefully reason about the range in which each mutable reference is in +scope. Does it help to update the value of referent (`x`) immediately after +the mutable reference is taken? Read more about 'Mutable References' +in the book's section 'References and Borrowing': +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. +""" + +[[exercises]] +name = "move_semantics6" +dir = "06_move_semantics" +test = false +hint = """ +To find the answer, you can consult the book section "References and Borrowing": +https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html + +The first problem is that `get_char` is taking ownership of the string. So +`data` is moved and can't be used for `string_uppercase`. `data` is moved to +`get_char` first, meaning that `string_uppercase` cannot manipulate the data. + +Once you've fixed that, `string_uppercase`'s function signature will also need +to be adjusted. + +Can you figure out how? + +Another hint: it has to do with the `&` character.""" + +# STRUCTS + +[[exercises]] +name = "structs1" +dir = "07_structs" +hint = """ +Rust has more than one type of struct. Three actually, all variants are used to +package related data together. + +There are normal (or classic) structs. These are named collections of related +data stored in fields. + +Tuple structs are basically just named tuples. + +Finally, Unit-like structs. These don't have any fields and are useful for +generics. + +In this exercise you need to complete and implement one of each kind. +Read more about structs in The Book: +https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" + +[[exercises]] +name = "structs2" +dir = "07_structs" +hint = """ +Creating instances of structs is easy, all you need to do is assign some values +to its fields. + +There are however some shortcuts that can be taken when instantiating structs. +Have a look in The Book, to find out more: +https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" + +[[exercises]] +name = "structs3" +dir = "07_structs" +hint = """ +For `is_international`: What makes a package international? Seems related to +the places it goes through right? + +For `get_fees`: This method takes an additional argument, is there a field in +the `Package` struct that this relates to? + +Have a look in The Book, to find out more about method implementations: +https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" + +# ENUMS + +[[exercises]] +name = "enums1" +dir = "08_enums" +test = false +hint = "No hints this time ;)" + +[[exercises]] +name = "enums2" +dir = "08_enums" +test = false +hint = """ +You can create enumerations that have different variants with different types +such as no data, anonymous structs, a single string, tuples, ...etc""" + +[[exercises]] +name = "enums3" +dir = "08_enums" +hint = """ +As a first step, you can define enums to compile this code without errors. + +And then create a match expression in `process()`. + +Note that you need to deconstruct some message variants in the match expression +to get value in the variant.""" + +# STRINGS + +[[exercises]] +name = "strings1" +dir = "09_strings" +test = false +hint = """ +The `current_favorite_color` function is currently returning a string slice +with the `'static` lifetime. We know this because the data of the string lives +in our code itself -- it doesn't come from a file or user input or another +program -- so it will live as long as our program lives. + +But it is still a string slice. There's one way to create a `String` by +converting a string slice covered in the Strings chapter of the book, and +another way that uses the `From` trait.""" + +[[exercises]] +name = "strings2" +dir = "09_strings" +test = false +hint = """ +Yes, it would be really easy to fix this by just changing the value bound to +`word` to be a string slice instead of a `String`, wouldn't it?? There is a way +to add one character to the `if` statement, though, that will coerce the +`String` into a string slice. + +Side note: If you're interested in learning about how this kind of reference +conversion works, you can jump ahead in the book and read this part in the +smart pointers chapter: +https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" + +[[exercises]] +name = "strings3" +dir = "09_strings" +hint = """ +There's tons of useful standard library functions for strings. Let's try and use some of them: +https://doc.rust-lang.org/std/string/struct.String.html#method.trim + +For the `compose_me` method: You can either use the `format!` macro, or convert +the string slice into an owned string, which you can then freely extend.""" + +[[exercises]] +name = "strings4" +dir = "09_strings" +test = false +hint = "No hints this time ;)" + +# MODULES + +[[exercises]] +name = "modules1" +dir = "10_modules" +test = false +hint = """ +Everything is private in Rust by default-- but there's a keyword we can use +to make something public! The compiler error should point to the thing that +needs to be public.""" + +[[exercises]] +name = "modules2" +dir = "10_modules" +test = false +hint = """ +The delicious_snacks module is trying to present an external interface that is +different than its internal structure (the `fruits` and `veggies` modules and +associated constants). Complete the `use` statements to fit the uses in main and +find the one keyword missing for both constants. + +Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" + +[[exercises]] +name = "modules3" +dir = "10_modules" +test = false +hint = """ +`UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a +`use` statement for these two to bring them into scope. You can use nested +paths or the glob operator to bring these two in using only one line.""" + +# HASHMAPS + +[[exercises]] +name = "hashmaps1" +dir = "11_hashmaps" +hint = """ +Hint 1: Take a look at the return type of the function to figure out + the type for the `basket`. + +Hint 2: Number of fruits should be at least 5. And you have to put + at least three different types of fruits. +""" + +[[exercises]] +name = "hashmaps2" +dir = "11_hashmaps" +hint = """ +Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. +Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +""" + +[[exercises]] +name = "hashmaps3" +dir = "11_hashmaps" +hint = """ +Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert + entries corresponding to each team in the scores table. + +Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value + +Hint 2: If there is already an entry for a given key, the value returned by + `entry()` can be updated based on the existing value. + +Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value +""" + +# QUIZ 2 + +[[exercises]] +name = "quiz2" +dir = "quizzes" +hint = "No hints this time ;)" + +# OPTIONS + +[[exercises]] +name = "options1" +dir = "12_options" +hint = """ +Options can have a `Some` value, with an inner value, or a `None` value, +without an inner value. + +There's multiple ways to get at the inner value, you can use `unwrap`, or +pattern match. Unwrapping is the easiest, but how do you do it safely so that +it doesn't panic in your face later?""" + +[[exercises]] +name = "options2" +dir = "12_options" +hint = """ +Check out: + +- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html +- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html + +Remember that `Option`s can be stacked in `if let` and `while let`. + +For example: `Some(Some(variable)) = variable2` + +Also see `Option::flatten` +""" + +[[exercises]] +name = "options3" +dir = "12_options" +test = false +hint = """ +The compiler says a partial move happened in the `match` statement. How can +this be avoided? The compiler shows the correction needed. + +After making the correction as suggested by the compiler, do read: +https://doc.rust-lang.org/std/keyword.ref.html""" + +# ERROR HANDLING + +[[exercises]] +name = "errors1" +dir = "13_error_handling" +hint = """ +`Ok` and `Err` are the two variants of `Result`, so what the tests are saying +is that `generate_nametag_text` should return a `Result` instead of an `Option`. + +To make this change, you'll need to: + - update the return type in the function signature to be a `Result` that could be the variants `Ok(String)` and `Err(String)` + - change the body of the function to return `Ok(stuff)` where it currently + returns `Some(stuff)` + - change the body of the function to return `Err(error message)` where it + currently returns `None`""" + +[[exercises]] +name = "errors2" +dir = "13_error_handling" +hint = """ +One way to handle this is using a `match` statement on +`item_quantity.parse::()` where the cases are `Ok(something)` and +`Err(something)`. + +This pattern is very common in Rust, though, so there's a `?` operator that +does pretty much what you would make that match statement do for you! + +Take a look at this section of the 'Error Handling' chapter: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator +and give it a try!""" + +[[exercises]] +name = "errors3" +dir = "13_error_handling" +test = false +hint = """ +If other functions can return a `Result`, why shouldn't `main`? It's a fairly +common convention to return something like `Result<(), ErrorType>` from your +main function. + +The unit (`()`) type is there because nothing is really needed in terms of +positive results.""" + +[[exercises]] +name = "errors4" +dir = "13_error_handling" +hint = """ +`PositiveNonzeroInteger::new` is always creating a new instance and returning +an `Ok` result. + +It should be doing some checking, returning an `Err` result if those checks +fail, and only returning an `Ok` result if those checks determine that +everything is... okay :)""" + +[[exercises]] +name = "errors5" +dir = "13_error_handling" +test = false +hint = """ +There are two different possible `Result` types produced within `main()`, which +are propagated using `?` operators. How do we declare a return type from +`main()` that allows both? + +Under the hood, the `?` operator calls `From::from` on the error value to +convert it to a boxed trait object, a `Box`. This boxed trait +object is polymorphic, and since all errors implement the `error::Error` trait, +we can capture lots of different errors in one "Box" object. + +Check out this section of the book: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator + +Read more about boxing errors: +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html + +Read more about using the `?` operator with boxed errors: +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +""" + +[[exercises]] +name = "errors6" +dir = "13_error_handling" +hint = """ +This exercise uses a completed version of `PositiveNonzeroInteger` from +errors4. + +Below the line that `TODO` asks you to change, there is an example of using +the `map_err()` method on a `Result` to transform one type of error into +another. Try using something similar on the `Result` from `parse()`. You +might use the `?` operator to return early from the function, or you might +use a `match` expression, or maybe there's another way! + +You can create another function inside `impl ParsePosNonzeroError` to use +with `map_err()`. + +Read more about `map_err()` in the `std::result` documentation: +https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" + +# Generics + +[[exercises]] +name = "generics1" +dir = "14_generics" +test = false +hint = """ +Vectors in Rust make use of generics to create dynamically sized arrays of any +type. + +You need to tell the compiler what type we are pushing onto this vector.""" + +[[exercises]] +name = "generics2" +dir = "14_generics" +hint = """ +Currently we are wrapping only values of type `u32`. + +Maybe we could update the explicit references to this data type somehow? + +If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions +""" + +# TRAITS + +[[exercises]] +name = "traits1" +dir = "15_traits" +hint = """ +A discussion about Traits in Rust can be found at: +https://doc.rust-lang.org/book/ch10-02-traits.html +""" + +[[exercises]] +name = "traits2" +dir = "15_traits" +hint = """ +Notice how the trait takes ownership of `self`, and returns `Self`. + +Try mutating the incoming string vector. Have a look at the tests to see +what the result should look like! + +Vectors provide suitable methods for adding an element at the end. See +the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" + +[[exercises]] +name = "traits3" +dir = "15_traits" +hint = """ +Traits can have a default implementation for functions. Structs that implement +the trait can then use the default version of these functions if they choose not +to implement the function themselves. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations +""" + +[[exercises]] +name = "traits4" +dir = "15_traits" +hint = """ +Instead of using concrete types as parameters you can use traits. Try replacing +the '??' with 'impl ' + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +""" + +[[exercises]] +name = "traits5" +dir = "15_traits" +test = false +hint = """ +To ensure a parameter implements multiple traits use the '+ syntax'. Try +replacing the '??' with 'impl <> + <>'. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax +""" + +# QUIZ 3 + +[[exercises]] +name = "quiz3" +dir = "quizzes" +hint = """ +To find the best solution to this challenge you're going to need to think back +to your knowledge of traits, specifically 'Trait Bound Syntax' + +You may also need this: `use std::fmt::Display;`.""" + +# LIFETIMES + +[[exercises]] +name = "lifetimes1" +dir = "16_lifetimes" +test = false +hint = """ +Let the compiler guide you. Also take a look at the book if you need help: +https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" + +[[exercises]] +name = "lifetimes2" +dir = "16_lifetimes" +test = false +hint = """ +Remember that the generic lifetime `'a` will get the concrete lifetime that is +equal to the smaller of the lifetimes of `x` and `y`. + +You can take at least two paths to achieve the desired result while keeping the +inner block: +1. Move the `string2` declaration to make it live as long as `string1` (how is + `result` declared?) +2. Move `println!` into the inner block""" + +[[exercises]] +name = "lifetimes3" +dir = "16_lifetimes" +test = false +hint = """ +If you use a lifetime annotation in a struct's fields, where else does it need +to be added?""" + +# TESTS + +[[exercises]] +name = "tests1" +dir = "17_tests" +hint = """ +You don't even need to write any code to test -- you can just test values and +run that, even though you wouldn't do that in real life. :) + +`assert!` is a macro that needs an argument. Depending on the value of the +argument, `assert!` will do nothing (in which case the test will pass) or +`assert!` will panic (in which case the test will fail). + +So try giving different values to `assert!` and see which ones compile, which +ones pass, and which ones fail :)""" + +[[exercises]] +name = "tests2" +dir = "17_tests" +hint = """ +Like the previous exercise, you don't need to write any code to get this test +to compile and run. + +`assert_eq!` is a macro that takes two arguments and compares them. Try giving +it two values that are equal! Try giving it two arguments that are different! +Try giving it two values that are of different types! Try switching which +argument comes first and which comes second!""" + +[[exercises]] +name = "tests3" +dir = "17_tests" +hint = """ +You can call a function right where you're passing arguments to `assert!`. So +you could do something like `assert!(having_fun())`. + +If you want to check that you indeed get `false`, you can negate the result of +what you're doing using `!`, like `assert!(!having_fun())`.""" + +[[exercises]] +name = "tests4" +dir = "17_tests" +hint = """ +We expect method `Rectangle::new()` to panic for negative values. + +To handle that you need to add a special attribute to the test function. + +You can refer to the docs: +https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" + +# STANDARD LIBRARY TYPES + +[[exercises]] +name = "iterators1" +dir = "18_iterators" +hint = """ +Step 1: + +We need to apply something to the collection `my_fav_fruits` before we start to +go through it. What could that be? Take a look at the struct definition for a +vector for inspiration: +https://doc.rust-lang.org/std/vec/struct.Vec.html + +Step 2 & step 3: + +Very similar to the lines above and below. You've got this! + +Step 4: + +An iterator goes through all elements in a collection, but what if we've run +out of elements? What should we expect here? If you're stuck, take a look at +https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. +""" + +[[exercises]] +name = "iterators2" +dir = "18_iterators" +hint = """ +Step 1: + +The variable `first` is a `char`. It needs to be capitalized and added to the +remaining characters in `c` in order to return the correct `String`. + +The remaining characters in `c` can be viewed as a string slice using the +`as_str` method. + +The documentation for `char` contains many useful methods. +https://doc.rust-lang.org/std/primitive.char.html + +Step 2: + +Create an iterator from the slice. Transform the iterated values by applying +the `capitalize_first` function. Remember to `collect` the iterator. + +Step 3: + +This is surprisingly similar to the previous solution. `collect` is very +powerful and very general. Rust just needs to know the desired type.""" + +[[exercises]] +name = "iterators3" +dir = "18_iterators" +hint = """ +The `divide` function needs to return the correct error when even division is +not possible. + +The `division_results` variable needs to be collected into a collection type. + +The `result_with_list` function needs to return a single `Result` where the +success case is a vector of integers and the failure case is a `DivisionError`. + +The `list_of_results` function needs to return a vector of results. + +See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for +how the `FromIterator` trait is used in `collect()`. This trait is REALLY +powerful! It can make the solution to this exercise infinitely easier.""" + +[[exercises]] +name = "iterators4" +dir = "18_iterators" +hint = """ +In an imperative language, you might write a `for` loop that updates a mutable +variable. Or, you might write code utilizing recursion and a match clause. In +Rust you can take another functional approach, computing the factorial +elegantly with ranges and iterators. + +Hint 2: Check out the `fold` and `rfold` methods!""" + +[[exercises]] +name = "iterators5" +dir = "18_iterators" +hint = """ +The documentation for the `std::iter::Iterator` trait contains numerous methods +that would be helpful here. + +The `collection` variable in `count_collection_iterator` is a slice of +`HashMap`s. It needs to be converted into an iterator in order to use the +iterator methods. + +The `fold` method can be useful in the `count_collection_iterator` function. + +For a further challenge, consult the documentation for `Iterator` to find +a different method that could make your code more compact than using `fold`.""" + +# SMART POINTERS + +[[exercises]] +name = "box1" +dir = "19_smart_pointers" +hint = """ +Step 1: + +The compiler's message should help: since we cannot store the value of the +actual type when working with recursive types, we need to store a reference +(pointer) to its value. + +We should, therefore, place our `List` inside a `Box`. More details in the book +here: https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes + +Step 2: + +Creating an empty list should be fairly straightforward (hint: peek at the +assertions). + +For a non-empty list keep in mind that we want to use our `Cons` "list builder". +Although the current list is one of integers (`i32`), feel free to change the +definition and try other types! +""" + +[[exercises]] +name = "rc1" +dir = "19_smart_pointers" +hint = """ +This is a straightforward exercise to use the `Rc` type. Each `Planet` has +ownership of the `Sun`, and uses `Rc::clone()` to increment the reference count +of the `Sun`. + +After using `drop()` to move the `Planet`s out of scope individually, the +reference count goes down. + +In the end the `Sun` only has one reference again, to itself. + +See more at: https://doc.rust-lang.org/book/ch15-04-rc.html + +* Unfortunately Pluto is no longer considered a planet :( +""" + +[[exercises]] +name = "arc1" +dir = "19_smart_pointers" +test = false +hint = """ +Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order +to avoid creating a copy of `numbers`, you'll need to create `child_numbers` +inside the loop but still in the main thread. + +`child_numbers` should be a clone of the `Arc` of the numbers instead of a +thread-local copy of the numbers. + +This is a simple exercise if you understand the underlying concepts, but if this +is too much of a struggle, consider reading through all of Chapter 16 in the +book: +https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html +""" + +[[exercises]] +name = "cow1" +dir = "19_smart_pointers" +hint = """ +If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is +called. + +Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation +on the `Cow` type. +""" + +# THREADS + +[[exercises]] +name = "threads1" +dir = "20_threads" +test = false +hint = """ +`JoinHandle` is a struct that is returned from a spawned thread: +https://doc.rust-lang.org/std/thread/fn.spawn.html + +A challenge with multi-threaded applications is that the main thread can +finish before the spawned threads are completed. +https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles + +Use the `JoinHandle`s to wait for each thread to finish and collect their +results. + +https://doc.rust-lang.org/std/thread/struct.JoinHandle.html +""" + +[[exercises]] +name = "threads2" +dir = "20_threads" +test = false +hint = """ +`Arc` is an Atomic Reference Counted pointer that allows safe, shared access +to **immutable** data. But we want to *change* the number of `jobs_completed` +so we'll need to also use another type that will only allow one thread to +mutate the data at a time. Take a look at this section of the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct + +Keep reading if you'd like more hints :) + +Do you now have an `Arc>` at the beginning of `main`? Like: +``` +let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); +``` + +Similar to the code in the following example in the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads +""" + +[[exercises]] +name = "threads3" +dir = "20_threads" +hint = """ +An alternate way to handle concurrency between threads is to use an `mpsc` +(multiple producer, single consumer) channel to communicate. + +With both a sending end and a receiving end, it's possible to send values in +one thread and receive them in another. + +Multiple producers are possible by using clone() to create a duplicate of the +original sending end. + +See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. +""" + +# MACROS + +[[exercises]] +name = "macros1" +dir = "21_macros" +test = false +hint = """ +When you call a macro, you need to add something special compared to a +regular function call. If you're stuck, take a look at what's inside +`my_macro`.""" + +[[exercises]] +name = "macros2" +dir = "21_macros" +test = false +hint = """ +Macros don't quite play by the same rules as the rest of Rust, in terms of +what's available where. + +Unlike other things in Rust, the order of "where you define a macro" versus +"where you use it" actually matters.""" + +[[exercises]] +name = "macros3" +dir = "21_macros" +test = false +hint = """ +In order to use a macro outside of its module, you need to do something +special to the module to lift the macro out into its parent. + +The same trick also works on "extern crate" statements for crates that have +exported macros, if you've seen any of those around.""" + +[[exercises]] +name = "macros4" +dir = "21_macros" +test = false +hint = """ +You only need to add a single character to make this compile. + +The way macros are written, it wants to see something between each "macro arm", +so it can separate them. + +That's all the macro exercises we have in here, but it's barely even scratching +the surface of what you can do with Rust's macros. For a more thorough +introduction, you can have a read through 'The Little Book of Rust Macros': +https://veykril.github.io/tlborm/""" + +# CLIPPY + +[[exercises]] +name = "clippy1" +dir = "22_clippy" +test = false +strict_clippy = true +hint = """ +Rust stores the highest precision version of any long or infinite precision +mathematical constants in the Rust standard library: +https://doc.rust-lang.org/stable/std/f32/consts/index.html + +We may be tempted to use our own approximations for certain mathematical +constants, but clippy recognizes those imprecise mathematical constants as a +source of potential error. + +See the suggestions of the clippy warning in compile output and use the +appropriate replacement constant from `std::f32::consts`...""" + +[[exercises]] +name = "clippy2" +dir = "22_clippy" +test = false +strict_clippy = true +hint = """ +`for` loops over `Option` values are more clearly expressed as an `if let`""" + +[[exercises]] +name = "clippy3" +dir = "22_clippy" +test = false +strict_clippy = true +hint = "No hints this time!" + +# TYPE CONVERSIONS + +[[exercises]] +name = "using_as" +dir = "23_conversions" +hint = """ +Use the `as` operator to cast one of the operands in the last line of the +`average` function into the expected return type.""" + +[[exercises]] +name = "from_into" +dir = "23_conversions" +hint = """ +Follow the steps provided right before the `From` implementation""" + +[[exercises]] +name = "from_str" +dir = "23_conversions" +hint = """ +The implementation of `FromStr` should return an `Ok` with a `Person` object, +or an `Err` with an error if the string is not valid. + +This is almost like the `from_into` exercise, but returning errors instead +of falling back to a default value. + +Look at the test cases to see which error variants to return. + +Another hint: You can use the `map_err` method of `Result` with a function +or a closure to wrap the error from `parse::`. + +Yet another hint: If you would like to propagate errors by using the `?` +operator in your solution, you might want to look at +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +""" + +[[exercises]] +name = "try_from_into" +dir = "23_conversions" +hint = """ +Follow the steps provided right before the `TryFrom` implementation. +You can also use the example at +https://doc.rust-lang.org/std/convert/trait.TryFrom.html + +Is there an implementation of `TryFrom` in the standard library that +can both do the required integer conversion and check the range of the input? + +Another hint: Look at the test cases to see which error variants to return. + +Yet another hint: You can use the `map_err` or `or` methods of `Result` to +convert errors. + +Yet another hint: If you would like to propagate errors by using the `?` +operator in your solution, you might want to look at +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html + +Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" + +[[exercises]] +name = "as_ref_mut" +dir = "23_conversions" +hint = """ +Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 4417a4f90e..6c6067bc9d 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -15,7 +15,8 @@ struct InfoFile { #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { - let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) + let info_file = include_str!("../info.toml"); + let exercises = toml_edit::de::from_str::(info_file) .expect("Failed to parse `info.toml`") .exercises; @@ -46,6 +47,7 @@ pub fn include_files(_: TokenStream) -> TokenStream { quote! { EmbeddedFiles { + info_file: #info_file, exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files), dir_ind: #dir_inds }),*], exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*] } diff --git a/src/embedded.rs b/src/embedded.rs index 23c8d6e40c..45f8eca8ac 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -70,6 +70,7 @@ impl ExerciseDir { } pub struct EmbeddedFiles { + pub info_file: &'static str, exercise_files: &'static [ExerciseFiles], exercise_dirs: &'static [ExerciseDir], } @@ -148,7 +149,7 @@ mod tests { #[test] fn dirs() { - let exercises = toml_edit::de::from_str::(include_str!("../info.toml")) + let exercises = toml_edit::de::from_str::(EMBEDDED_FILES.info_file) .expect("Failed to parse `info.toml`") .exercises; diff --git a/src/info_file.rs b/src/info_file.rs index dbe4f089ed..14b886b244 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -2,6 +2,8 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; use std::{fs, io::ErrorKind}; +use crate::embedded::EMBEDDED_FILES; + // Deserialized from the `info.toml` file. #[derive(Deserialize)] pub struct ExerciseInfo { @@ -47,7 +49,7 @@ impl InfoFile { .context("Failed to parse the `info.toml` file")?, Err(e) => { if e.kind() == ErrorKind::NotFound { - return toml_edit::de::from_str(include_str!("../info.toml")) + return toml_edit::de::from_str(EMBEDDED_FILES.info_file) .context("Failed to parse the embedded `info.toml` file"); } From 052573904604896398a6cc7281398fa9fdf8f083 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:20:04 +0200 Subject: [PATCH 0871/1432] Fix invisible input on Windows --- src/watch.rs | 4 ++-- src/watch/state.rs | 13 ++++------- src/watch/terminal_event.rs | 46 +++++++++++++++---------------------- 3 files changed, 25 insertions(+), 38 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 453d9a43d0..944d77b48e 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -92,8 +92,8 @@ pub fn watch( break; } WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise()?, - WatchEvent::Input(InputEvent::Unrecognized(cmd)) => { - watch_state.handle_invalid_cmd(&cmd)?; + WatchEvent::Input(InputEvent::Unrecognized(input)) => { + watch_state.handle_invalid_input(input)?; } WatchEvent::FileChange { exercise_ind } => { watch_state.run_exercise_with_ind(exercise_ind)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 2cf7521df9..f3ffac859d 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -166,14 +166,11 @@ When you are done experimenting, enter `n` (or `next`) to move on to the next ex self.render() } - pub fn handle_invalid_cmd(&mut self, cmd: &str) -> io::Result<()> { - self.writer.write_all(b"Invalid command: ")?; - self.writer.write_all(cmd.as_bytes())?; - if cmd.len() > 1 { - self.writer - .write_all(b" (confusing input can occur after resizing the terminal)")?; - } - self.writer.write_all(b"\n")?; + pub fn handle_invalid_input(&mut self, input: char) -> io::Result<()> { + writeln!( + self.writer, + "Invalid input: {input} (confusing input can occur after resizing the terminal)", + )?; self.show_prompt() } } diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 6d790b7ced..846bec170d 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -9,12 +9,10 @@ pub enum InputEvent { Hint, List, Quit, - Unrecognized(String), + Unrecognized(char), } pub fn terminal_event_handler(tx: Sender, manual_run: bool) { - let mut input = String::with_capacity(8); - let last_input_event = loop { let terminal_event = match event::read() { Ok(v) => v, @@ -28,36 +26,28 @@ pub fn terminal_event_handler(tx: Sender, manual_run: bool) { match terminal_event { Event::Key(key) => { - if key.modifiers != KeyModifiers::NONE { - continue; - } - match key.kind { - KeyEventKind::Release => continue, - KeyEventKind::Press | KeyEventKind::Repeat => (), + KeyEventKind::Release | KeyEventKind::Repeat => continue, + KeyEventKind::Press => (), } - match key.code { - KeyCode::Enter => { - let input_event = match input.trim() { - "n" | "next" => InputEvent::Next, - "h" | "hint" => InputEvent::Hint, - "l" | "list" => break InputEvent::List, - "q" | "quit" => break InputEvent::Quit, - "r" | "run" if manual_run => InputEvent::Run, - _ => InputEvent::Unrecognized(input.clone()), - }; - - if tx.send(WatchEvent::Input(input_event)).is_err() { - return; - } + if key.modifiers != KeyModifiers::NONE { + continue; + } - input.clear(); - } - KeyCode::Char(c) => { - input.push(c); + if let KeyCode::Char(c) = key.code { + let input_event = match c { + 'n' => InputEvent::Next, + 'h' => InputEvent::Hint, + 'l' => break InputEvent::List, + 'q' => break InputEvent::Quit, + 'r' if manual_run => InputEvent::Run, + _ => InputEvent::Unrecognized(c), + }; + + if tx.send(WatchEvent::Input(input_event)).is_err() { + return; } - _ => (), } } Event::Resize(_, _) => { From f9e35a4344cd7d51923f1983cf824fb36be92d50 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:32:25 +0200 Subject: [PATCH 0872/1432] Improve input handling --- src/app_state.rs | 10 +++++++--- src/run.rs | 6 +++++- src/watch.rs | 7 +++---- src/watch/state.rs | 13 +------------ src/watch/terminal_event.rs | 4 ++-- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 492be345a8..85639e512a 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -21,8 +21,12 @@ const BAD_INDEX_ERR: &str = "The current exercise index is higher than the numbe #[must_use] pub enum ExercisesProgress { + // All exercises are done. AllDone, - Pending, + // The current exercise failed and is still pending. + CurrentPending, + // A new exercise is now pending. + NewPending, } pub enum StateFileStatus { @@ -343,7 +347,7 @@ impl AppState { if let Some(ind) = self.next_pending_exercise_ind() { self.set_current_exercise_ind(ind)?; - return Ok(ExercisesProgress::Pending); + return Ok(ExercisesProgress::NewPending); } writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; @@ -366,7 +370,7 @@ impl AppState { self.write()?; - return Ok(ExercisesProgress::Pending); + return Ok(ExercisesProgress::NewPending); } writeln!(writer, "{}", "ok".green())?; diff --git a/src/run.rs b/src/run.rs index 9b5ddd3473..ac974143f8 100644 --- a/src/run.rs +++ b/src/run.rs @@ -41,7 +41,11 @@ pub fn run(app_state: &mut AppState) -> Result<()> { match app_state.done_current_exercise(&mut stdout)? { ExercisesProgress::AllDone => (), - ExercisesProgress::Pending => println!( + ExercisesProgress::CurrentPending => println!( + "Current exercise: {}", + app_state.current_exercise().terminal_link(), + ), + ExercisesProgress::NewPending => println!( "Next exercise: {}", app_state.current_exercise().terminal_link(), ), diff --git a/src/watch.rs b/src/watch.rs index 944d77b48e..7d4f54bce6 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -79,7 +79,8 @@ pub fn watch( match event { WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise()? { ExercisesProgress::AllDone => break, - ExercisesProgress::Pending => watch_state.run_current_exercise()?, + ExercisesProgress::CurrentPending => watch_state.render()?, + ExercisesProgress::NewPending => watch_state.run_current_exercise()?, }, WatchEvent::Input(InputEvent::Hint) => { watch_state.show_hint()?; @@ -92,9 +93,7 @@ pub fn watch( break; } WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise()?, - WatchEvent::Input(InputEvent::Unrecognized(input)) => { - watch_state.handle_invalid_input(input)?; - } + WatchEvent::Input(InputEvent::Unrecognized) => watch_state.render()?, WatchEvent::FileChange { exercise_ind } => { watch_state.run_exercise_with_ind(exercise_ind)?; } diff --git a/src/watch/state.rs b/src/watch/state.rs index f3ffac859d..2e98546171 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -78,10 +78,7 @@ impl<'a> WatchState<'a> { pub fn next_exercise(&mut self) -> Result { if matches!(self.done_status, DoneStatus::Pending) { - self.writer - .write_all(b"The current exercise isn't done yet\n")?; - self.show_prompt()?; - return Ok(ExercisesProgress::Pending); + return Ok(ExercisesProgress::CurrentPending); } self.app_state.done_current_exercise(&mut self.writer) @@ -165,12 +162,4 @@ When you are done experimenting, enter `n` (or `next`) to move on to the next ex self.show_hint = true; self.render() } - - pub fn handle_invalid_input(&mut self, input: char) -> io::Result<()> { - writeln!( - self.writer, - "Invalid input: {input} (confusing input can occur after resizing the terminal)", - )?; - self.show_prompt() - } } diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 846bec170d..29a672a05c 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -9,7 +9,7 @@ pub enum InputEvent { Hint, List, Quit, - Unrecognized(char), + Unrecognized, } pub fn terminal_event_handler(tx: Sender, manual_run: bool) { @@ -42,7 +42,7 @@ pub fn terminal_event_handler(tx: Sender, manual_run: bool) { 'l' => break InputEvent::List, 'q' => break InputEvent::Quit, 'r' if manual_run => InputEvent::Run, - _ => InputEvent::Unrecognized(c), + _ => InputEvent::Unrecognized, }; if tx.send(WatchEvent::Input(input_event)).is_err() { From d2b5906be226f936481ff3a5cb8fccde5c721524 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:37:32 +0200 Subject: [PATCH 0873/1432] No more word input --- README.md | 4 ++-- exercises/00_intro/intro1.rs | 2 +- rustlings-macros/info.toml | 4 ++-- src/main.rs | 2 +- src/watch.rs | 2 +- src/watch/state.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3ba080f552..0180608e1d 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ It will rerun the current exercise automatically every time you change the exerc
If detecting file changes in the exercises/ directory fails… (click to expand) -> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` (or `run`) in the watch mode. +> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. > > Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). @@ -106,7 +106,7 @@ It will rerun the current exercise automatically every time you change the exerc ### Exercise List -In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` (or `list`) to open the interactive exercise list. +In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list. The list allows you to… diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 62bf95f1cc..bdbf34b059 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,6 @@ // We sometimes encourage you to keep trying things on a given exercise, even // after you already figured it out. If you got everything working and feel -// ready for the next exercise, enter `n` (or `next`) in the terminal. +// ready for the next exercise, enter `n` in the terminal. // // The exercise file will be reloaded when you change one of the lines below! // Try adding a new `println!`. diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4204f27433..485665e4d4 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -13,7 +13,7 @@ get started, here are some notes about how Rustlings operates: the exercise file in your editor, fix errors and save the file. Rustlings will automatically detect the file change and rerun the exercise. If all errors are fixed, Rustlings will ask you to move on to the next exercise. -3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. +3. If you're stuck on an exercise, enter `h` to show a hint. 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, other learners do too so you can help each other out! @@ -35,7 +35,7 @@ name = "intro1" dir = "00_intro" test = false # TODO: Fix hint -hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" +hint = """Enter `n` to move on to the next exercise. You might need to press ENTER after typing `n`.""" [[exercises]] name = "intro2" diff --git a/src/main.rs b/src/main.rs index 15bcc8e37d..cf6f0d9616 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,7 @@ fn press_enter_prompt() -> io::Result<()> { struct Args { #[command(subcommand)] command: Option, - /// Manually run the current exercise using `r` or `run` in the watch mode. + /// Manually run the current exercise using `r` in the watch mode. /// Only use this if Rustlings fails to detect exercise file changes. #[arg(long)] manual_run: bool, diff --git a/src/watch.rs b/src/watch.rs index 7d4f54bce6..f72ebf7d0b 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -123,5 +123,5 @@ The automatic detection of exercise file changes failed :( Please try running `rustlings` again. If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher. -You need to manually trigger running the current exercise using `r` (or `run`) then. +You need to manually trigger running the current exercise using `r` then. "; diff --git a/src/watch/state.rs b/src/watch/state.rs index 2e98546171..c21d7cae43 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -127,7 +127,7 @@ impl<'a> WatchState<'a> { self.writer, "{}\n", "Exercise done βœ“ -When you are done experimenting, enter `n` (or `next`) to move on to the next exercise πŸ¦€" +When you are done experimenting, enter `n` to move on to the next exercise πŸ¦€" .bold() .green(), )?; From 8b2d9ed50398c4c5c999ab9ab67757770449ed56 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:45:12 +0200 Subject: [PATCH 0874/1432] Use PartialEq instead of matches! --- src/watch/state.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index c21d7cae43..74cf1823a5 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -13,6 +13,7 @@ use crate::{ terminal_link::TerminalFileLink, }; +#[derive(PartialEq, Eq)] enum DoneStatus { DoneWithSolution(String), DoneWithoutSolution, @@ -77,7 +78,7 @@ impl<'a> WatchState<'a> { } pub fn next_exercise(&mut self) -> Result { - if matches!(self.done_status, DoneStatus::Pending) { + if self.done_status == DoneStatus::Pending { return Ok(ExercisesProgress::CurrentPending); } @@ -91,7 +92,7 @@ impl<'a> WatchState<'a> { write!(self.writer, "{}un/", 'r'.bold())?; } - if !matches!(self.done_status, DoneStatus::Pending) { + if self.done_status != DoneStatus::Pending { write!(self.writer, "{}ext/", 'n'.bold())?; } @@ -122,7 +123,7 @@ impl<'a> WatchState<'a> { )?; } - if !matches!(self.done_status, DoneStatus::Pending) { + if self.done_status != DoneStatus::Pending { writeln!( self.writer, "{}\n", From a4da216a5c52cf5626e1861991d1172c5897e746 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:46:26 +0200 Subject: [PATCH 0875/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a6049edab..aebbfc5b98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,7 +656,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.6" +version = "6.0.0-beta.7" dependencies = [ "anyhow", "assert_cmd", @@ -675,7 +675,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.6" +version = "6.0.0-beta.7" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index f2015cb3b3..eacae3b8c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.6" +version = "6.0.0-beta.7" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.6" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.7" } serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true From 7a74a72dc8a7bd906e7733731b427a1cdeee103a Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:48:42 +0200 Subject: [PATCH 0876/1432] Update beta version in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0180608e1d..c1ce95ce1b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.6 +cargo install rustlings@6.0.0-beta.7 ```
@@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.6 - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.6 --locked` +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.7 --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
From f6cf6c611c8b79131e1b6eac3ece7987ba1eaaf5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 04:11:11 +0200 Subject: [PATCH 0877/1432] Fix Windows terminal links --- README.md | 4 ++-- src/terminal_link.rs | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c1ce95ce1b..e6ea8de890 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.7 +cargo install rustlings@6.0.0-beta.8 ```
@@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.7 - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.7 --locked` +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.8 --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
diff --git a/src/terminal_link.rs b/src/terminal_link.rs index c9e6bced12..9bea07d9ee 100644 --- a/src/terminal_link.rs +++ b/src/terminal_link.rs @@ -7,15 +7,18 @@ pub struct TerminalFileLink<'a>(pub &'a str); impl<'a> Display for TerminalFileLink<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Ok(Some(canonical_path)) = fs::canonicalize(self.0) - .as_deref() - .map(|path| path.to_str()) - { - write!( - f, - "\x1b]8;;file://{}\x1b\\{}\x1b]8;;\x1b\\", - canonical_path, self.0, - ) + let path = fs::canonicalize(self.0); + + if let Some(path) = path.as_deref().ok().and_then(|path| path.to_str()) { + // Windows itself can't handle its verbatim paths. + #[cfg(windows)] + let path = if path.len() > 5 && &path[0..4] == r"\\?\" { + &path[4..] + } else { + path + }; + + write!(f, "\x1b]8;;file://{path}\x1b\\{}\x1b]8;;\x1b\\", self.0) } else { write!(f, "{}", self.0) } From 56eb4a5d650e6bb671fe60b8cc746b19e9c5f3d4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 04:11:29 +0200 Subject: [PATCH 0878/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aebbfc5b98..7c1d1a6bb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,7 +656,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.7" +version = "6.0.0-beta.8" dependencies = [ "anyhow", "assert_cmd", @@ -675,7 +675,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.7" +version = "6.0.0-beta.8" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index eacae3b8c0..e552590112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.7" +version = "6.0.0-beta.8" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.7" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.8" } serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true From a7bc6d53a56e105b4d8ad558ef533ee7ecb6aaea Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 16:39:38 +0200 Subject: [PATCH 0879/1432] Only send `Unrecognized` on ENTER if the last input wasn't valid --- src/watch/terminal_event.rs | 47 +++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 29a672a05c..f54af17a7d 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -13,6 +13,9 @@ pub enum InputEvent { } pub fn terminal_event_handler(tx: Sender, manual_run: bool) { + // Only send `Unrecognized` on ENTER if the last input wasn't valid. + let mut last_input_valid = false; + let last_input_event = loop { let terminal_event = match event::read() { Ok(v) => v, @@ -32,22 +35,42 @@ pub fn terminal_event_handler(tx: Sender, manual_run: bool) { } if key.modifiers != KeyModifiers::NONE { + last_input_valid = false; continue; } - if let KeyCode::Char(c) = key.code { - let input_event = match c { - 'n' => InputEvent::Next, - 'h' => InputEvent::Hint, - 'l' => break InputEvent::List, - 'q' => break InputEvent::Quit, - 'r' if manual_run => InputEvent::Run, - _ => InputEvent::Unrecognized, - }; - - if tx.send(WatchEvent::Input(input_event)).is_err() { - return; + let input_event = match key.code { + KeyCode::Enter => { + if last_input_valid { + continue; + } + + InputEvent::Unrecognized + } + KeyCode::Char(c) => { + let input_event = match c { + 'n' => InputEvent::Next, + 'h' => InputEvent::Hint, + 'l' => break InputEvent::List, + 'q' => break InputEvent::Quit, + 'r' if manual_run => InputEvent::Run, + _ => { + last_input_valid = false; + continue; + } + }; + + last_input_valid = true; + input_event } + _ => { + last_input_valid = false; + continue; + } + }; + + if tx.send(WatchEvent::Input(input_event)).is_err() { + return; } } Event::Resize(_, _) => { From 17a2d42ffd868e2049c91d7d1adbecd7f9958020 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 16:44:48 +0200 Subject: [PATCH 0880/1432] Better variable naming --- src/watch/notify_event.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index f66a83427c..a2243771e4 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -9,17 +9,17 @@ pub struct DebounceEventHandler { } impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { - fn handle_event(&mut self, event: DebounceEventResult) { - let event = match event { - Ok(event) => { - let Some(exercise_ind) = event + fn handle_event(&mut self, input_event: DebounceEventResult) { + let output_event = match input_event { + Ok(input_event) => { + let Some(exercise_ind) = input_event .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any { + .filter_map(|input_event| { + if input_event.kind != DebouncedEventKind::Any { return None; } - let file_name = event.path.file_name()?.to_str()?.as_bytes(); + let file_name = input_event.path.file_name()?.to_str()?.as_bytes(); if file_name.len() < 4 { return None; @@ -46,6 +46,6 @@ impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { // An error occurs when the receiver is dropped. // After dropping the receiver, the debouncer guard should also be dropped. - let _ = self.tx.send(event); + let _ = self.tx.send(output_event); } } From 4ae3fcc3caf91d4b22680ed4497c8ee05296eaad Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 17:06:11 +0200 Subject: [PATCH 0881/1432] Don't skip exercises on file changes --- src/app_state.rs | 4 ++++ src/watch.rs | 2 +- src/watch/state.rs | 9 ++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 85639e512a..75014cebbd 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -198,6 +198,10 @@ impl AppState { } pub fn set_current_exercise_ind(&mut self, exercise_ind: usize) -> Result<()> { + if exercise_ind == self.current_exercise_ind { + return Ok(()); + } + if exercise_ind >= self.exercises.len() { bail!(BAD_INDEX_ERR); } diff --git a/src/watch.rs b/src/watch.rs index f72ebf7d0b..2fbc533741 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -95,7 +95,7 @@ pub fn watch( WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise()?, WatchEvent::Input(InputEvent::Unrecognized) => watch_state.render()?, WatchEvent::FileChange { exercise_ind } => { - watch_state.run_exercise_with_ind(exercise_ind)?; + watch_state.handle_file_change(exercise_ind)?; } WatchEvent::TerminalResize => { watch_state.render()?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 74cf1823a5..60b6d5a82b 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -72,7 +72,14 @@ impl<'a> WatchState<'a> { self.render() } - pub fn run_exercise_with_ind(&mut self, exercise_ind: usize) -> Result<()> { + pub fn handle_file_change(&mut self, exercise_ind: usize) -> Result<()> { + // Don't skip exercises on file changes to avoid confusion from missing exercises. + // Skipping exercises must be explicit in the interactive list. + // But going back to an earlier exercise on file change is fine. + if self.app_state.current_exercise_ind() < exercise_ind { + return Ok(()); + } + self.app_state.set_current_exercise_ind(exercise_ind)?; self.run_current_exercise() } From e80e91faf284a4d20a2a7fd6ecd1c241e5c2e136 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 17:12:58 +0200 Subject: [PATCH 0882/1432] Thanks Clippy :) --- src/embedded.rs | 8 ++++---- src/exercise.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index 45f8eca8ac..39ade17d6e 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -96,8 +96,8 @@ impl EmbeddedFiles { } pub fn write_exercise_to_disk(&self, exercise_ind: usize, path: &str) -> Result<()> { - let exercise_files = &EMBEDDED_FILES.exercise_files[exercise_ind]; - let dir = &EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind]; + let exercise_files = &self.exercise_files[exercise_ind]; + let dir = &self.exercise_dirs[exercise_files.dir_ind]; dir.init_on_disk()?; WriteStrategy::Overwrite.write(path, exercise_files.exercise) @@ -109,8 +109,8 @@ impl EmbeddedFiles { exercise_ind: usize, exercise_name: &str, ) -> Result { - let exercise_files = &EMBEDDED_FILES.exercise_files[exercise_ind]; - let dir = &EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind]; + let exercise_files = &self.exercise_files[exercise_ind]; + let dir = &self.exercise_dirs[exercise_files.dir_ind]; // 14 = 10 + 1 + 3 // solutions/ + / + .rs diff --git a/src/exercise.rs b/src/exercise.rs index 4edf378e77..6e1b3f0d5d 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -142,7 +142,7 @@ impl From for Exercise { let hint = exercise_info.hint.trim().to_owned(); - Exercise { + Self { dir, name, path, From 5a1d95028c8d8bca49efc1cc27c4cf6254abf87d Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 17:14:00 +0200 Subject: [PATCH 0883/1432] Update version in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6ea8de890..c0de89d3d6 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.8 +cargo install rustlings@6.0.0-beta.9 ```
@@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.8 - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.8 --locked` +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.9 --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
From 0add5ac240909e2744b492b41a310c9598c1e622 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 17:14:11 +0200 Subject: [PATCH 0884/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c1d1a6bb0..20a628c74b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,7 +656,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.8" +version = "6.0.0-beta.9" dependencies = [ "anyhow", "assert_cmd", @@ -675,7 +675,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.8" +version = "6.0.0-beta.9" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index e552590112..508fdcc3e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.8" +version = "6.0.0-beta.9" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.8" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true From 2dfc7cdb1a26f46c5537e10e8a182dd2125758cb Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 21:07:04 +0200 Subject: [PATCH 0885/1432] Document embedded --- src/embedded.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index 39ade17d6e..bc1a5cc60b 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -6,6 +6,7 @@ use std::{ use crate::info_file::ExerciseInfo; +// Contains all embedded files. pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); #[derive(Clone, Copy)] @@ -31,12 +32,17 @@ impl WriteStrategy { } } +// Files related to one exercise. struct ExerciseFiles { + // The content of the exercise file. exercise: &'static [u8], + // The content of the solution file. solution: &'static [u8], + // Index of the related `ExerciseDir` in `EmbeddedFiles::exercise_dirs`. dir_ind: usize, } +// A directory in the `exercises/` directory. struct ExerciseDir { name: &'static str, readme: &'static [u8], @@ -63,19 +69,20 @@ impl ExerciseDir { let mut readme_path = dir_path; readme_path.push_str("/README.md"); - WriteStrategy::Overwrite.write(&readme_path, self.readme)?; - - Ok(()) + WriteStrategy::Overwrite.write(&readme_path, self.readme) } } +// All embedded files. pub struct EmbeddedFiles { + // `info.toml` pub info_file: &'static str, exercise_files: &'static [ExerciseFiles], exercise_dirs: &'static [ExerciseDir], } impl EmbeddedFiles { + // Dump all the embedded files of the `exercises/` direcotry. pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> Result<()> { create_dir("exercises").context("Failed to create the directory `exercises`")?; From 39a19f945008ef59af107fe54d9dc62943469c8b Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 21:36:20 +0200 Subject: [PATCH 0886/1432] Document exercise --- src/app_state.rs | 22 +++++++++++++++++++++- src/exercise.rs | 49 +++++++++++++++--------------------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 75014cebbd..b10ebb5eba 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -124,7 +124,27 @@ impl AppState { let exercises = exercise_infos .into_iter() - .map(Exercise::from) + .map(|exercise_info| { + // Leaking to be able to borrow in the watch mode `Table`. + // Leaking is not a problem because the `AppState` instance lives until + // the end of the program. + let path = exercise_info.path().leak(); + let name = exercise_info.name.leak(); + let dir = exercise_info.dir.map(|dir| &*dir.leak()); + + let hint = exercise_info.hint.trim().to_owned(); + + Exercise { + dir, + name, + path, + test: exercise_info.test, + strict_clippy: exercise_info.strict_clippy, + hint, + // Updated in `Self::update_from_file`. + done: false, + } + }) .collect::>(); let mut slf = Self { diff --git a/src/exercise.rs b/src/exercise.rs index 6e1b3f0d5d..494fc4270f 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -10,30 +10,33 @@ use std::{ use crate::{ cmd::{run_cmd, CargoCmd}, in_official_repo, - info_file::ExerciseInfo, terminal_link::TerminalFileLink, DEBUG_PROFILE, }; +// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; pub struct Exercise { + /// Directory name. pub dir: Option<&'static str>, - // Exercise's unique name + /// Exercise's unique name. pub name: &'static str, - // Exercise's path + /// Path of the exercise file starting with the `exercises/` directory. pub path: &'static str, pub test: bool, pub strict_clippy: bool, - // The hint text associated with the exercise pub hint: String, pub done: bool, } impl Exercise { + // Run the exercise's binary and append its output to the `output` buffer. + // Compilation should be done before calling this method. fn run_bin(&self, output: &mut Vec, target_dir: &Path) -> Result { writeln!(output, "{}", "Output".underlined())?; + // 7 = "/debug/".len() let mut bin_path = PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + self.name.len()); bin_path.push(target_dir); @@ -43,18 +46,23 @@ impl Exercise { let success = run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output)?; if !success { + // This output is important to show the user that something went wrong. + // Otherwise, calling something like `exit(1)` in an exercise without further output + // leaves the user confused about why the exercise isn't done yet. writeln!( output, "{}", "The exercise didn't run successfully (nonzero exit code)" .bold() - .red() + .red(), )?; } Ok(success) } + /// Compile, check and run the exercise. + /// The output is written to the `output` buffer after clearing it. pub fn run(&self, output: &mut Vec, target_dir: &Path) -> Result { output.clear(); @@ -76,9 +84,10 @@ impl Exercise { return Ok(false); } - // Discard the output of `cargo build` because it will be shown again by the Cargo command. + // Discard the output of `cargo build` because it will be shown again by Clippy. output.clear(); + // `--profile test` is required to also check code with `[cfg(test)]`. let clippy_args: &[&str] = if self.strict_clippy { &["--profile", "test", "--", "-D", "warnings"] } else { @@ -126,34 +135,6 @@ impl Exercise { } } -impl From for Exercise { - fn from(mut exercise_info: ExerciseInfo) -> Self { - // Leaking to be able to borrow in the watch mode `Table`. - // Leaking is not a problem because the `AppState` instance lives until - // the end of the program. - let path = exercise_info.path().leak(); - - exercise_info.name.shrink_to_fit(); - let name = exercise_info.name.leak(); - let dir = exercise_info.dir.map(|mut dir| { - dir.shrink_to_fit(); - &*dir.leak() - }); - - let hint = exercise_info.hint.trim().to_owned(); - - Self { - dir, - name, - path, - test: exercise_info.test, - strict_clippy: exercise_info.strict_clippy, - hint, - done: false, - } - } -} - impl Display for Exercise { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.path.fmt(f) From d48e86b1540dcf649412c088cc50161f3e356e26 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 21:40:40 +0200 Subject: [PATCH 0887/1432] Use public comments for public items --- src/app_state.rs | 10 +++++----- src/cargo_toml.rs | 14 +++++++------- src/cmd.rs | 14 +++++++------- src/embedded.rs | 10 +++++----- src/exercise.rs | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index b10ebb5eba..c7c090f6c2 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -330,8 +330,8 @@ impl AppState { } } - // Official exercises: Dump the solution file form the binary and return its path. - // Third-party exercises: Check if a solution file exists and return its path in that case. + /// Official exercises: Dump the solution file form the binary and return its path. + /// Third-party exercises: Check if a solution file exists and return its path in that case. pub fn current_solution_path(&self) -> Result> { if DEBUG_PROFILE { return Ok(None); @@ -358,9 +358,9 @@ impl AppState { } } - // Mark the current exercise as done and move on to the next pending exercise if one exists. - // If all exercises are marked as done, run all of them to make sure that they are actually - // done. If an exercise which is marked as done fails, mark it as pending and continue on it. + /// Mark the current exercise as done and move on to the next pending exercise if one exists. + /// If all exercises are marked as done, run all of them to make sure that they are actually + /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { let exercise = &mut self.exercises[self.current_exercise_ind]; if !exercise.done { diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index 106e6a7ab0..b7951f6b66 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -2,10 +2,10 @@ use anyhow::{Context, Result}; use crate::info_file::ExerciseInfo; -// Return the start and end index of the content of the list `bin = […]`. -// bin = [xxxxxxxxxxxxxxxxx] -// |start_ind | -// |end_ind +/// Return the start and end index of the content of the list `bin = […]`. +/// bin = [xxxxxxxxxxxxxxxxx] +/// |start_ind | +/// |end_ind pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { let start_ind = cargo_toml .find("bin = [") @@ -20,8 +20,8 @@ pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { Ok((start_ind, end_ind)) } -// Generate and append the content of the `bin` list in `Cargo.toml`. -// The `exercise_path_prefix` is the prefix of the `path` field of every list entry. +/// Generate and append the content of the `bin` list in `Cargo.toml`. +/// The `exercise_path_prefix` is the prefix of the `path` field of every list entry. pub fn append_bins( buf: &mut Vec, exercise_infos: &[ExerciseInfo], @@ -43,7 +43,7 @@ pub fn append_bins( } } -// Update the `bin` list and leave everything else unchanged. +/// Update the `bin` list and leave everything else unchanged. pub fn updated_cargo_toml( exercise_infos: &[ExerciseInfo], current_cargo_toml: &str, diff --git a/src/cmd.rs b/src/cmd.rs index 9762cf856d..b914ed88dc 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,8 +1,8 @@ use anyhow::{Context, Result}; use std::{io::Read, path::Path, process::Command}; -// Run a command with a description for a possible error and append the merged stdout and stderr. -// The boolean in the returned `Result` is true if the command's exit status is success. +/// Run a command with a description for a possible error and append the merged stdout and stderr. +/// The boolean in the returned `Result` is true if the command's exit status is success. pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec) -> Result { let (mut reader, writer) = os_pipe::pipe() .with_context(|| format!("Failed to create a pipe to run the command `{description}``"))?; @@ -37,18 +37,18 @@ pub struct CargoCmd<'a> { pub args: &'a [&'a str], pub exercise_name: &'a str, pub description: &'a str, - // RUSTFLAGS="-A warnings" + /// RUSTFLAGS="-A warnings" pub hide_warnings: bool, - // Added as `--target-dir` if `Self::dev` is true. + /// Added as `--target-dir` if `Self::dev` is true. pub target_dir: &'a Path, - // The output buffer to append the merged stdout and stderr. + /// The output buffer to append the merged stdout and stderr. pub output: &'a mut Vec, - // true while developing Rustlings. + /// true while developing Rustlings. pub dev: bool, } impl<'a> CargoCmd<'a> { - // Run `cargo SUBCOMMAND --bin EXERCISE_NAME … ARGS`. + /// Run `cargo SUBCOMMAND --bin EXERCISE_NAME … ARGS`. pub fn run(&mut self) -> Result { let mut cmd = Command::new("cargo"); cmd.arg(self.subcommand); diff --git a/src/embedded.rs b/src/embedded.rs index bc1a5cc60b..6f870684f0 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -6,7 +6,7 @@ use std::{ use crate::info_file::ExerciseInfo; -// Contains all embedded files. +/// Contains all embedded files. pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); #[derive(Clone, Copy)] @@ -73,16 +73,16 @@ impl ExerciseDir { } } -// All embedded files. +/// All embedded files. pub struct EmbeddedFiles { - // `info.toml` + /// The content of the `info.toml` file. pub info_file: &'static str, exercise_files: &'static [ExerciseFiles], exercise_dirs: &'static [ExerciseDir], } impl EmbeddedFiles { - // Dump all the embedded files of the `exercises/` direcotry. + /// Dump all the embedded files of the `exercises/` direcotry. pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> Result<()> { create_dir("exercises").context("Failed to create the directory `exercises`")?; @@ -110,7 +110,7 @@ impl EmbeddedFiles { WriteStrategy::Overwrite.write(path, exercise_files.exercise) } - // Write the solution file to disk and return its path. + /// Write the solution file to disk and return its path. pub fn write_solution_to_disk( &self, exercise_ind: usize, diff --git a/src/exercise.rs b/src/exercise.rs index 494fc4270f..a63c9aac3e 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -14,7 +14,7 @@ use crate::{ DEBUG_PROFILE, }; -// The initial capacity of the output buffer. +/// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; pub struct Exercise { From a67e63cce0443a2a289fdfc275a41cff704cd35e Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 22:02:45 +0200 Subject: [PATCH 0888/1432] Document info_file --- src/exercise.rs | 3 +-- src/info_file.rs | 43 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index a63c9aac3e..4bc37cd777 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -17,10 +17,9 @@ use crate::{ /// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; +/// See `info_file::ExerciseInfo` pub struct Exercise { - /// Directory name. pub dir: Option<&'static str>, - /// Exercise's unique name. pub name: &'static str, /// Path of the exercise file starting with the `exercises/` directory. pub path: &'static str, diff --git a/src/info_file.rs b/src/info_file.rs index 14b886b244..0c459284f3 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -4,44 +4,69 @@ use std::{fs, io::ErrorKind}; use crate::embedded::EMBEDDED_FILES; -// Deserialized from the `info.toml` file. +/// Deserialized from the `info.toml` file. #[derive(Deserialize)] pub struct ExerciseInfo { - // Name of the exercise + /// Exercise's unique name. pub name: String, - // The exercise's directory inside the `exercises` directory + /// Exercise's directory name inside the `exercises/` directory. pub dir: Option, #[serde(default = "default_true")] + /// Run `cargo test` on the exercise. pub test: bool, + /// Deny all Clippy warnings. #[serde(default)] pub strict_clippy: bool, - // The hint text associated with the exercise + /// The exercise's hint to be shown to the user on request. pub hint: String, } -#[inline] +#[inline(always)] const fn default_true() -> bool { true } impl ExerciseInfo { + /// Path to the exercise file starting with the `exercises/` directory. pub fn path(&self) -> String { - if let Some(dir) = &self.dir { - format!("exercises/{dir}/{}.rs", self.name) + let mut path = if let Some(dir) = &self.dir { + // 14 = 10 + 1 + 3 + // exercises/ + / + .rs + let mut path = String::with_capacity(14 + dir.len() + self.name.len()); + path.push_str("exercises/"); + path.push_str(dir); + path.push('/'); + path } else { - format!("exercises/{}.rs", self.name) - } + // 13 = 10 + 3 + // exercises/ + .rs + let mut path = String::with_capacity(13 + self.name.len()); + path.push_str("exercises/"); + path + }; + + path.push_str(&self.name); + path.push_str(".rs"); + + path } } +/// The deserialized `info.toml` file. #[derive(Deserialize)] pub struct InfoFile { + /// For possible breaking changes in the future for third-party exercises. pub format_version: u8, + /// Shown to users when starting with the exercises. pub welcome_message: Option, + /// Shown to users after finishing all exercises. pub final_message: Option, + /// List of all exercises. pub exercises: Vec, } impl InfoFile { + /// Official exercises: Parse the embedded `info.toml` file. + /// Third-party exercises: Parse the `info.toml` file in the current directory. pub fn parse() -> Result { // Read a local `info.toml` if it exists. let slf = match fs::read_to_string("info.toml") { From 700605ff356f70b840f05664b8823a7e14702f92 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 14 May 2024 00:35:12 +0200 Subject: [PATCH 0889/1432] Document init --- src/init.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/init.rs b/src/init.rs index cb3a6bc6f8..67d8a24389 100644 --- a/src/init.rs +++ b/src/init.rs @@ -11,8 +11,11 @@ use std::{ use crate::{cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile}; pub fn init() -> Result<()> { - if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { - bail!(PROBABLY_IN_RUSTLINGS_DIR_ERR); + // Prevent initialization in a directory that contains the file `Cargo.toml`. + // This can mean that Rustlings was already initialized in this directory. + // Otherwise, this can cause problems with Cargo workspaces. + if Path::new("Cargo.toml").exists() { + bail!(CARGO_TOML_EXISTS_ERR); } let rustlings_path = Path::new("rustlings"); @@ -24,7 +27,7 @@ pub fn init() -> Result<()> { } set_current_dir("rustlings") - .context("Failed to change the current directory to `rustlings`")?; + .context("Failed to change the current directory to `rustlings/`")?; let info_file = InfoFile::parse()?; EMBEDDED_FILES @@ -37,9 +40,10 @@ pub fn init() -> Result<()> { .as_bytes() .iter() .position(|c| *c == b'\n') - .context("The embedded `Cargo.toml` is empty or contains only one line.")?; - let current_cargo_toml = - ¤t_cargo_toml[(newline_ind + 1).min(current_cargo_toml.len() - 1)..]; + .context("The embedded `Cargo.toml` is empty or contains only one line")?; + let current_cargo_toml = current_cargo_toml + .get(newline_ind + 1..) + .context("The embedded `Cargo.toml` contains only one line")?; let updated_cargo_toml = updated_cargo_toml(&info_file.exercises, current_cargo_toml, b"") .context("Failed to generate `Cargo.toml`")?; fs::write("Cargo.toml", updated_cargo_toml) @@ -77,12 +81,10 @@ target 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 -in the current directory. It looks like Rustlings was already initialized here. -Run `rustlings` for instructions on getting started with the exercises. +const CARGO_TOML_EXISTS_ERR: &str = "The current directory contains the file `Cargo.toml`. -If you didn't already initialize Rustlings, please initialize it in another directory."; +If you already initialized Rustlings, run the command `rustlings` for instructions on getting started with the exercises. +Otherwise, please run `rustlings init` again in another directory."; const RUSTLINGS_DIR_ALREADY_EXISTS_ERR: &str = "A directory with the name `rustlings` already exists in the current directory. From 0ae66d18607a78c1dc55879cafd70732604e528e Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 14 May 2024 00:55:07 +0200 Subject: [PATCH 0890/1432] Remove inline --- src/list/state.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 0f2a1c82a2..d6df6344b3 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -148,14 +148,12 @@ impl<'a> UiState<'a> { } } - #[inline] pub fn select_first(&mut self) { if self.n_rows > 0 { self.table_state.select(Some(0)); } } - #[inline] pub fn select_last(&mut self) { if self.n_rows > 0 { self.table_state.select(Some(self.n_rows - 1)); From 96a44f3dcf2dd9e2562b757d7840084b45b90b61 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 14 May 2024 01:23:58 +0200 Subject: [PATCH 0891/1432] Make it more clear that only one char is expected --- src/watch/state.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index 60b6d5a82b..abd21fbd1b 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -96,18 +96,18 @@ impl<'a> WatchState<'a> { self.writer.write_all(b"\n")?; if self.manual_run { - write!(self.writer, "{}un/", 'r'.bold())?; + write!(self.writer, "{}:run / ", 'r'.bold())?; } if self.done_status != DoneStatus::Pending { - write!(self.writer, "{}ext/", 'n'.bold())?; + write!(self.writer, "{}:next / ", 'n'.bold())?; } if !self.show_hint { - write!(self.writer, "{}int/", 'h'.bold())?; + write!(self.writer, "{}:hint / ", 'h'.bold())?; } - write!(self.writer, "{}ist/{}uit? ", 'l'.bold(), 'q'.bold())?; + write!(self.writer, "{}:list / {}:quit ? ", 'l'.bold(), 'q'.bold())?; self.writer.flush() } From c8481d35c120ff99213e6ed73ba889e51cac10c5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 14 May 2024 01:49:22 +0200 Subject: [PATCH 0892/1432] Done documentation --- src/progress_bar.rs | 3 +++ src/run.rs | 6 +----- src/watch.rs | 5 +++-- src/watch/notify_event.rs | 5 +++-- src/watch/state.rs | 9 +++++---- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/progress_bar.rs b/src/progress_bar.rs index d6962b8c03..4a54170a91 100644 --- a/src/progress_bar.rs +++ b/src/progress_bar.rs @@ -12,6 +12,7 @@ const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; const PROGRESS_EXCEEDS_MAX_ERR: &str = "The progress of the progress bar is higher than the maximum"; +/// Terminal progress bar to be used when not using Ratataui. pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result { use crossterm::style::Stylize; @@ -54,6 +55,8 @@ pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result Result> { use ratatui::style::Stylize; diff --git a/src/run.rs b/src/run.rs index ac974143f8..36899b91ec 100644 --- a/src/run.rs +++ b/src/run.rs @@ -41,11 +41,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { match app_state.done_current_exercise(&mut stdout)? { ExercisesProgress::AllDone => (), - ExercisesProgress::CurrentPending => println!( - "Current exercise: {}", - app_state.current_exercise().terminal_link(), - ), - ExercisesProgress::NewPending => println!( + ExercisesProgress::CurrentPending | ExercisesProgress::NewPending => println!( "Next exercise: {}", app_state.current_exercise().terminal_link(), ), diff --git a/src/watch.rs b/src/watch.rs index 2fbc533741..88a1230155 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -14,7 +14,7 @@ use std::{ use crate::app_state::{AppState, ExercisesProgress}; use self::{ - notify_event::DebounceEventHandler, + notify_event::NotifyEventHandler, state::WatchState, terminal_event::{terminal_event_handler, InputEvent}, }; @@ -40,6 +40,7 @@ pub enum WatchExit { List, } +/// `notify_exercise_names` as None activates the manual run mode. pub fn watch( app_state: &mut AppState, notify_exercise_names: Option<&'static [&'static [u8]]>, @@ -52,7 +53,7 @@ pub fn watch( let _debouncer_guard = if let Some(exercise_names) = notify_exercise_names { let mut debouncer = new_debouncer( Duration::from_millis(200), - DebounceEventHandler { + NotifyEventHandler { tx: tx.clone(), exercise_names, }, diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index a2243771e4..74716409c2 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -3,12 +3,13 @@ use std::sync::mpsc::Sender; use super::WatchEvent; -pub struct DebounceEventHandler { +pub struct NotifyEventHandler { pub tx: Sender, + /// Used to report which exercise was modified. pub exercise_names: &'static [&'static [u8]], } -impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { +impl notify_debouncer_mini::DebounceEventHandler for NotifyEventHandler { fn handle_event(&mut self, input_event: DebounceEventResult) { let output_event = match input_event { Ok(input_event) => { diff --git a/src/watch/state.rs b/src/watch/state.rs index abd21fbd1b..14c3f01533 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,7 +1,7 @@ use anyhow::Result; use crossterm::{ style::{style, Stylize}, - terminal::size, + terminal, }; use std::io::{self, StdoutLock, Write}; @@ -84,6 +84,7 @@ impl<'a> WatchState<'a> { self.run_current_exercise() } + /// Move on to the next exercise if the current one is done. pub fn next_exercise(&mut self) -> Result { if self.done_status == DoneStatus::Pending { return Ok(ExercisesProgress::CurrentPending); @@ -113,7 +114,7 @@ impl<'a> WatchState<'a> { } pub fn render(&mut self) -> Result<()> { - // Prevent having the first line shifted. + // Prevent having the first line shifted if clearing wasn't successful. self.writer.write_all(b"\n")?; clear_terminal(&mut self.writer)?; @@ -145,11 +146,11 @@ When you are done experimenting, enter `n` to move on to the next exercise πŸ¦€" writeln!( self.writer, "A solution file can be found at {}\n", - style(TerminalFileLink(solution_path)).underlined().green() + style(TerminalFileLink(solution_path)).underlined().green(), )?; } - let line_width = size()?.0; + let line_width = terminal::size()?.0; let progress_bar = progress_bar( self.app_state.n_done(), self.app_state.exercises().len() as u16, From cf3f6fd6a16e81905bb44676d623502aeb8e5d01 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 14 May 2024 01:50:03 +0200 Subject: [PATCH 0893/1432] Fix typo --- src/embedded.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embedded.rs b/src/embedded.rs index 6f870684f0..e710a4e536 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -82,7 +82,7 @@ pub struct EmbeddedFiles { } impl EmbeddedFiles { - /// Dump all the embedded files of the `exercises/` direcotry. + /// Dump all the embedded files of the `exercises/` directory. pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> Result<()> { create_dir("exercises").context("Failed to create the directory `exercises`")?; From bde2524c3b1043da489541d4885592abe16646fa Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 20 May 2024 18:11:19 +0200 Subject: [PATCH 0894/1432] Update deps --- Cargo.lock | 74 ++++++++++++++++++++++++++++-------------------------- Cargo.toml | 8 +++--- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20a628c74b..10dfa30657 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "assert_cmd" @@ -210,18 +210,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -262,9 +262,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "equivalent" @@ -334,12 +334,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "inotify" version = "0.9.6" @@ -403,9 +397,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lock_api" @@ -571,9 +565,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -589,21 +583,21 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80" +checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" dependencies = [ "bitflags 2.5.0", "cassowary", "compact_str", "crossterm", - "indoc", "itertools", "lru", "paste", "stability", "strum", "unicode-segmentation", + "unicode-truncate", "unicode-width", ] @@ -684,9 +678,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" @@ -711,18 +705,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", @@ -742,9 +736,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -831,9 +825,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", @@ -848,18 +842,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", @@ -880,6 +874,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-truncate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" +dependencies = [ + "itertools", + "unicode-width", +] + [[package]] name = "unicode-width" version = "0.1.12" diff --git a/Cargo.toml b/Cargo.toml index 508fdcc3e8..ebcbe6cf17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ license = "MIT" edition = "2021" [workspace.dependencies] -serde = { version = "1.0.199", features = ["derive"] } -toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } +serde = { version = "1.0.202", features = ["derive"] } +toml_edit = { version = "0.22.13", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -46,13 +46,13 @@ include = [ ] [dependencies] -anyhow = "1.0.83" +anyhow = "1.0.86" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" -ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.26.3", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } serde_json = "1.0.117" serde.workspace = true From 0f4c42d54ea7322a4ee0ae7036c058c3061e80e9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 21 May 2024 01:47:57 +0200 Subject: [PATCH 0895/1432] Add solutions to intro and variables --- exercises/00_intro/intro2.rs | 4 +-- exercises/01_variables/variables1.rs | 6 ++--- exercises/01_variables/variables2.rs | 2 ++ exercises/01_variables/variables3.rs | 4 ++- exercises/01_variables/variables4.rs | 9 ++++--- exercises/01_variables/variables5.rs | 10 +++++--- exercises/01_variables/variables6.rs | 4 ++- rustlings-macros/info.toml | 37 ++++++++++++++-------------- solutions/00_intro/intro1.rs | 3 ++- solutions/00_intro/intro2.rs | 5 +++- solutions/01_variables/variables1.rs | 7 +++++- solutions/01_variables/variables2.rs | 17 ++++++++++++- solutions/01_variables/variables3.rs | 14 ++++++++++- solutions/01_variables/variables4.rs | 10 +++++++- solutions/01_variables/variables5.rs | 10 +++++++- solutions/01_variables/variables6.rs | 7 +++++- 16 files changed, 109 insertions(+), 40 deletions(-) diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index c7a3ab2a57..e443ec8fc6 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -1,5 +1,5 @@ -// Make the code print a greeting to the world. +// TODO: Fix the code to print "Hello world!". fn main() { - printline!("Hello there!") + printline!("Hello world!"); } diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs index 3035bfaef4..0a9e55488f 100644 --- a/exercises/01_variables/variables1.rs +++ b/exercises/01_variables/variables1.rs @@ -1,6 +1,6 @@ -// Make me compile! - fn main() { + // TODO: Add missing keyword. x = 5; - println!("x has the value {}", x); + + println!("x has the value {x}"); } diff --git a/exercises/01_variables/variables2.rs b/exercises/01_variables/variables2.rs index ce2dd85177..e2a360351a 100644 --- a/exercises/01_variables/variables2.rs +++ b/exercises/01_variables/variables2.rs @@ -1,5 +1,7 @@ fn main() { + // TODO: Change the line below to fix the compiler error. let x; + if x == 10 { println!("x is ten!"); } else { diff --git a/exercises/01_variables/variables3.rs b/exercises/01_variables/variables3.rs index 488385ba58..06f35bb1e2 100644 --- a/exercises/01_variables/variables3.rs +++ b/exercises/01_variables/variables3.rs @@ -1,4 +1,6 @@ fn main() { + // TODO: Change the line below to fix the compiler error. let x: i32; - println!("Number {}", x); + + println!("Number {x}"); } diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 67be12716d..8634ceb5c4 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -1,6 +1,9 @@ +// TODO: Fix the compiler error. + fn main() { let x = 3; - println!("Number {}", x); - x = 5; // don't change this line - println!("Number {}", x); + println!("Number {x}"); + + x = 5; // Don't change this line + println!("Number {x}"); } diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index 3a745411b7..73f655e36a 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -1,6 +1,8 @@ fn main() { - let number = "T-H-R-E-E"; // don't change this line - println!("Spell a Number : {}", number); - number = 3; // don't rename this variable - println!("Number plus two is : {}", number + 2); + let number = "T-H-R-E-E"; // Don't change this line + println!("Spell a number: {}", number); + + // TODO: Fix the compiler error by changing the line below without renaming the the variable. + number = 3; + println!("Number plus two is: {}", number + 2); } diff --git a/exercises/01_variables/variables6.rs b/exercises/01_variables/variables6.rs index 4746331ba6..4a040fddac 100644 --- a/exercises/01_variables/variables6.rs +++ b/exercises/01_variables/variables6.rs @@ -1,4 +1,6 @@ +// TODO: Change the line below to fix the compiler error. const NUMBER = 3; + fn main() { - println!("Number {}", NUMBER); + println!("Number: {NUMBER}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 485665e4d4..be3b262dee 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -29,20 +29,21 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md # INTRO -# TODO: Update exercise [[exercises]] name = "intro1" dir = "00_intro" test = false -# TODO: Fix hint -hint = """Enter `n` to move on to the next exercise. You might need to press ENTER after typing `n`.""" +hint = """ +Enter `n` to move on to the next exercise. +You might need to press ENTER after typing `n`.""" [[exercises]] name = "intro2" dir = "00_intro" test = false hint = """ -The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" +The compiler is informing us that we've got the name of the print macro wrong. +It also suggests an alternative.""" # VARIABLES @@ -51,18 +52,18 @@ name = "variables1" dir = "01_variables" test = false hint = """ -The declaration in the first line in the main function is missing a keyword -that is needed in Rust to create a new variable binding.""" +The declaration in the `main` function is missing a keyword that is needed +in Rust to create a new variable binding.""" [[exercises]] name = "variables2" dir = "01_variables" test = false hint = """ -The compiler message is saying that Rust cannot infer the type that the +The compiler message is saying that Rust can't infer the type that the variable binding `x` has with what is given here. -What happens if you annotate the first line in the main function with a type +What happens if you annotate the first line in the `main` function with a type annotation? What if you give `x` a value? @@ -78,9 +79,9 @@ name = "variables3" dir = "01_variables" test = false hint = """ -Oops! In this exercise, we have a variable binding that we've created on in the -first line in the `main` function, and we're trying to use it in the next line, -but we haven't given it a value. +In this exercise, we have a variable binding that we've created in the `main` +function, and we're trying to use it in the next line, but we haven't given it +a value. We can't print out something that isn't there; try giving `x` a value! @@ -92,7 +93,7 @@ name = "variables4" dir = "01_variables" test = false hint = """ -In Rust, variable bindings are immutable by default. But here we're trying +In Rust, variable bindings are immutable by default. But here, we're trying to reassign a different value to `x`! There's a keyword we can use to make a variable binding mutable instead.""" @@ -120,12 +121,12 @@ dir = "01_variables" test = false hint = """ We know about variables and mutability, but there is another important type of -variable available: constants. +variables available: constants. -Constants are always immutable and they are declared with keyword `const` rather -than keyword `let`. +Constants are always immutable. They are declared with the keyword `const` instead +of `let`. -Constants types must also always be annotated. +The type of Constants must always be annotated. Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability': @@ -139,7 +140,7 @@ name = "functions1" dir = "02_functions" test = false hint = """ -This main function is calling a function that it expects to exist, but the +This `main` function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. It expects this function to not take any arguments and not return a value. Sounds a lot like `main`, doesn't it?""" @@ -688,7 +689,7 @@ test = false hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your -main function. +`main` function. The unit (`()`) type is there because nothing is really needed in terms of positive results.""" diff --git a/solutions/00_intro/intro1.rs b/solutions/00_intro/intro1.rs index 4e18198923..07d4e4fdce 100644 --- a/solutions/00_intro/intro1.rs +++ b/solutions/00_intro/intro1.rs @@ -1 +1,2 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// The exercise `intro1` only requires entering `n` in the terminal to go to the next exercise. +// It is just an introduction to how Rustlings works. diff --git a/solutions/00_intro/intro2.rs b/solutions/00_intro/intro2.rs index 4e18198923..b8e031a08a 100644 --- a/solutions/00_intro/intro2.rs +++ b/solutions/00_intro/intro2.rs @@ -1 +1,4 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // `println!` instead of `printline!`. + println!("Hello world!"); +} diff --git a/solutions/01_variables/variables1.rs b/solutions/01_variables/variables1.rs index 4e18198923..58d046b2a1 100644 --- a/solutions/01_variables/variables1.rs +++ b/solutions/01_variables/variables1.rs @@ -1 +1,6 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // Declaring variables requires the `let` keyword. + let x = 5; + + println!("x has the value {x}"); +} diff --git a/solutions/01_variables/variables2.rs b/solutions/01_variables/variables2.rs index 4e18198923..50b8d1b42f 100644 --- a/solutions/01_variables/variables2.rs +++ b/solutions/01_variables/variables2.rs @@ -1 +1,16 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // The easiest way to fix the compiler error is to initialize the + // variable `x`. By setting its value to an integer, Rust infers its type + // as `i32` which is the default type for integers. + let x = 42; + + // But we can enforce a type different from the default `i32` by adding + // a type annotation: + // let x: u8 = 42; + + if x == 10 { + println!("x is ten!"); + } else { + println!("x is not ten!"); + } +} diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs index 4e18198923..7db42a9566 100644 --- a/solutions/01_variables/variables3.rs +++ b/solutions/01_variables/variables3.rs @@ -1 +1,13 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // Reading uninitialized variables isn't allowed in Rust! + // Therefore, we need to assign a value first. + let x: i32 = 42; + + println!("Number {x}"); + + // It possible to declare a variable and initialize it later. + // But it can't be used before initialization. + let y: i32; + y = 42; + println!("Number {y}"); +} diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs index 4e18198923..0540caa2a6 100644 --- a/solutions/01_variables/variables4.rs +++ b/solutions/01_variables/variables4.rs @@ -1 +1,9 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // In Rust, variables are immutable by default. + // Adding the `mut` keyword after `let` makes the declared variable mutable. + let mut x = 3; + println!("Number {x}"); + + x = 5; // Don't change this line + println!("Number {x}"); +} diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs index 4e18198923..456dc9cf41 100644 --- a/solutions/01_variables/variables5.rs +++ b/solutions/01_variables/variables5.rs @@ -1 +1,9 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + let number = "T-H-R-E-E"; // Don't change this line + println!("Spell a number: {}", number); + + // Using variable shadowing + // https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing + let number = 3; + println!("Number plus two is: {}", number + 2); +} diff --git a/solutions/01_variables/variables6.rs b/solutions/01_variables/variables6.rs index 4e18198923..25b7a1e43e 100644 --- a/solutions/01_variables/variables6.rs +++ b/solutions/01_variables/variables6.rs @@ -1 +1,6 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// The type of constants must always be annotated. +const NUMBER: u64 = 3; + +fn main() { + println!("Number: {NUMBER}"); +} From d0b843d6c4a99636d3dc6caf3ceebea14cb3b07d Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 21 May 2024 02:43:18 +0200 Subject: [PATCH 0896/1432] Add solutions to functions --- exercises/02_functions/functions1.rs | 4 +++- exercises/02_functions/functions2.rs | 9 +++++---- exercises/02_functions/functions3.rs | 9 +++++---- exercises/02_functions/functions4.rs | 19 ++++++++++--------- exercises/02_functions/functions5.rs | 11 ++++++----- rustlings-macros/info.toml | 22 +++++++++++----------- solutions/01_variables/variables4.rs | 2 +- solutions/01_variables/variables5.rs | 2 +- solutions/02_functions/functions1.rs | 9 ++++++++- solutions/02_functions/functions2.rs | 12 +++++++++++- solutions/02_functions/functions3.rs | 11 ++++++++++- solutions/02_functions/functions4.rs | 18 +++++++++++++++++- solutions/02_functions/functions5.rs | 10 +++++++++- 13 files changed, 97 insertions(+), 41 deletions(-) diff --git a/exercises/02_functions/functions1.rs b/exercises/02_functions/functions1.rs index 4e3b1036e8..a812c21bf2 100644 --- a/exercises/02_functions/functions1.rs +++ b/exercises/02_functions/functions1.rs @@ -1,3 +1,5 @@ +// TODO: Add some function with the name `call_me` without arguments or a return value. + fn main() { - call_me(); + call_me(); // Don't change this line } diff --git a/exercises/02_functions/functions2.rs b/exercises/02_functions/functions2.rs index 84e09cda92..2c773c6b7f 100644 --- a/exercises/02_functions/functions2.rs +++ b/exercises/02_functions/functions2.rs @@ -1,9 +1,10 @@ -fn main() { - call_me(3); -} - +// TODO: Add the missing type of the argument `num` after the colon `:`. fn call_me(num:) { for i in 0..num { println!("Ring! Call number {}", i + 1); } } + +fn main() { + call_me(3); +} diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs index 66fb6d32e3..5d5122afa3 100644 --- a/exercises/02_functions/functions3.rs +++ b/exercises/02_functions/functions3.rs @@ -1,9 +1,10 @@ -fn main() { - call_me(); -} - fn call_me(num: u32) { for i in 0..num { println!("Ring! Call number {}", i + 1); } } + +fn main() { + // TODO: Fix the function call. + call_me(); +} diff --git a/exercises/02_functions/functions4.rs b/exercises/02_functions/functions4.rs index 06d3b1bbfd..b22bffdaf2 100644 --- a/exercises/02_functions/functions4.rs +++ b/exercises/02_functions/functions4.rs @@ -1,14 +1,14 @@ // 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!) +// Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. +// Don't worry about the function bodies themselves, we are only interested in +// the signatures for now. -fn main() { - let original_price = 51; - println!("Your sale price is {}", sale_price(original_price)); +fn is_even(num: i64) -> bool { + num % 2 == 0 } -fn sale_price(price: i32) -> { +// TODO: Fix the function signature. +fn sale_price(price: i64) -> { if is_even(price) { price - 10 } else { @@ -16,6 +16,7 @@ fn sale_price(price: i32) -> { } } -fn is_even(num: i32) -> bool { - num % 2 == 0 +fn main() { + let original_price = 51; + println!("Your sale price is {}", sale_price(original_price)); } diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs index 3bb5e52af8..31fd057a87 100644 --- a/exercises/02_functions/functions5.rs +++ b/exercises/02_functions/functions5.rs @@ -1,8 +1,9 @@ -fn main() { - let answer = square(3); - println!("The square of 3 is {}", answer); -} - +// TODO: Fix the function body without chaning the signature. fn square(num: i32) -> i32 { num * num; } + +fn main() { + let answer = square(3); + println!("The square of 3 is {answer}"); +} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index be3b262dee..495e9c3e4d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -142,7 +142,7 @@ test = false hint = """ This `main` function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. -It expects this function to not take any arguments and not return a value. +It also expects this function to not take any arguments and not return a value. Sounds a lot like `main`, doesn't it?""" [[exercises]] @@ -159,7 +159,7 @@ dir = "02_functions" test = false hint = """ This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function.""" +with the place where we are calling the function.""" [[exercises]] name = "functions4" @@ -167,8 +167,8 @@ dir = "02_functions" test = false hint = """ The error message points to the function `sale_price` and says it expects a type -after the `->`. This is where the function's return type should be -- take a -look at the `is_even` function for an example!""" +after `->`. This is where the function's return type should be. +Take a look at the `is_even` function for an example!""" [[exercises]] name = "functions5" @@ -177,15 +177,15 @@ test = false hint = """ This is a really common error that can be fixed by removing one character. It happens because Rust distinguishes between expressions and statements: -expressions return a value based on their operand(s), and statements simply -return a `()` type which behaves just like `void` in C/C++ language. +Expressions return a value based on their operand(s), and statements simply +return a `()` type which behaves just like `void` in C/C++. -We want to return a value of `i32` type from the `square` function, but it is -returning a `()` type... +We want to return a value with the type `i32` from the `square` function, but +it is returning the type `()`. -They are not the same. There are two solutions: -1. Add a `return` ahead of `num * num;` -2. remove `;`, make it to be `num * num`""" +There are two solutions: +1. Add the `return` keyword before `num * num;` +2. Remove the semicolon `;` after `num * num`""" # IF diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs index 0540caa2a6..7de6bcbf0c 100644 --- a/solutions/01_variables/variables4.rs +++ b/solutions/01_variables/variables4.rs @@ -4,6 +4,6 @@ fn main() { let mut x = 3; println!("Number {x}"); - x = 5; // Don't change this line + x = 5; println!("Number {x}"); } diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs index 456dc9cf41..9057754ce4 100644 --- a/solutions/01_variables/variables5.rs +++ b/solutions/01_variables/variables5.rs @@ -1,5 +1,5 @@ fn main() { - let number = "T-H-R-E-E"; // Don't change this line + let number = "T-H-R-E-E"; println!("Spell a number: {}", number); // Using variable shadowing diff --git a/solutions/02_functions/functions1.rs b/solutions/02_functions/functions1.rs index 4e18198923..dc52744648 100644 --- a/solutions/02_functions/functions1.rs +++ b/solutions/02_functions/functions1.rs @@ -1 +1,8 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Some function with the name `call_me` without arguments or a return value. +fn call_me() { + println!("Hello world!"); +} + +fn main() { + call_me(); +} diff --git a/solutions/02_functions/functions2.rs b/solutions/02_functions/functions2.rs index 4e18198923..f14ffa350c 100644 --- a/solutions/02_functions/functions2.rs +++ b/solutions/02_functions/functions2.rs @@ -1 +1,11 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// The type of function arguments must be annotated. +// Added the type annotation `u64`. +fn call_me(num: u64) { + for i in 0..num { + println!("Ring! Call number {}", i + 1); + } +} + +fn main() { + call_me(3); +} diff --git a/solutions/02_functions/functions3.rs b/solutions/02_functions/functions3.rs index 4e18198923..c581c425f7 100644 --- a/solutions/02_functions/functions3.rs +++ b/solutions/02_functions/functions3.rs @@ -1 +1,10 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn call_me(num: u32) { + for i in 0..num { + println!("Ring! Call number {}", i + 1); + } +} + +fn main() { + // `call_me` expects an argument. + call_me(5); +} diff --git a/solutions/02_functions/functions4.rs b/solutions/02_functions/functions4.rs index 4e18198923..f823de24ef 100644 --- a/solutions/02_functions/functions4.rs +++ b/solutions/02_functions/functions4.rs @@ -1 +1,17 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn is_even(num: i64) -> bool { + num % 2 == 0 +} + +// The return type must always be annotated. +fn sale_price(price: i64) -> i64 { + if is_even(price) { + price - 10 + } else { + price - 3 + } +} + +fn main() { + let original_price = 51; + println!("Your sale price is {}", sale_price(original_price)); +} diff --git a/solutions/02_functions/functions5.rs b/solutions/02_functions/functions5.rs index 4e18198923..03354186b9 100644 --- a/solutions/02_functions/functions5.rs +++ b/solutions/02_functions/functions5.rs @@ -1 +1,9 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn square(num: i32) -> i32 { + // Removed the semicolon `;` at the end of the line below to implicitely return the result. + num * num +} + +fn main() { + let answer = square(3); + println!("The square of 3 is {answer}"); +} From 3bb71c6b0c9d58e421f79d914f5483cb5a98af0b Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 15:04:12 +0200 Subject: [PATCH 0897/1432] Remove unneeded pub --- exercises/03_if/if1.rs | 4 ++-- exercises/03_if/if2.rs | 2 +- exercises/03_if/if3.rs | 2 +- exercises/13_error_handling/errors1.rs | 2 +- exercises/13_error_handling/errors2.rs | 2 +- exercises/13_error_handling/errors3.rs | 2 +- exercises/14_generics/generics2.rs | 2 +- exercises/15_traits/traits3.rs | 2 +- exercises/15_traits/traits4.rs | 2 +- exercises/15_traits/traits5.rs | 4 ++-- exercises/17_tests/tests3.rs | 2 +- exercises/17_tests/tests4.rs | 2 +- exercises/18_iterators/iterators2.rs | 6 +++--- exercises/18_iterators/iterators3.rs | 6 +++--- exercises/18_iterators/iterators4.rs | 2 +- exercises/19_smart_pointers/box1.rs | 6 +++--- exercises/quizzes/quiz2.rs | 2 +- exercises/quizzes/quiz3.rs | 10 +++++----- 18 files changed, 30 insertions(+), 30 deletions(-) diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs index 52dee0b54a..e5a3c5a5b2 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -1,5 +1,5 @@ -pub fn bigger(a: i32, b: i32) -> i32 { - // Complete this function to return the bigger number! +fn bigger(a: i32, b: i32) -> i32 { + // TODO: Complete this function to return the bigger number! // If both numbers are equal, any of them can be returned. // Do not use: // - another function call diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index a06bba559b..d834ab2742 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -1,7 +1,7 @@ // Step 1: Make me compile! // Step 2: Get the bar_for_fuzz and default_to_baz tests passing! -pub fn foo_if_fizz(fizzish: &str) -> &str { +fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" } else { diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index 1d9b7c25ff..ff6fee643f 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -1,4 +1,4 @@ -pub fn animal_habitat(animal: &str) -> &'static str { +fn animal_habitat(animal: &str) -> &'static str { let identifier = if animal == "crab" { 1 } else if animal == "gopher" { diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index 15a3716df0..e3e0482310 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -8,7 +8,7 @@ fn main() { // You can optionally experiment here. } -pub fn generate_nametag_text(name: String) -> Option { +fn generate_nametag_text(name: String) -> Option { if name.is_empty() { // Empty names aren't allowed. None diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index e39aa959dd..345a0eef70 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -16,7 +16,7 @@ use std::num::ParseIntError; -pub fn total_cost(item_quantity: &str) -> Result { +fn total_cost(item_quantity: &str) -> Result { let processing_fee = 1; let cost_per_item = 5; let qty = item_quantity.parse::(); diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index 5661f17baf..2ef84f981f 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -18,7 +18,7 @@ fn main() { } } -pub fn total_cost(item_quantity: &str) -> Result { +fn total_cost(item_quantity: &str) -> Result { let processing_fee = 1; let cost_per_item = 5; let qty = item_quantity.parse::()?; diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index cbb9b5f982..6cdcdaf532 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -6,7 +6,7 @@ struct Wrapper { } impl Wrapper { - pub fn new(value: u32) -> Self { + fn new(value: u32) -> Self { Wrapper { value } } } diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 9a2365ae46..66da235f46 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -3,7 +3,7 @@ // // Consider what you can add to the Licensed trait. -pub trait Licensed { +trait Licensed { fn licensing_info(&self) -> String; } diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index 7af30b5752..ed63f6e135 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -2,7 +2,7 @@ // // Don't change any line other than the marked one. -pub trait Licensed { +trait Licensed { fn licensing_info(&self) -> String { "some information".to_string() } diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index 9a45bb7612..3e62283fb6 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -2,13 +2,13 @@ // // Don't change any line other than the marked one. -pub trait SomeTrait { +trait SomeTrait { fn some_function(&self) -> bool { true } } -pub trait OtherTrait { +trait OtherTrait { fn other_function(&self) -> bool { true } diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index 3b4e199076..d1cb48922e 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -2,7 +2,7 @@ // 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)`. -pub fn is_even(num: i32) -> bool { +fn is_even(num: i32) -> bool { num % 2 == 0 } diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs index 35a9a3b50f..4303ed069e 100644 --- a/exercises/17_tests/tests4.rs +++ b/exercises/17_tests/tests4.rs @@ -7,7 +7,7 @@ struct Rectangle { impl Rectangle { // Only change the test functions themselves - pub fn new(width: i32, height: i32) -> Self { + fn new(width: i32, height: i32) -> Self { if width <= 0 || height <= 0 { panic!("Rectangle width and height cannot be negative!") } diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index df1fa8387d..8d8909bfb2 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -4,7 +4,7 @@ // Step 1. // Complete the `capitalize_first` function. // "hello" -> "Hello" -pub fn capitalize_first(input: &str) -> String { +fn capitalize_first(input: &str) -> String { let mut c = input.chars(); match c.next() { None => String::new(), @@ -16,7 +16,7 @@ pub fn capitalize_first(input: &str) -> String { // Apply the `capitalize_first` function to a slice of string slices. // Return a vector of strings. // ["hello", "world"] -> ["Hello", "World"] -pub fn capitalize_words_vector(words: &[&str]) -> Vec { +fn capitalize_words_vector(words: &[&str]) -> Vec { vec![] } @@ -24,7 +24,7 @@ pub fn capitalize_words_vector(words: &[&str]) -> Vec { // Apply the `capitalize_first` function again to a slice of string slices. // Return a single string. // ["hello", " ", "world"] -> "Hello World" -pub fn capitalize_words_string(words: &[&str]) -> String { +fn capitalize_words_string(words: &[&str]) -> String { String::new() } diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 9f106aa8a9..dfe4014908 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -5,20 +5,20 @@ // list_of_results functions. #[derive(Debug, PartialEq, Eq)] -pub enum DivisionError { +enum DivisionError { NotDivisible(NotDivisibleError), DivideByZero, } #[derive(Debug, PartialEq, Eq)] -pub struct NotDivisibleError { +struct NotDivisibleError { dividend: i32, divisor: i32, } // Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // Otherwise, return a suitable error. -pub fn divide(a: i32, b: i32) -> Result { +fn divide(a: i32, b: i32) -> Result { todo!(); } diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 60c7b8d123..ae4d502d79 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,4 +1,4 @@ -pub fn factorial(num: u64) -> u64 { +fn factorial(num: u64) -> u64 { // Complete this function to return the factorial of num // Do not use: // - early returns (using the `return` keyword explicitly) diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index 226a117795..908c92322d 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -15,7 +15,7 @@ // Note: the tests should not be changed #[derive(PartialEq, Debug)] -pub enum List { +enum List { Cons(i32, List), Nil, } @@ -28,11 +28,11 @@ fn main() { ); } -pub fn create_empty_list() -> List { +fn create_empty_list() -> List { todo!() } -pub fn create_non_empty_list() -> List { +fn create_non_empty_list() -> List { todo!() } diff --git a/exercises/quizzes/quiz2.rs b/exercises/quizzes/quiz2.rs index 0a29e78114..e01e3f1dcf 100644 --- a/exercises/quizzes/quiz2.rs +++ b/exercises/quizzes/quiz2.rs @@ -16,7 +16,7 @@ // the first element is the string, the second one is the command. // - The output element is going to be a Vector of strings. -pub enum Command { +enum Command { Uppercase, Trim, Append(usize), diff --git a/exercises/quizzes/quiz3.rs b/exercises/quizzes/quiz3.rs index f255cb5d5d..f3cb1bcf26 100644 --- a/exercises/quizzes/quiz3.rs +++ b/exercises/quizzes/quiz3.rs @@ -12,14 +12,14 @@ // to support alphabetical report cards. Change the Grade in the second test to // "A+" to show that your changes allow alphabetical grades. -pub struct ReportCard { - pub grade: f32, - pub student_name: String, - pub student_age: u8, +struct ReportCard { + grade: f32, + student_name: String, + student_age: u8, } impl ReportCard { - pub fn print(&self) -> String { + fn print(&self) -> String { format!( "{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, &self.grade From c8ad6c3960b4bec44a610cc144e6b635bffcbc31 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 15:04:21 +0200 Subject: [PATCH 0898/1432] if1 solution --- rustlings-macros/info.toml | 4 ++-- solutions/03_if/if1.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 495e9c3e4d..a67e38d692 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -200,9 +200,9 @@ Some similar examples from other languages: - In Python this would be: `a if a > b else b` Remember in Rust that: -- the `if` condition does not need to be surrounded by parentheses +- The `if` condition does not need to be surrounded by parentheses - `if`/`else` conditionals are expressions -- Each condition is followed by a `{}` block.""" +- Each condition is followed by a `{}` block""" [[exercises]] name = "if2" diff --git a/solutions/03_if/if1.rs b/solutions/03_if/if1.rs index 4e18198923..079c671584 100644 --- a/solutions/03_if/if1.rs +++ b/solutions/03_if/if1.rs @@ -1 +1,32 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn bigger(a: i32, b: i32) -> i32 { + if a > b { + a + } else { + b + } +} + +fn main() { + // You can optionally experiment here. +} + +// Don't mind this for now :) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ten_is_bigger_than_eight() { + assert_eq!(10, bigger(10, 8)); + } + + #[test] + fn fortytwo_is_bigger_than_thirtytwo() { + assert_eq!(42, bigger(32, 42)); + } + + #[test] + fn equal_numbers() { + assert_eq!(42, bigger(42, 42)); + } +} From 7cdf6b79429428e944b440eb713e711d844a92d7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 15:13:18 +0200 Subject: [PATCH 0899/1432] Add missing semicolons --- exercises/03_if/if2.rs | 6 +++--- exercises/03_if/if3.rs | 8 ++++---- exercises/04_primitive_types/primitive_types4.rs | 2 +- exercises/19_smart_pointers/box1.rs | 4 ++-- exercises/20_threads/threads3.rs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index d834ab2742..1b65596b18 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -20,16 +20,16 @@ mod tests { #[test] fn foo_for_fizz() { - assert_eq!(foo_if_fizz("fizz"), "foo") + assert_eq!(foo_if_fizz("fizz"), "foo"); } #[test] fn bar_for_fuzz() { - assert_eq!(foo_if_fizz("fuzz"), "bar") + assert_eq!(foo_if_fizz("fuzz"), "bar"); } #[test] fn default_to_baz() { - assert_eq!(foo_if_fizz("literally anything"), "baz") + assert_eq!(foo_if_fizz("literally anything"), "baz"); } } diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index ff6fee643f..d3e4b06c2b 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -34,21 +34,21 @@ mod tests { #[test] fn gopher_lives_in_burrow() { - assert_eq!(animal_habitat("gopher"), "Burrow") + assert_eq!(animal_habitat("gopher"), "Burrow"); } #[test] fn snake_lives_in_desert() { - assert_eq!(animal_habitat("snake"), "Desert") + assert_eq!(animal_habitat("snake"), "Desert"); } #[test] fn crab_lives_on_beach() { - assert_eq!(animal_habitat("crab"), "Beach") + assert_eq!(animal_habitat("crab"), "Beach"); } #[test] fn unknown_animal() { - assert_eq!(animal_habitat("dinosaur"), "Unknown") + assert_eq!(animal_habitat("dinosaur"), "Unknown"); } } diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index c583ae1388..661e0515dc 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -14,6 +14,6 @@ mod tests { let nice_slice = ??? - assert_eq!([2, 3, 4], nice_slice) + assert_eq!([2, 3, 4], nice_slice); } } diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index 908c92322d..c8c2640da5 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -42,11 +42,11 @@ mod tests { #[test] fn test_create_empty_list() { - assert_eq!(List::Nil, create_empty_list()) + assert_eq!(List::Nil, create_empty_list()); } #[test] fn test_create_non_empty_list() { - assert_ne!(create_empty_list(), create_non_empty_list()) + assert_ne!(create_empty_list(), create_non_empty_list()); } } diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 13abc45024..37810cf9e4 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -60,6 +60,6 @@ mod tests { } println!("total numbers received: {}", total_received); - assert_eq!(total_received, queue_length) + assert_eq!(total_received, queue_length); } } From eafb157d60ee46e95e2b54ec75b33180a838b0c5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 15:16:50 +0200 Subject: [PATCH 0900/1432] if2 solution --- exercises/03_if/if2.rs | 8 ++++---- rustlings-macros/info.toml | 6 ++++-- solutions/03_if/if2.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index 1b65596b18..593a77a720 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -1,6 +1,4 @@ -// Step 1: Make me compile! -// Step 2: Get the bar_for_fuzz and default_to_baz tests passing! - +// TODO: Fix the compiler error on this function. fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" @@ -13,13 +11,15 @@ fn main() { // You can optionally experiment here. } -// No test changes needed! +// TODO: Read the tests to understand the desired behavior. +// Make all tests pass without changing them. #[cfg(test)] mod tests { use super::*; #[test] fn foo_for_fizz() { + // This means that calling `foo_if_fizz` with the argument "fizz" should return "foo". assert_eq!(foo_if_fizz("fizz"), "foo"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index a67e38d692..39fc5c4dcb 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -209,8 +209,10 @@ name = "if2" dir = "03_if" hint = """ For that first compiler error, it's important in Rust that each conditional -block returns the same type! To get the tests passing, you will need a couple -conditions checking different input values.""" +block returns the same type! + +To get the tests passing, you will need a couple conditions checking different +input values. Read the tests to find out what they expect.""" [[exercises]] name = "if3" diff --git a/solutions/03_if/if2.rs b/solutions/03_if/if2.rs index 4e18198923..440bba055f 100644 --- a/solutions/03_if/if2.rs +++ b/solutions/03_if/if2.rs @@ -1 +1,33 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn foo_if_fizz(fizzish: &str) -> &str { + if fizzish == "fizz" { + "foo" + } else if fizzish == "fuzz" { + "bar" + } else { + "baz" + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn foo_for_fizz() { + assert_eq!(foo_if_fizz("fizz"), "foo"); + } + + #[test] + fn bar_for_fuzz() { + assert_eq!(foo_if_fizz("fuzz"), "bar"); + } + + #[test] + fn default_to_baz() { + assert_eq!(foo_if_fizz("literally anything"), "baz"); + } +} From 73e84f83791f00ef8ccfe438bc018d2c0a9b21fe Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 15:54:35 +0200 Subject: [PATCH 0901/1432] if3 solution --- exercises/03_if/if3.rs | 21 ++++++++-------- solutions/03_if/if3.rs | 54 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index d3e4b06c2b..89164eb218 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -1,4 +1,5 @@ -fn animal_habitat(animal: &str) -> &'static str { +fn animal_habitat(animal: &str) -> &str { + // TODO: Fix the compiler error in the statement below. let identifier = if animal == "crab" { 1 } else if animal == "gopher" { @@ -9,8 +10,8 @@ fn animal_habitat(animal: &str) -> &'static str { "Unknown" }; - // DO NOT CHANGE THIS STATEMENT BELOW - let habitat = if identifier == 1 { + // Don't change the expression below! + if identifier == 1 { "Beach" } else if identifier == 2 { "Burrow" @@ -18,37 +19,35 @@ fn animal_habitat(animal: &str) -> &'static str { "Desert" } else { "Unknown" - }; - - habitat + } } fn main() { // You can optionally experiment here. } -// No test changes needed. +// Don't change the tests! #[cfg(test)] mod tests { use super::*; #[test] fn gopher_lives_in_burrow() { - assert_eq!(animal_habitat("gopher"), "Burrow"); + assert_eq!(animal_habitat("gopher"), "Burrow") } #[test] fn snake_lives_in_desert() { - assert_eq!(animal_habitat("snake"), "Desert"); + assert_eq!(animal_habitat("snake"), "Desert") } #[test] fn crab_lives_on_beach() { - assert_eq!(animal_habitat("crab"), "Beach"); + assert_eq!(animal_habitat("crab"), "Beach") } #[test] fn unknown_animal() { - assert_eq!(animal_habitat("dinosaur"), "Unknown"); + assert_eq!(animal_habitat("dinosaur"), "Unknown") } } diff --git a/solutions/03_if/if3.rs b/solutions/03_if/if3.rs index 4e18198923..571644d423 100644 --- a/solutions/03_if/if3.rs +++ b/solutions/03_if/if3.rs @@ -1 +1,53 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn animal_habitat(animal: &str) -> &str { + let identifier = if animal == "crab" { + 1 + } else if animal == "gopher" { + 2 + } else if animal == "snake" { + 3 + } else { + // Any unused identifier. + 4 + }; + + // Instead of such an identifier, you would use an enum in Rust. + // But we didn't get into enums yet. + if identifier == 1 { + "Beach" + } else if identifier == 2 { + "Burrow" + } else if identifier == 3 { + "Desert" + } else { + "Unknown" + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn gopher_lives_in_burrow() { + assert_eq!(animal_habitat("gopher"), "Burrow") + } + + #[test] + fn snake_lives_in_desert() { + assert_eq!(animal_habitat("snake"), "Desert") + } + + #[test] + fn crab_lives_on_beach() { + assert_eq!(animal_habitat("crab"), "Beach") + } + + #[test] + fn unknown_animal() { + assert_eq!(animal_habitat("dinosaur"), "Unknown") + } +} From f2c3dcab3ac20e5aeddc7f792409727803da8bb8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 22 May 2024 16:35:57 +0200 Subject: [PATCH 0902/1432] quiz1 solution --- exercises/quizzes/quiz1.rs | 17 ++++++----------- solutions/quizzes/quiz1.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/exercises/quizzes/quiz1.rs b/exercises/quizzes/quiz1.rs index edb672ee20..5f17514be4 100644 --- a/exercises/quizzes/quiz1.rs +++ b/exercises/quizzes/quiz1.rs @@ -10,27 +10,22 @@ // quantity bought. // Put your function here! -// fn calculate_price_of_apples { +// fn calculate_price_of_apples(???) -> ??? { fn main() { // You can optionally experiment here. } +// Don't change the tests! #[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!(calculate_price_of_apples(35), 70); + assert_eq!(calculate_price_of_apples(40), 80); + assert_eq!(calculate_price_of_apples(41), 41); + assert_eq!(calculate_price_of_apples(65), 65); } } diff --git a/solutions/quizzes/quiz1.rs b/solutions/quizzes/quiz1.rs index 4e18198923..bc76166731 100644 --- a/solutions/quizzes/quiz1.rs +++ b/solutions/quizzes/quiz1.rs @@ -1 +1,31 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Mary is buying apples. The price of an apple is calculated as follows: +// - An apple costs 2 rustbucks. +// - 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. + +fn calculate_price_of_apples(n_apples: u64) -> u64 { + if n_apples > 40 { + n_apples + } else { + 2 * n_apples + } +} + +fn main() { + // You can optionally experiment here. +} + +// Don't change the tests! +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_test() { + assert_eq!(calculate_price_of_apples(35), 70); + assert_eq!(calculate_price_of_apples(40), 80); + assert_eq!(calculate_price_of_apples(41), 41); + assert_eq!(calculate_price_of_apples(65), 65); + } +} From 8d4145038d8b1a2a31314669cc17dc42b1c8e616 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 25 May 2024 16:15:35 +0200 Subject: [PATCH 0903/1432] Update deps --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10dfa30657..e1044ff087 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -825,9 +825,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", From 990c68efcba8e1e2b7f2d8c5b6c16885d3920010 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 25 May 2024 16:31:21 +0200 Subject: [PATCH 0904/1432] primitive_types1 solution --- exercises/04_primitive_types/primitive_types1.rs | 8 +++----- rustlings-macros/info.toml | 5 ++++- solutions/04_primitive_types/primitive_types1.rs | 12 +++++++++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index 0002651d8b..750d6e552b 100644 --- a/exercises/04_primitive_types/primitive_types1.rs +++ b/exercises/04_primitive_types/primitive_types1.rs @@ -1,14 +1,12 @@ -// Fill in the rest of the line that has code missing! - fn main() { - // Booleans (`bool`) - let is_morning = true; if is_morning { println!("Good morning!"); } - let // Finish the rest of this line like the example! Or make it be false! + // TODO: Define a boolean variable with the name `is_evening` before the `if` statement below. + // The value of the variable should be the negation (opposite) of `is_morning`. + // let … if is_evening { println!("Good evening!"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 39fc5c4dcb..59de7f255e 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -234,7 +234,10 @@ hint = "No hints this time ;)" name = "primitive_types1" dir = "04_primitive_types" test = false -hint = "No hints this time ;)" +hint = """ +In Rust, a boolean can be negated using the operator `!` before it. +Example: `!true == false` +This also works with boolean variables.""" [[exercises]] name = "primitive_types2" diff --git a/solutions/04_primitive_types/primitive_types1.rs b/solutions/04_primitive_types/primitive_types1.rs index 4e18198923..fac6ec04a9 100644 --- a/solutions/04_primitive_types/primitive_types1.rs +++ b/solutions/04_primitive_types/primitive_types1.rs @@ -1 +1,11 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + let is_morning = true; + if is_morning { + println!("Good morning!"); + } + + let is_evening = !is_morning; + if is_evening { + println!("Good evening!"); + } +} From 5337620476526526544ce20b889043d6df69b13c Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Tue, 14 May 2024 12:10:47 +0200 Subject: [PATCH 0905/1432] docs: improved syntaxis of hint of traits4 and traits5 --- info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/info.toml b/info.toml index 5690701d55..ab403232db 100644 --- a/info.toml +++ b/info.toml @@ -816,7 +816,7 @@ path = "exercises/15_traits/traits4.rs" mode = "test" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing -the '??' with 'impl ' +the '??' with 'impl [what goes here?]' See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters """ @@ -827,7 +827,7 @@ path = "exercises/15_traits/traits5.rs" mode = "compile" hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try -replacing the '??' with 'impl <> + <>'. +replacing the '??' with 'impl [what goes here?] + [what goes here?]'. See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax """ From beb7b24e8e9fe05cbcfaaf4676d52f63351fad16 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 25 May 2024 18:19:30 +0200 Subject: [PATCH 0906/1432] Add solutions to bins --- solutions/00_intro/intro1.rs | 7 +++++-- src/cargo_toml.rs | 19 +++++++++++++++++++ src/embedded.rs | 18 +++++++----------- src/info_file.rs | 24 ++++++++++++++++++++++++ src/init.rs | 20 ++++++++++++++++++++ 5 files changed, 75 insertions(+), 13 deletions(-) diff --git a/solutions/00_intro/intro1.rs b/solutions/00_intro/intro1.rs index 07d4e4fdce..4fe84549e0 100644 --- a/solutions/00_intro/intro1.rs +++ b/solutions/00_intro/intro1.rs @@ -1,2 +1,5 @@ -// The exercise `intro1` only requires entering `n` in the terminal to go to the next exercise. -// It is just an introduction to how Rustlings works. +fn main() { + // Congratulations, you finished the first exercise πŸŽ‰ + // As an introduction to Rustlings, the first exercise only required + // entering `n` in the terminal to go to the next exercise. +} diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index b7951f6b66..cf17acd4f5 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use std::path::Path; use crate::info_file::ExerciseInfo; @@ -40,6 +41,24 @@ pub fn append_bins( } buf.extend_from_slice(exercise_info.name.as_bytes()); buf.extend_from_slice(b".rs\" },\n"); + + let sol_path = exercise_info.sol_path(); + if !Path::new(&sol_path).exists() { + continue; + } + + buf.extend_from_slice(b" { name = \""); + buf.extend_from_slice(exercise_info.name.as_bytes()); + buf.extend_from_slice(b"_sol"); + buf.extend_from_slice(b"\", path = \""); + buf.extend_from_slice(exercise_path_prefix); + buf.extend_from_slice(b"solutions/"); + 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"); } } diff --git a/src/embedded.rs b/src/embedded.rs index e710a4e536..1dce46c5e4 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Error, Result}; use std::{ - fs::{create_dir, create_dir_all, OpenOptions}, + fs::{create_dir, OpenOptions}, io::{self, Write}, }; @@ -43,8 +43,8 @@ struct ExerciseFiles { } // A directory in the `exercises/` directory. -struct ExerciseDir { - name: &'static str, +pub struct ExerciseDir { + pub name: &'static str, readme: &'static [u8], } @@ -78,7 +78,7 @@ pub struct EmbeddedFiles { /// The content of the `info.toml` file. pub info_file: &'static str, exercise_files: &'static [ExerciseFiles], - exercise_dirs: &'static [ExerciseDir], + pub exercise_dirs: &'static [ExerciseDir], } impl EmbeddedFiles { @@ -121,13 +121,9 @@ impl EmbeddedFiles { // 14 = 10 + 1 + 3 // solutions/ + / + .rs - let mut dir_path = String::with_capacity(14 + dir.name.len() + exercise_name.len()); - dir_path.push_str("solutions/"); - dir_path.push_str(dir.name); - create_dir_all(&dir_path) - .with_context(|| format!("Failed to create the directory {dir_path}"))?; - - let mut solution_path = dir_path; + let mut solution_path = String::with_capacity(14 + dir.name.len() + exercise_name.len()); + solution_path.push_str("solutions/"); + solution_path.push_str(dir.name); solution_path.push('/'); solution_path.push_str(exercise_name); solution_path.push_str(".rs"); diff --git a/src/info_file.rs b/src/info_file.rs index 0c459284f3..5ea487fcb4 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -49,6 +49,30 @@ impl ExerciseInfo { path } + + /// Path to the solution file starting with the `solutions/` directory. + pub fn sol_path(&self) -> String { + let mut path = if let Some(dir) = &self.dir { + // 14 = 10 + 1 + 3 + // solutions/ + / + .rs + let mut path = String::with_capacity(14 + dir.len() + self.name.len()); + path.push_str("solutions/"); + path.push_str(dir); + path.push('/'); + path + } else { + // 13 = 10 + 3 + // solutions/ + .rs + let mut path = String::with_capacity(13 + self.name.len()); + path.push_str("solutions/"); + path + }; + + path.push_str(&self.name); + path.push_str(".rs"); + + path + } } /// The deserialized `info.toml` file. diff --git a/src/init.rs b/src/init.rs index 67d8a24389..4063ca75f0 100644 --- a/src/init.rs +++ b/src/init.rs @@ -34,6 +34,20 @@ pub fn init() -> Result<()> { .init_exercises_dir(&info_file.exercises) .context("Failed to initialize the `rustlings/exercises` directory")?; + create_dir("solutions").context("Failed to create the `solutions/` directory")?; + for dir in EMBEDDED_FILES.exercise_dirs { + let mut dir_path = String::with_capacity(10 + dir.name.len()); + dir_path.push_str("solutions/"); + dir_path.push_str(dir.name); + create_dir(&dir_path) + .with_context(|| format!("Failed to create the directory {dir_path}"))?; + } + for exercise_info in &info_file.exercises { + let solution_path = exercise_info.sol_path(); + fs::write(&solution_path, INIT_SOLUTION_FILE) + .with_context(|| format!("Failed to create the file {solution_path}"))?; + } + let current_cargo_toml = include_str!("../dev-Cargo.toml"); // Skip the first line (comment). let newline_ind = current_cargo_toml @@ -72,6 +86,12 @@ pub fn init() -> Result<()> { Ok(()) } +const INIT_SOLUTION_FILE: &[u8] = b"fn main() { + // DON'T EDIT THIS SOLUTION FILE! + // It will be automatically filled after you finish the exercise. +} +"; + const GITIGNORE: &[u8] = b".rustlings-state.txt solutions Cargo.lock From 84a818dbda156aeccf97d8dc0a33daeef084013a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 15:01:18 +0200 Subject: [PATCH 0907/1432] Update the bins buffer capacity --- src/cargo_toml.rs | 5 ++++- src/dev/check.rs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index cf17acd4f5..c4d6700a86 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -3,6 +3,9 @@ use std::path::Path; use crate::info_file::ExerciseInfo; +/// Initial capacity of the bins buffer. +pub const BINS_BUFFER_CAPACITY: usize = 1 << 14; + /// Return the start and end index of the content of the list `bin = […]`. /// bin = [xxxxxxxxxxxxxxxxx] /// |start_ind | @@ -70,7 +73,7 @@ pub fn updated_cargo_toml( ) -> Result> { let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?; - let mut updated_cargo_toml = Vec::with_capacity(1 << 13); + let mut updated_cargo_toml = Vec::with_capacity(BINS_BUFFER_CAPACITY); updated_cargo_toml.extend_from_slice(current_cargo_toml[..bins_start_ind].as_bytes()); append_bins( &mut updated_cargo_toml, diff --git a/src/dev/check.rs b/src/dev/check.rs index 81d05ce633..7c35b4f3b5 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -7,7 +7,7 @@ use std::{ }; use crate::{ - cargo_toml::{append_bins, bins_start_end_ind}, + cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY}, info_file::{ExerciseInfo, InfoFile}, CURRENT_FORMAT_VERSION, DEBUG_PROFILE, }; @@ -145,7 +145,7 @@ fn check_cargo_toml( let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?; let old_bins = ¤t_cargo_toml.as_bytes()[bins_start_ind..bins_end_ind]; - let mut new_bins = Vec::with_capacity(1 << 13); + let mut new_bins = Vec::with_capacity(BINS_BUFFER_CAPACITY); append_bins(&mut new_bins, exercise_infos, exercise_path_prefix); if old_bins != new_bins { From 1984a8d38eddb037498afe4fc42351253ff58b05 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 15:01:27 +0200 Subject: [PATCH 0908/1432] Update Cargo.toml with the solution bins --- dev/Cargo.toml | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index ad39debfa7..3283871a7a 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,101 +1,197 @@ # Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`. This comment line will be stripped in `rustlings init`. bin = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, + { name = "intro1_sol", path = "../solutions/00_intro/intro1.rs" }, { name = "intro2", path = "../exercises/00_intro/intro2.rs" }, + { name = "intro2_sol", path = "../solutions/00_intro/intro2.rs" }, { name = "variables1", path = "../exercises/01_variables/variables1.rs" }, + { name = "variables1_sol", path = "../solutions/01_variables/variables1.rs" }, { name = "variables2", path = "../exercises/01_variables/variables2.rs" }, + { name = "variables2_sol", path = "../solutions/01_variables/variables2.rs" }, { name = "variables3", path = "../exercises/01_variables/variables3.rs" }, + { name = "variables3_sol", path = "../solutions/01_variables/variables3.rs" }, { name = "variables4", path = "../exercises/01_variables/variables4.rs" }, + { name = "variables4_sol", path = "../solutions/01_variables/variables4.rs" }, { name = "variables5", path = "../exercises/01_variables/variables5.rs" }, + { name = "variables5_sol", path = "../solutions/01_variables/variables5.rs" }, { name = "variables6", path = "../exercises/01_variables/variables6.rs" }, + { name = "variables6_sol", path = "../solutions/01_variables/variables6.rs" }, { name = "functions1", path = "../exercises/02_functions/functions1.rs" }, + { name = "functions1_sol", path = "../solutions/02_functions/functions1.rs" }, { name = "functions2", path = "../exercises/02_functions/functions2.rs" }, + { name = "functions2_sol", path = "../solutions/02_functions/functions2.rs" }, { name = "functions3", path = "../exercises/02_functions/functions3.rs" }, + { name = "functions3_sol", path = "../solutions/02_functions/functions3.rs" }, { name = "functions4", path = "../exercises/02_functions/functions4.rs" }, + { name = "functions4_sol", path = "../solutions/02_functions/functions4.rs" }, { name = "functions5", path = "../exercises/02_functions/functions5.rs" }, + { name = "functions5_sol", path = "../solutions/02_functions/functions5.rs" }, { name = "if1", path = "../exercises/03_if/if1.rs" }, + { name = "if1_sol", path = "../solutions/03_if/if1.rs" }, { name = "if2", path = "../exercises/03_if/if2.rs" }, + { name = "if2_sol", path = "../solutions/03_if/if2.rs" }, { name = "if3", path = "../exercises/03_if/if3.rs" }, + { name = "if3_sol", path = "../solutions/03_if/if3.rs" }, { name = "quiz1", path = "../exercises/quizzes/quiz1.rs" }, + { name = "quiz1_sol", path = "../solutions/quizzes/quiz1.rs" }, { name = "primitive_types1", path = "../exercises/04_primitive_types/primitive_types1.rs" }, + { name = "primitive_types1_sol", path = "../solutions/04_primitive_types/primitive_types1.rs" }, { name = "primitive_types2", path = "../exercises/04_primitive_types/primitive_types2.rs" }, + { name = "primitive_types2_sol", path = "../solutions/04_primitive_types/primitive_types2.rs" }, { name = "primitive_types3", path = "../exercises/04_primitive_types/primitive_types3.rs" }, + { name = "primitive_types3_sol", path = "../solutions/04_primitive_types/primitive_types3.rs" }, { name = "primitive_types4", path = "../exercises/04_primitive_types/primitive_types4.rs" }, + { name = "primitive_types4_sol", path = "../solutions/04_primitive_types/primitive_types4.rs" }, { name = "primitive_types5", path = "../exercises/04_primitive_types/primitive_types5.rs" }, + { name = "primitive_types5_sol", path = "../solutions/04_primitive_types/primitive_types5.rs" }, { name = "primitive_types6", path = "../exercises/04_primitive_types/primitive_types6.rs" }, + { name = "primitive_types6_sol", path = "../solutions/04_primitive_types/primitive_types6.rs" }, { name = "vecs1", path = "../exercises/05_vecs/vecs1.rs" }, + { name = "vecs1_sol", path = "../solutions/05_vecs/vecs1.rs" }, { name = "vecs2", path = "../exercises/05_vecs/vecs2.rs" }, + { name = "vecs2_sol", path = "../solutions/05_vecs/vecs2.rs" }, { name = "move_semantics1", path = "../exercises/06_move_semantics/move_semantics1.rs" }, + { name = "move_semantics1_sol", path = "../solutions/06_move_semantics/move_semantics1.rs" }, { name = "move_semantics2", path = "../exercises/06_move_semantics/move_semantics2.rs" }, + { name = "move_semantics2_sol", path = "../solutions/06_move_semantics/move_semantics2.rs" }, { name = "move_semantics3", path = "../exercises/06_move_semantics/move_semantics3.rs" }, + { name = "move_semantics3_sol", path = "../solutions/06_move_semantics/move_semantics3.rs" }, { name = "move_semantics4", path = "../exercises/06_move_semantics/move_semantics4.rs" }, + { name = "move_semantics4_sol", path = "../solutions/06_move_semantics/move_semantics4.rs" }, { name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" }, + { name = "move_semantics5_sol", path = "../solutions/06_move_semantics/move_semantics5.rs" }, { name = "move_semantics6", path = "../exercises/06_move_semantics/move_semantics6.rs" }, + { name = "move_semantics6_sol", path = "../solutions/06_move_semantics/move_semantics6.rs" }, { name = "structs1", path = "../exercises/07_structs/structs1.rs" }, + { name = "structs1_sol", path = "../solutions/07_structs/structs1.rs" }, { name = "structs2", path = "../exercises/07_structs/structs2.rs" }, + { name = "structs2_sol", path = "../solutions/07_structs/structs2.rs" }, { name = "structs3", path = "../exercises/07_structs/structs3.rs" }, + { name = "structs3_sol", path = "../solutions/07_structs/structs3.rs" }, { name = "enums1", path = "../exercises/08_enums/enums1.rs" }, + { name = "enums1_sol", path = "../solutions/08_enums/enums1.rs" }, { name = "enums2", path = "../exercises/08_enums/enums2.rs" }, + { name = "enums2_sol", path = "../solutions/08_enums/enums2.rs" }, { name = "enums3", path = "../exercises/08_enums/enums3.rs" }, + { name = "enums3_sol", path = "../solutions/08_enums/enums3.rs" }, { name = "strings1", path = "../exercises/09_strings/strings1.rs" }, + { name = "strings1_sol", path = "../solutions/09_strings/strings1.rs" }, { name = "strings2", path = "../exercises/09_strings/strings2.rs" }, + { name = "strings2_sol", path = "../solutions/09_strings/strings2.rs" }, { name = "strings3", path = "../exercises/09_strings/strings3.rs" }, + { name = "strings3_sol", path = "../solutions/09_strings/strings3.rs" }, { name = "strings4", path = "../exercises/09_strings/strings4.rs" }, + { name = "strings4_sol", path = "../solutions/09_strings/strings4.rs" }, { name = "modules1", path = "../exercises/10_modules/modules1.rs" }, + { name = "modules1_sol", path = "../solutions/10_modules/modules1.rs" }, { name = "modules2", path = "../exercises/10_modules/modules2.rs" }, + { name = "modules2_sol", path = "../solutions/10_modules/modules2.rs" }, { name = "modules3", path = "../exercises/10_modules/modules3.rs" }, + { name = "modules3_sol", path = "../solutions/10_modules/modules3.rs" }, { name = "hashmaps1", path = "../exercises/11_hashmaps/hashmaps1.rs" }, + { name = "hashmaps1_sol", path = "../solutions/11_hashmaps/hashmaps1.rs" }, { name = "hashmaps2", path = "../exercises/11_hashmaps/hashmaps2.rs" }, + { name = "hashmaps2_sol", path = "../solutions/11_hashmaps/hashmaps2.rs" }, { name = "hashmaps3", path = "../exercises/11_hashmaps/hashmaps3.rs" }, + { name = "hashmaps3_sol", path = "../solutions/11_hashmaps/hashmaps3.rs" }, { name = "quiz2", path = "../exercises/quizzes/quiz2.rs" }, + { name = "quiz2_sol", path = "../solutions/quizzes/quiz2.rs" }, { name = "options1", path = "../exercises/12_options/options1.rs" }, + { name = "options1_sol", path = "../solutions/12_options/options1.rs" }, { name = "options2", path = "../exercises/12_options/options2.rs" }, + { name = "options2_sol", path = "../solutions/12_options/options2.rs" }, { name = "options3", path = "../exercises/12_options/options3.rs" }, + { name = "options3_sol", path = "../solutions/12_options/options3.rs" }, { name = "errors1", path = "../exercises/13_error_handling/errors1.rs" }, + { name = "errors1_sol", path = "../solutions/13_error_handling/errors1.rs" }, { name = "errors2", path = "../exercises/13_error_handling/errors2.rs" }, + { name = "errors2_sol", path = "../solutions/13_error_handling/errors2.rs" }, { name = "errors3", path = "../exercises/13_error_handling/errors3.rs" }, + { name = "errors3_sol", path = "../solutions/13_error_handling/errors3.rs" }, { name = "errors4", path = "../exercises/13_error_handling/errors4.rs" }, + { name = "errors4_sol", path = "../solutions/13_error_handling/errors4.rs" }, { name = "errors5", path = "../exercises/13_error_handling/errors5.rs" }, + { name = "errors5_sol", path = "../solutions/13_error_handling/errors5.rs" }, { name = "errors6", path = "../exercises/13_error_handling/errors6.rs" }, + { name = "errors6_sol", path = "../solutions/13_error_handling/errors6.rs" }, { name = "generics1", path = "../exercises/14_generics/generics1.rs" }, + { name = "generics1_sol", path = "../solutions/14_generics/generics1.rs" }, { name = "generics2", path = "../exercises/14_generics/generics2.rs" }, + { name = "generics2_sol", path = "../solutions/14_generics/generics2.rs" }, { name = "traits1", path = "../exercises/15_traits/traits1.rs" }, + { name = "traits1_sol", path = "../solutions/15_traits/traits1.rs" }, { name = "traits2", path = "../exercises/15_traits/traits2.rs" }, + { name = "traits2_sol", path = "../solutions/15_traits/traits2.rs" }, { name = "traits3", path = "../exercises/15_traits/traits3.rs" }, + { name = "traits3_sol", path = "../solutions/15_traits/traits3.rs" }, { name = "traits4", path = "../exercises/15_traits/traits4.rs" }, + { name = "traits4_sol", path = "../solutions/15_traits/traits4.rs" }, { name = "traits5", path = "../exercises/15_traits/traits5.rs" }, + { name = "traits5_sol", path = "../solutions/15_traits/traits5.rs" }, { name = "quiz3", path = "../exercises/quizzes/quiz3.rs" }, + { name = "quiz3_sol", path = "../solutions/quizzes/quiz3.rs" }, { name = "lifetimes1", path = "../exercises/16_lifetimes/lifetimes1.rs" }, + { name = "lifetimes1_sol", path = "../solutions/16_lifetimes/lifetimes1.rs" }, { name = "lifetimes2", path = "../exercises/16_lifetimes/lifetimes2.rs" }, + { name = "lifetimes2_sol", path = "../solutions/16_lifetimes/lifetimes2.rs" }, { name = "lifetimes3", path = "../exercises/16_lifetimes/lifetimes3.rs" }, + { name = "lifetimes3_sol", path = "../solutions/16_lifetimes/lifetimes3.rs" }, { name = "tests1", path = "../exercises/17_tests/tests1.rs" }, + { name = "tests1_sol", path = "../solutions/17_tests/tests1.rs" }, { name = "tests2", path = "../exercises/17_tests/tests2.rs" }, + { name = "tests2_sol", path = "../solutions/17_tests/tests2.rs" }, { name = "tests3", path = "../exercises/17_tests/tests3.rs" }, + { name = "tests3_sol", path = "../solutions/17_tests/tests3.rs" }, { name = "tests4", path = "../exercises/17_tests/tests4.rs" }, + { name = "tests4_sol", path = "../solutions/17_tests/tests4.rs" }, { name = "iterators1", path = "../exercises/18_iterators/iterators1.rs" }, + { name = "iterators1_sol", path = "../solutions/18_iterators/iterators1.rs" }, { name = "iterators2", path = "../exercises/18_iterators/iterators2.rs" }, + { name = "iterators2_sol", path = "../solutions/18_iterators/iterators2.rs" }, { name = "iterators3", path = "../exercises/18_iterators/iterators3.rs" }, + { name = "iterators3_sol", path = "../solutions/18_iterators/iterators3.rs" }, { name = "iterators4", path = "../exercises/18_iterators/iterators4.rs" }, + { name = "iterators4_sol", path = "../solutions/18_iterators/iterators4.rs" }, { name = "iterators5", path = "../exercises/18_iterators/iterators5.rs" }, + { name = "iterators5_sol", path = "../solutions/18_iterators/iterators5.rs" }, { name = "box1", path = "../exercises/19_smart_pointers/box1.rs" }, + { name = "box1_sol", path = "../solutions/19_smart_pointers/box1.rs" }, { name = "rc1", path = "../exercises/19_smart_pointers/rc1.rs" }, + { name = "rc1_sol", path = "../solutions/19_smart_pointers/rc1.rs" }, { name = "arc1", path = "../exercises/19_smart_pointers/arc1.rs" }, + { name = "arc1_sol", path = "../solutions/19_smart_pointers/arc1.rs" }, { name = "cow1", path = "../exercises/19_smart_pointers/cow1.rs" }, + { name = "cow1_sol", path = "../solutions/19_smart_pointers/cow1.rs" }, { name = "threads1", path = "../exercises/20_threads/threads1.rs" }, + { name = "threads1_sol", path = "../solutions/20_threads/threads1.rs" }, { name = "threads2", path = "../exercises/20_threads/threads2.rs" }, + { name = "threads2_sol", path = "../solutions/20_threads/threads2.rs" }, { name = "threads3", path = "../exercises/20_threads/threads3.rs" }, + { name = "threads3_sol", path = "../solutions/20_threads/threads3.rs" }, { name = "macros1", path = "../exercises/21_macros/macros1.rs" }, + { name = "macros1_sol", path = "../solutions/21_macros/macros1.rs" }, { name = "macros2", path = "../exercises/21_macros/macros2.rs" }, + { name = "macros2_sol", path = "../solutions/21_macros/macros2.rs" }, { name = "macros3", path = "../exercises/21_macros/macros3.rs" }, + { name = "macros3_sol", path = "../solutions/21_macros/macros3.rs" }, { name = "macros4", path = "../exercises/21_macros/macros4.rs" }, + { name = "macros4_sol", path = "../solutions/21_macros/macros4.rs" }, { name = "clippy1", path = "../exercises/22_clippy/clippy1.rs" }, + { name = "clippy1_sol", path = "../solutions/22_clippy/clippy1.rs" }, { name = "clippy2", path = "../exercises/22_clippy/clippy2.rs" }, + { name = "clippy2_sol", path = "../solutions/22_clippy/clippy2.rs" }, { name = "clippy3", path = "../exercises/22_clippy/clippy3.rs" }, + { name = "clippy3_sol", path = "../solutions/22_clippy/clippy3.rs" }, { name = "using_as", path = "../exercises/23_conversions/using_as.rs" }, + { name = "using_as_sol", path = "../solutions/23_conversions/using_as.rs" }, { name = "from_into", path = "../exercises/23_conversions/from_into.rs" }, + { name = "from_into_sol", path = "../solutions/23_conversions/from_into.rs" }, { name = "from_str", path = "../exercises/23_conversions/from_str.rs" }, + { name = "from_str_sol", path = "../solutions/23_conversions/from_str.rs" }, { name = "try_from_into", path = "../exercises/23_conversions/try_from_into.rs" }, + { name = "try_from_into_sol", path = "../solutions/23_conversions/try_from_into.rs" }, { name = "as_ref_mut", path = "../exercises/23_conversions/as_ref_mut.rs" }, + { name = "as_ref_mut_sol", path = "../solutions/23_conversions/as_ref_mut.rs" }, ] [package] From c324ea10df1e8a19cd9fae9be0eda28d99b9f2e5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 15:10:17 +0200 Subject: [PATCH 0909/1432] Update deps --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1044ff087..fec0365a66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,9 +565,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -705,18 +705,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -1099,9 +1099,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index ebcbe6cf17..90e329eb33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" edition = "2021" [workspace.dependencies] -serde = { version = "1.0.202", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive"] } toml_edit = { version = "0.22.13", default-features = false, features = ["parse", "serde"] } [package] From 8e9c99ae5bfd9212c7d2a2c78186ab82133e69c7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 15:10:43 +0200 Subject: [PATCH 0910/1432] Change condition order --- src/dev/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 7c35b4f3b5..59352e9662 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -14,7 +14,7 @@ use crate::{ // Find a char that isn't allowed in the exercise's `name` or `dir`. fn forbidden_char(input: &str) -> Option { - input.chars().find(|c| *c != '_' && !c.is_alphanumeric()) + input.chars().find(|c| !c.is_alphanumeric() && *c != '_') } // Check the info of all exercises and return their paths in a set. From 611f9d8722593430d82187aebee9db5cc6952da1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 21:48:15 +0200 Subject: [PATCH 0911/1432] Check that all solutions run successfully --- src/app_state.rs | 41 ++++++++------- src/cmd.rs | 4 +- src/dev/check.rs | 59 ++++++++++++++------- src/exercise.rs | 124 ++++++++++++++++++++++++++++++--------------- src/info_file.rs | 19 ++++++- src/run.rs | 4 +- src/watch/state.rs | 4 +- 7 files changed, 169 insertions(+), 86 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index c7c090f6c2..e9a5b1094d 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,7 +11,7 @@ use std::{ use crate::{ clear_terminal, embedded::EMBEDDED_FILES, - exercise::{Exercise, OUTPUT_CAPACITY}, + exercise::{Exercise, RunnableExercise, OUTPUT_CAPACITY}, info_file::ExerciseInfo, DEBUG_PROFILE, }; @@ -40,6 +40,25 @@ struct CargoMetadata { target_directory: PathBuf, } +pub fn parse_target_dir() -> Result { + // Get the target directory from Cargo. + let metadata_output = Command::new("cargo") + .arg("metadata") + .arg("-q") + .arg("--format-version") + .arg("1") + .arg("--no-deps") + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output() + .context(CARGO_METADATA_ERR)? + .stdout; + + serde_json::de::from_slice::(&metadata_output) + .context("Failed to read the field `target_directory` from the `cargo metadata` output") + .map(|metadata| metadata.target_directory) +} + pub struct AppState { current_exercise_ind: usize, exercises: Vec, @@ -104,23 +123,7 @@ impl AppState { exercise_infos: Vec, final_message: String, ) -> Result<(Self, StateFileStatus)> { - // Get the target directory from Cargo. - let metadata_output = Command::new("cargo") - .arg("metadata") - .arg("-q") - .arg("--format-version") - .arg("1") - .arg("--no-deps") - .stdin(Stdio::null()) - .stderr(Stdio::inherit()) - .output() - .context(CARGO_METADATA_ERR)? - .stdout; - let target_dir = serde_json::de::from_slice::(&metadata_output) - .context( - "Failed to read the field `target_directory` from the `cargo metadata` output", - )? - .target_directory; + let target_dir = parse_target_dir()?; let exercises = exercise_infos .into_iter() @@ -381,7 +384,7 @@ impl AppState { write!(writer, "Running {exercise} ... ")?; writer.flush()?; - let success = exercise.run(&mut output, &self.target_dir)?; + let success = exercise.run_exercise(&mut output, &self.target_dir)?; if !success { writeln!(writer, "{}\n", "FAILED".red())?; diff --git a/src/cmd.rs b/src/cmd.rs index b914ed88dc..6092f531ca 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -35,7 +35,7 @@ pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec) -> Res pub struct CargoCmd<'a> { pub subcommand: &'a str, pub args: &'a [&'a str], - pub exercise_name: &'a str, + pub bin_name: &'a str, pub description: &'a str, /// RUSTFLAGS="-A warnings" pub hide_warnings: bool, @@ -65,7 +65,7 @@ impl<'a> CargoCmd<'a> { .arg("always") .arg("-q") .arg("--bin") - .arg(self.exercise_name) + .arg(self.bin_name) .args(self.args); if self.hide_warnings { diff --git a/src/dev/check.rs b/src/dev/check.rs index 59352e9662..6a3597cc58 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -2,12 +2,14 @@ use anyhow::{anyhow, bail, Context, Error, Result}; use std::{ cmp::Ordering, fs::{self, read_dir, OpenOptions}, - io::Read, + io::{self, Read, Write}, path::{Path, PathBuf}, }; use crate::{ + app_state::parse_target_dir, cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY}, + exercise::{RunnableExercise, OUTPUT_CAPACITY}, info_file::{ExerciseInfo, InfoFile}, CURRENT_FORMAT_VERSION, DEBUG_PROFILE, }; @@ -17,6 +19,29 @@ fn forbidden_char(input: &str) -> Option { input.chars().find(|c| !c.is_alphanumeric() && *c != '_') } +// Check that the Cargo.toml file is up-to-date. +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 = ¤t_cargo_toml.as_bytes()[bins_start_ind..bins_end_ind]; + let mut new_bins = Vec::with_capacity(BINS_BUFFER_CAPACITY); + append_bins(&mut new_bins, exercise_infos, exercise_path_prefix); + + if old_bins != new_bins { + if DEBUG_PROFILE { + bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it"); + } + + bail!("The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it"); + } + + Ok(()) +} + // Check the info of all exercises and return their paths in a set. fn check_info_file_exercises(info_file: &InfoFile) -> Result> { let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); @@ -136,24 +161,20 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { Ok(()) } -// Check that the Cargo.toml file is up-to-date. -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)?; +fn check_solutions(info_file: &InfoFile) -> Result<()> { + let target_dir = parse_target_dir()?; + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - let old_bins = ¤t_cargo_toml.as_bytes()[bins_start_ind..bins_end_ind]; - let mut new_bins = Vec::with_capacity(BINS_BUFFER_CAPACITY); - append_bins(&mut new_bins, exercise_infos, exercise_path_prefix); - - if old_bins != new_bins { - if DEBUG_PROFILE { - bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it"); + for exercise_info in &info_file.exercises { + let success = exercise_info.run_solution(&mut output, &target_dir)?; + if !success { + io::stderr().write_all(&output)?; + + bail!( + "Failed to run the solution of the exercise {}", + exercise_info.name, + ); } - - bail!("The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it"); } Ok(()) @@ -161,7 +182,6 @@ fn check_cargo_toml( pub fn check() -> Result<()> { let info_file = InfoFile::parse()?; - check_exercises(&info_file)?; // A hack to make `cargo run -- dev check` work when developing Rustlings. if DEBUG_PROFILE { @@ -176,6 +196,9 @@ pub fn check() -> Result<()> { check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"")?; } + check_exercises(&info_file)?; + check_solutions(&info_file)?; + println!("\nEverything looks fine!"); Ok(()) diff --git a/src/exercise.rs b/src/exercise.rs index 4bc37cd777..b6adc141e7 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -17,6 +17,35 @@ use crate::{ /// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; +// Run an exercise binary and append its output to the `output` buffer. +// Compilation must be done before calling this method. +fn run_bin(bin_name: &str, output: &mut Vec, target_dir: &Path) -> Result { + writeln!(output, "{}", "Output".underlined())?; + + // 7 = "/debug/".len() + let mut bin_path = PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + bin_name.len()); + bin_path.push(target_dir); + bin_path.push("debug"); + bin_path.push(bin_name); + + let success = run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output)?; + + if !success { + // This output is important to show the user that something went wrong. + // Otherwise, calling something like `exit(1)` in an exercise without further output + // leaves the user confused about why the exercise isn't done yet. + writeln!( + output, + "{}", + "The exercise didn't run successfully (nonzero exit code)" + .bold() + .red(), + )?; + } + + Ok(success) +} + /// See `info_file::ExerciseInfo` pub struct Exercise { pub dir: Option<&'static str>, @@ -30,39 +59,25 @@ pub struct Exercise { } impl Exercise { - // Run the exercise's binary and append its output to the `output` buffer. - // Compilation should be done before calling this method. - fn run_bin(&self, output: &mut Vec, target_dir: &Path) -> Result { - writeln!(output, "{}", "Output".underlined())?; - - // 7 = "/debug/".len() - let mut bin_path = - PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + self.name.len()); - bin_path.push(target_dir); - bin_path.push("debug"); - bin_path.push(self.name); - - let success = run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output)?; - - if !success { - // This output is important to show the user that something went wrong. - // Otherwise, calling something like `exit(1)` in an exercise without further output - // leaves the user confused about why the exercise isn't done yet. - writeln!( - output, - "{}", - "The exercise didn't run successfully (nonzero exit code)" - .bold() - .red(), - )?; - } + pub fn terminal_link(&self) -> StyledContent> { + style(TerminalFileLink(self.path)).underlined().blue() + } +} - Ok(success) +impl Display for Exercise { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.path.fmt(f) } +} - /// Compile, check and run the exercise. - /// The output is written to the `output` buffer after clearing it. - pub fn run(&self, output: &mut Vec, target_dir: &Path) -> Result { +pub trait RunnableExercise { + fn name(&self) -> &str; + fn strict_clippy(&self) -> bool; + fn test(&self) -> bool; + + // Compile, check and run the exercise or its solution (depending on `bin_nameΒ΄). + // The output is written to the `output` buffer after clearing it. + fn run(&self, bin_name: &str, output: &mut Vec, target_dir: &Path) -> Result { output.clear(); // Developing the official Rustlings. @@ -71,7 +86,7 @@ impl Exercise { let build_success = CargoCmd { subcommand: "build", args: &[], - exercise_name: self.name, + bin_name, description: "cargo build …", hide_warnings: false, target_dir, @@ -87,7 +102,7 @@ impl Exercise { output.clear(); // `--profile test` is required to also check code with `[cfg(test)]`. - let clippy_args: &[&str] = if self.strict_clippy { + let clippy_args: &[&str] = if self.strict_clippy() { &["--profile", "test", "--", "-D", "warnings"] } else { &["--profile", "test"] @@ -95,7 +110,7 @@ impl Exercise { let clippy_success = CargoCmd { subcommand: "clippy", args: clippy_args, - exercise_name: self.name, + bin_name, description: "cargo clippy …", hide_warnings: false, target_dir, @@ -107,14 +122,14 @@ impl Exercise { return Ok(false); } - if !self.test { - return self.run_bin(output, target_dir); + if !self.test() { + return run_bin(bin_name, output, target_dir); } let test_success = CargoCmd { subcommand: "test", args: &["--", "--color", "always", "--show-output"], - exercise_name: self.name, + bin_name, description: "cargo test …", // Hide warnings because they are shown by Clippy. hide_warnings: true, @@ -124,18 +139,43 @@ impl Exercise { } .run()?; - let run_success = self.run_bin(output, target_dir)?; + let run_success = run_bin(bin_name, output, target_dir)?; Ok(test_success && run_success) } - pub fn terminal_link(&self) -> StyledContent> { - style(TerminalFileLink(self.path)).underlined().blue() + /// Compile, check and run the exercise. + /// The output is written to the `output` buffer after clearing it. + #[inline] + fn run_exercise(&self, output: &mut Vec, target_dir: &Path) -> Result { + self.run(self.name(), output, target_dir) + } + + /// Compile, check and run the exercise's solution. + /// The output is written to the `output` buffer after clearing it. + fn run_solution(&self, output: &mut Vec, target_dir: &Path) -> Result { + let name = self.name(); + let mut bin_name = String::with_capacity(name.len()); + bin_name.push_str(name); + bin_name.push_str("_sol"); + + self.run(&bin_name, output, target_dir) } } -impl Display for Exercise { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.path.fmt(f) +impl RunnableExercise for Exercise { + #[inline] + fn name(&self) -> &str { + self.name + } + + #[inline] + fn strict_clippy(&self) -> bool { + self.strict_clippy + } + + #[inline] + fn test(&self) -> bool { + self.test } } diff --git a/src/info_file.rs b/src/info_file.rs index 5ea487fcb4..f226f73540 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -2,7 +2,7 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; use std::{fs, io::ErrorKind}; -use crate::embedded::EMBEDDED_FILES; +use crate::{embedded::EMBEDDED_FILES, exercise::RunnableExercise}; /// Deserialized from the `info.toml` file. #[derive(Deserialize)] @@ -75,6 +75,23 @@ impl ExerciseInfo { } } +impl RunnableExercise for ExerciseInfo { + #[inline] + fn name(&self) -> &str { + &self.name + } + + #[inline] + fn strict_clippy(&self) -> bool { + self.strict_clippy + } + + #[inline] + fn test(&self) -> bool { + self.test + } +} + /// The deserialized `info.toml` file. #[derive(Deserialize)] pub struct InfoFile { diff --git a/src/run.rs b/src/run.rs index 36899b91ec..899d0a94db 100644 --- a/src/run.rs +++ b/src/run.rs @@ -4,14 +4,14 @@ use std::io::{self, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, - exercise::OUTPUT_CAPACITY, + exercise::{RunnableExercise, OUTPUT_CAPACITY}, terminal_link::TerminalFileLink, }; pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - let success = exercise.run(&mut output, app_state.target_dir())?; + let success = exercise.run_exercise(&mut output, app_state.target_dir())?; let mut stdout = io::stdout().lock(); stdout.write_all(&output)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 14c3f01533..dd43c56610 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -8,7 +8,7 @@ use std::io::{self, StdoutLock, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, clear_terminal, - exercise::OUTPUT_CAPACITY, + exercise::{RunnableExercise, OUTPUT_CAPACITY}, progress_bar::progress_bar, terminal_link::TerminalFileLink, }; @@ -54,7 +54,7 @@ impl<'a> WatchState<'a> { let success = self .app_state .current_exercise() - .run(&mut self.output, self.app_state.target_dir())?; + .run_exercise(&mut self.output, self.app_state.target_dir())?; if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { From 50530fa3cff5bc765291603873728799930d764b Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 21:50:11 +0200 Subject: [PATCH 0912/1432] Don't try to check a solution that doesn't exist --- src/dev/check.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dev/check.rs b/src/dev/check.rs index 6a3597cc58..78396a8b14 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -166,6 +166,11 @@ fn check_solutions(info_file: &InfoFile) -> Result<()> { let mut output = Vec::with_capacity(OUTPUT_CAPACITY); for exercise_info in &info_file.exercises { + if !Path::new(&exercise_info.sol_path()).exists() { + // No solution to check. + continue; + } + let success = exercise_info.run_solution(&mut output, &target_dir)?; if !success { io::stderr().write_all(&output)?; From a3ada0eee8b207870dd3774f23afbc5c4e2b3f12 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 21:51:45 +0200 Subject: [PATCH 0913/1432] Print the exercise solution on check --- src/dev/check.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dev/check.rs b/src/dev/check.rs index 78396a8b14..61b14190f4 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -171,6 +171,7 @@ fn check_solutions(info_file: &InfoFile) -> Result<()> { continue; } + println!("Running the solution of {}", exercise_info.name); let success = exercise_info.run_solution(&mut output, &target_dir)?; if !success { io::stderr().write_all(&output)?; From 6ae4a979f48301d259666129d2138291cd21246a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 2 Jun 2024 00:03:48 +0200 Subject: [PATCH 0914/1432] Check for unexpected files in the solutions dir --- src/dev/check.rs | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 61b14190f4..15ff08801f 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, bail, Context, Error, Result}; +use anyhow::{anyhow, bail, Context, Result}; use std::{ cmp::Ordering, fs::{self, read_dir, OpenOptions}, @@ -99,14 +99,19 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result) -> Result<()> { - 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()) - } +// Check `dir` for unexpected files. +// Only Rust files in `allowed_rust_files` and `README.md` files are allowed. +// Only one level of directory nesting is allowed. +fn check_unexpected_files( + dir: &str, + allowed_rust_files: &hashbrown::HashSet, +) -> Result<()> { + let unexpected_file = |path: &Path| { + anyhow!("Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory", path.display()) + }; - for entry in read_dir("exercises").context("Failed to open the `exercises` directory")? { - let entry = entry.context("Failed to read the `exercises` directory")?; + for entry in read_dir(dir).with_context(|| format!("Failed to open the `{dir}` directory"))? { + let entry = entry.with_context(|| format!("Failed to read the `{dir}` directory"))?; if entry.file_type().unwrap().is_file() { let path = entry.path(); @@ -115,7 +120,7 @@ fn check_unexpected_files(info_file_paths: &hashbrown::HashSet) -> Resu continue; } - if !info_file_paths.contains(&path) { + if !allowed_rust_files.contains(&path) { return Err(unexpected_file(&path)); } @@ -139,7 +144,7 @@ fn check_unexpected_files(info_file_paths: &hashbrown::HashSet) -> Resu continue; } - if !info_file_paths.contains(&path) { + if !allowed_rust_files.contains(&path) { return Err(unexpected_file(&path)); } } @@ -156,17 +161,19 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { } let info_file_paths = check_info_file_exercises(info_file)?; - check_unexpected_files(&info_file_paths)?; + check_unexpected_files("exercises", &info_file_paths)?; Ok(()) } fn check_solutions(info_file: &InfoFile) -> Result<()> { + let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); let target_dir = parse_target_dir()?; let mut output = Vec::with_capacity(OUTPUT_CAPACITY); for exercise_info in &info_file.exercises { - if !Path::new(&exercise_info.sol_path()).exists() { + let path = exercise_info.sol_path(); + if !Path::new(&path).exists() { // No solution to check. continue; } @@ -181,8 +188,12 @@ fn check_solutions(info_file: &InfoFile) -> Result<()> { exercise_info.name, ); } + + paths.insert(PathBuf::from(path)); } + check_unexpected_files("solutions", &paths)?; + Ok(()) } From 08ac11ff2250190a47a74a767c2efa3a71ce1e73 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 2 Jun 2024 00:11:41 +0200 Subject: [PATCH 0915/1432] Add --require-solutions option to `dev check` --- src/dev.rs | 8 ++++++-- src/dev/check.rs | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index fada8b330a..5f7e64c8d6 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -19,7 +19,11 @@ pub enum DevCommands { no_git: bool, }, /// Run checks on the exercises - Check, + Check { + /// Require that every exercise has a solution + #[arg(short, long)] + require_solutions: bool, + }, /// Update the `Cargo.toml` file for the exercises Update, } @@ -34,7 +38,7 @@ impl DevCommands { new::new(&path, no_git).context(INIT_ERR) } - Self::Check => check::check(), + Self::Check { require_solutions } => check::check(require_solutions), Self::Update => update::update(), } } diff --git a/src/dev/check.rs b/src/dev/check.rs index 15ff08801f..ef45cd288d 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -166,7 +166,7 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { Ok(()) } -fn check_solutions(info_file: &InfoFile) -> Result<()> { +fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> { let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); let target_dir = parse_target_dir()?; let mut output = Vec::with_capacity(OUTPUT_CAPACITY); @@ -174,6 +174,10 @@ fn check_solutions(info_file: &InfoFile) -> Result<()> { for exercise_info in &info_file.exercises { let path = exercise_info.sol_path(); if !Path::new(&path).exists() { + if require_solutions { + bail!("Exercise {} is missing a solution", exercise_info.name); + } + // No solution to check. continue; } @@ -197,7 +201,7 @@ fn check_solutions(info_file: &InfoFile) -> Result<()> { Ok(()) } -pub fn check() -> Result<()> { +pub fn check(require_solutions: bool) -> Result<()> { let info_file = InfoFile::parse()?; // A hack to make `cargo run -- dev check` work when developing Rustlings. @@ -214,7 +218,7 @@ pub fn check() -> Result<()> { } check_exercises(&info_file)?; - check_solutions(&info_file)?; + check_solutions(require_solutions, &info_file)?; println!("\nEverything looks fine!"); From 42bd0b8b75cbec30cb506f42295413ba0a6a1680 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 6 Jun 2024 01:58:05 +0200 Subject: [PATCH 0916/1432] Update deps --- Cargo.lock | 38 ++++++++++++++++---------------------- Cargo.toml | 4 ++-- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fec0365a66..fc37b23e16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -177,7 +177,7 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn", @@ -312,12 +312,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -496,9 +490,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "os_pipe" -version = "1.1.5" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" dependencies = [ "libc", "windows-sys 0.52.0", @@ -565,9 +559,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -812,11 +806,11 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -851,9 +845,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -886,9 +880,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" @@ -1099,9 +1093,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 90e329eb33..8845027d74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ edition = "2021" [workspace.dependencies] serde = { version = "1.0.203", features = ["derive"] } -toml_edit = { version = "0.22.13", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -51,7 +51,7 @@ clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } -os_pipe = "1.1.5" +os_pipe = "1.2.0" ratatui = { version = "0.26.3", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } serde_json = "1.0.117" From f8d38320cd239d0abbd1b24fc972fd6f7f592ed9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 6 Jun 2024 01:59:09 +0200 Subject: [PATCH 0917/1432] Fix typos --- exercises/02_functions/functions5.rs | 2 +- solutions/02_functions/functions5.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs index 31fd057a87..34a2ac7dce 100644 --- a/exercises/02_functions/functions5.rs +++ b/exercises/02_functions/functions5.rs @@ -1,4 +1,4 @@ -// TODO: Fix the function body without chaning the signature. +// TODO: Fix the function body without changing the signature. fn square(num: i32) -> i32 { num * num; } diff --git a/solutions/02_functions/functions5.rs b/solutions/02_functions/functions5.rs index 03354186b9..677f32780e 100644 --- a/solutions/02_functions/functions5.rs +++ b/solutions/02_functions/functions5.rs @@ -1,5 +1,5 @@ fn square(num: i32) -> i32 { - // Removed the semicolon `;` at the end of the line below to implicitely return the result. + // Removed the semicolon `;` at the end of the line below to implicitly return the result. num * num } From 0e4136d31e9aff282532b1a85cf40013bfba3f27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 8 Jun 2024 21:19:16 +0200 Subject: [PATCH 0918/1432] Update deps --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc37b23e16..58183ad28f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.4" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" dependencies = [ "anstream", "anstyle", @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -886,9 +886,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" @@ -1093,9 +1093,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.11" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 8845027d74..6ca25d7d2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.4", features = ["derive"] } +clap = { version = "4.5.6", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } From e1051724c3f8d9dc3d25bcb854e0c4ac7ff3b2b6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 8 Jun 2024 21:35:44 +0200 Subject: [PATCH 0919/1432] primitive_types2 solution --- .../04_primitive_types/primitive_types1.rs | 2 ++ .../04_primitive_types/primitive_types2.rs | 13 ++++++----- .../04_primitive_types/primitive_types2.rs | 22 ++++++++++++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index 750d6e552b..84923c7500 100644 --- a/exercises/04_primitive_types/primitive_types1.rs +++ b/exercises/04_primitive_types/primitive_types1.rs @@ -1,3 +1,5 @@ +// Booleans (`bool`) + fn main() { let is_morning = true; if is_morning { diff --git a/exercises/04_primitive_types/primitive_types2.rs b/exercises/04_primitive_types/primitive_types2.rs index 29c74718ad..14018475dd 100644 --- a/exercises/04_primitive_types/primitive_types2.rs +++ b/exercises/04_primitive_types/primitive_types2.rs @@ -1,6 +1,6 @@ -fn main() { - // Characters (`char`) +// Characters (`char`) +fn main() { // Note the _single_ quotes, these are different from the double quotes // you've been seeing around. let my_first_initial = 'C'; @@ -12,9 +12,12 @@ fn main() { println!("Neither alphabetic nor numeric!"); } - let // Finish this line like the example! What's your favorite character? - // Try a letter, try a number, try a special character, try a character - // from a different language than your own, try an emoji! + // TODO: Analogous to the example before, declare a variable called `your_character` + // below with your favorite character. + // Try a letter, try a digit (in single quotes), try a special character, try a character + // from a different language than your own, try an emoji πŸ˜‰ + // let your_character = ''; + if your_character.is_alphabetic() { println!("Alphabetical!"); } else if your_character.is_numeric() { diff --git a/solutions/04_primitive_types/primitive_types2.rs b/solutions/04_primitive_types/primitive_types2.rs index 4e18198923..eecc6802f9 100644 --- a/solutions/04_primitive_types/primitive_types2.rs +++ b/solutions/04_primitive_types/primitive_types2.rs @@ -1 +1,21 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + let my_first_initial = 'C'; + if my_first_initial.is_alphabetic() { + println!("Alphabetical!"); + } else if my_first_initial.is_numeric() { + println!("Numerical!"); + } else { + println!("Neither alphabetic nor numeric!"); + } + + // Example with an emoji. + let your_character = 'πŸ¦€'; + + if your_character.is_alphabetic() { + println!("Alphabetical!"); + } else if your_character.is_numeric() { + println!("Numerical!"); + } else { + println!("Neither alphabetic nor numeric!"); + } +} From 0338b1cbdfa567d5f9580afef1d4483c7d275c32 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 8 Jun 2024 21:43:38 +0200 Subject: [PATCH 0920/1432] primitive_types3 solution --- exercises/04_primitive_types/primitive_types3.rs | 7 +++---- rustlings-macros/info.toml | 2 +- solutions/04_primitive_types/primitive_types3.rs | 12 +++++++++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index 5095fc4ae4..bef5579ef5 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -1,12 +1,11 @@ -// Create an array with at least 100 elements in it where the ??? is. - fn main() { - let a = ??? + // TODO: Create an array with at least 100 elements in it where the ??? is. + // let a = ??? if a.len() >= 100 { println!("Wow, that's a big array!"); } else { println!("Meh, I eat arrays like that for breakfast."); - panic!("Array not big enough, more elements needed") + panic!("Array not big enough, more elements needed"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 59de7f255e..fc0bee80d1 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -250,7 +250,7 @@ name = "primitive_types3" dir = "04_primitive_types" test = false hint = """ -There's a shorthand to initialize Arrays with a certain size that does not +There's a shorthand to initialize arrays with a certain size that doesn't require you to type in 100 items (but you certainly can if you want!). For example, you can do: diff --git a/solutions/04_primitive_types/primitive_types3.rs b/solutions/04_primitive_types/primitive_types3.rs index 4e18198923..8dd109f92e 100644 --- a/solutions/04_primitive_types/primitive_types3.rs +++ b/solutions/04_primitive_types/primitive_types3.rs @@ -1 +1,11 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // An array with 100 elements of the value 42. + let a = [42; 100]; + + if a.len() >= 100 { + println!("Wow, that's a big array!"); + } else { + println!("Meh, I eat arrays like that for breakfast."); + panic!("Array not big enough, more elements needed"); + } +} From 98db5790144a0d32009718e54051b3f7d54ae494 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 8 Jun 2024 23:42:15 +0200 Subject: [PATCH 0921/1432] primitive_types4 solution --- .../04_primitive_types/primitive_types3.rs | 2 +- .../04_primitive_types/primitive_types4.rs | 7 ++---- rustlings-macros/info.toml | 4 ++-- .../04_primitive_types/primitive_types4.rs | 24 ++++++++++++++++++- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index bef5579ef5..9b79c0cf2a 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -1,5 +1,5 @@ fn main() { - // TODO: Create an array with at least 100 elements in it where the ??? is. + // TODO: Create an array called `a` with at least 100 elements in it. // let a = ??? if a.len() >= 100 { diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index 661e0515dc..16e4fd9321 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -1,18 +1,15 @@ -// Get a slice out of Array a where the ??? is so that the test passes. - 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 = ??? + // TODO: Get a slice called `nice_slice` out of the array `a` so that the test passes. + // let nice_slice = ??? assert_eq!([2, 3, 4], nice_slice); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index fc0bee80d1..313ba6f745 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -267,8 +267,8 @@ dir = "04_primitive_types" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the -starting and ending (plus one) indices of the items in the `Array` that you -want to end up in the slice. +starting and ending (plus one) indices of the items in the array that you want +to end up in the slice. If you're curious why the first argument of `assert_eq!` does not have an ampersand for a reference since the second argument is a reference, take a look diff --git a/solutions/04_primitive_types/primitive_types4.rs b/solutions/04_primitive_types/primitive_types4.rs index 4e18198923..4807e66c64 100644 --- a/solutions/04_primitive_types/primitive_types4.rs +++ b/solutions/04_primitive_types/primitive_types4.rs @@ -1 +1,23 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn slice_out_of_array() { + let a = [1, 2, 3, 4, 5]; + // 0 1 2 3 4 <- indices + // ------- + // | + // +--- slice + + // Note that the upper index 4 is excluded. + let nice_slice = &a[1..4]; + assert_eq!([2, 3, 4], nice_slice); + + // The upper index can be included by using the syntax `..=` (with `=` sign) + let nice_slice = &a[1..=3]; + assert_eq!([2, 3, 4], nice_slice); + } +} From 42a35039067861e82f5ec16901d12cb888834f09 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 10 Jun 2024 17:42:11 +0200 Subject: [PATCH 0922/1432] Run solutions in parallel --- src/dev/check.rs | 74 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index ef45cd288d..336360be6f 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -4,6 +4,11 @@ use std::{ fs::{self, read_dir, OpenOptions}, io::{self, Read, Write}, path::{Path, PathBuf}, + sync::{ + atomic::{self, AtomicBool}, + Mutex, + }, + thread, }; use crate::{ @@ -167,36 +172,52 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { } fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> { - let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); let target_dir = parse_target_dir()?; - let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - - for exercise_info in &info_file.exercises { - let path = exercise_info.sol_path(); - if !Path::new(&path).exists() { - if require_solutions { - bail!("Exercise {} is missing a solution", exercise_info.name); - } - - // No solution to check. - continue; + let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len())); + let error_occured = AtomicBool::new(false); + + println!("Running all solutions. This may take a while...\n"); + thread::scope(|s| { + for exercise_info in &info_file.exercises { + s.spawn(|| { + let error = |e| { + let mut stderr = io::stderr().lock(); + stderr.write_all(e).unwrap(); + stderr + .write_all(b"\nFailed to run the solution of the exercise ") + .unwrap(); + stderr.write_all(exercise_info.name.as_bytes()).unwrap(); + stderr.write_all(SEPARATOR).unwrap(); + error_occured.store(true, atomic::Ordering::Relaxed); + }; + + let path = exercise_info.sol_path(); + if !Path::new(&path).exists() { + if require_solutions { + error(b"Solution missing"); + } + + // No solution to check. + return; + } + + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + match exercise_info.run_solution(&mut output, &target_dir) { + Ok(true) => { + paths.lock().unwrap().insert(PathBuf::from(path)); + } + Ok(false) => error(&output), + Err(e) => error(e.to_string().as_bytes()), + } + }); } + }); - println!("Running the solution of {}", exercise_info.name); - let success = exercise_info.run_solution(&mut output, &target_dir)?; - if !success { - io::stderr().write_all(&output)?; - - bail!( - "Failed to run the solution of the exercise {}", - exercise_info.name, - ); - } - - paths.insert(PathBuf::from(path)); + if error_occured.load(atomic::Ordering::Relaxed) { + bail!("At least one solution failed. See the output above."); } - check_unexpected_files("solutions", &paths)?; + check_unexpected_files("solutions", &paths.into_inner().unwrap())?; Ok(()) } @@ -224,3 +245,6 @@ pub fn check(require_solutions: bool) -> Result<()> { Ok(()) } + +const SEPARATOR: &[u8] = + b"\n========================================================================================\n"; From 2ff18137462513883533a10c929fe59364b8f884 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 14 Jun 2024 13:32:02 +0200 Subject: [PATCH 0923/1432] Update deps --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58183ad28f..40b23f6627 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -422,9 +422,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mio" @@ -615,9 +615,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -627,9 +627,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -638,9 +638,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustlings" diff --git a/Cargo.toml b/Cargo.toml index 6ca25d7d2a..8e67312dfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.6", features = ["derive"] } +clap = { version = "4.5.7", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } From 5bf8d1fa1bcbf885c7cd9c7ae49494826c209da6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 14 Jun 2024 13:32:37 +0200 Subject: [PATCH 0924/1432] Fix typos --- exercises/01_variables/variables5.rs | 2 +- src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index 73f655e36a..49db8e9ed2 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -2,7 +2,7 @@ fn main() { let number = "T-H-R-E-E"; // Don't change this line println!("Spell a number: {}", number); - // TODO: Fix the compiler error by changing the line below without renaming the the variable. + // TODO: Fix the compiler error by changing the line below without renaming the variable. number = 3; println!("Number plus two is: {}", number + 2); } diff --git a/src/main.rs b/src/main.rs index cf6f0d9616..2233d8b069 100644 --- a/src/main.rs +++ b/src/main.rs @@ -151,7 +151,7 @@ fn main() -> Result<()> { let notify_exercise_names = if args.manual_run { None } else { - // For the the notify event handler thread. + // For the notify event handler thread. // Leaking is not a problem because the slice lives until the end of the program. Some( &*app_state From 2a1bc5377177981fa253ae4cb234bce584ce1b62 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 19 Jun 2024 14:03:06 +0200 Subject: [PATCH 0925/1432] Update deps --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40b23f6627..82c06f9bfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,7 +516,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -606,9 +606,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] From 532c9ebb30afa226590e68e87af11da42b598974 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 19 Jun 2024 14:17:06 +0200 Subject: [PATCH 0926/1432] primitive_types5 solution --- exercises/04_primitive_types/primitive_types5.rs | 8 ++++---- rustlings-macros/info.toml | 2 +- solutions/04_primitive_types/primitive_types5.rs | 9 ++++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs index f2216a55d1..6e00ef51f8 100644 --- a/exercises/04_primitive_types/primitive_types5.rs +++ b/exercises/04_primitive_types/primitive_types5.rs @@ -1,8 +1,8 @@ -// Destructure the `cat` tuple so that the println will work. - fn main() { let cat = ("Furry McFurson", 3.5); - let /* your pattern here */ = cat; - println!("{} is {} years old.", name, age); + // TODO: Destructure the `cat` tuple in one statement so that the println works. + // let /* your pattern here */ = cat; + + println!("{name} is {age} years old"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 313ba6f745..e5f58777a5 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -286,7 +286,7 @@ Particularly the part about destructuring (second to last example in the section). You'll need to make a pattern to bind `name` and `age` to the appropriate parts -of the tuple. You can do it!!""" +of the tuple.""" [[exercises]] name = "primitive_types6" diff --git a/solutions/04_primitive_types/primitive_types5.rs b/solutions/04_primitive_types/primitive_types5.rs index 4e18198923..46d7ae87a1 100644 --- a/solutions/04_primitive_types/primitive_types5.rs +++ b/solutions/04_primitive_types/primitive_types5.rs @@ -1 +1,8 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + let cat = ("Furry McFurson", 3.5); + + // Destructuring the tuple. + let (name, age) = cat; + + println!("{name} is {age} years old"); +} From 0abcdeed42957ca805a3a7475fb3f14085af346e Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 19 Jun 2024 14:25:29 +0200 Subject: [PATCH 0927/1432] primitive_types6 solution --- .../04_primitive_types/primitive_types6.rs | 14 +++++--------- rustlings-macros/info.toml | 2 +- .../04_primitive_types/primitive_types6.rs | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs index 83cec24b5d..a97e53110e 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -1,21 +1,17 @@ -// 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. - 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!") + // TODO: Use a tuple index to access the second element of `numbers` + // and assign it to a variable called `second`. + // let second = ???; + + assert_eq!(second, 2, "This is not the 2nd number in the tuple!"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index e5f58777a5..cd85dcc3d0 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -296,7 +296,7 @@ While you could use a destructuring `let` for the tuple here, try indexing into it instead, as explained in the last example of the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Now you have another tool in your toolbox!""" +Now, you have another tool in your toolbox!""" # VECS diff --git a/solutions/04_primitive_types/primitive_types6.rs b/solutions/04_primitive_types/primitive_types6.rs index 4e18198923..9b7c277971 100644 --- a/solutions/04_primitive_types/primitive_types6.rs +++ b/solutions/04_primitive_types/primitive_types6.rs @@ -1 +1,16 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn indexing_tuple() { + let numbers = (1, 2, 3); + + // Tuple indexing syntax. + let second = numbers.1; + + assert_eq!(second, 2, "This is not the 2nd number in the tuple!"); + } +} From 1ede3a82e9df4a0cd93110e49722701946b41e11 Mon Sep 17 00:00:00 2001 From: jphilis <117293564+jphilis@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:55:34 +0200 Subject: [PATCH 0928/1432] chore: update error message to error message given by rustc. error[E0596] --- info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.toml b/info.toml index e8c19bf43b..ba9b6e3d2a 100644 --- a/info.toml +++ b/info.toml @@ -318,7 +318,7 @@ name = "move_semantics1" path = "exercises/06_move_semantics/move_semantics1.rs" mode = "test" hint = """ -So you've got the "cannot borrow immutable local variable `vec` as mutable" +So you've got the "cannot borrow `vec` as mutable, as it is not declared as mutable" error on the line where we push an element to the vector, right? The fix for this is going to be adding one keyword, and the addition is NOT on From a9f0c7bf1f00ab19733953d3121d462eede34466 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 20 Jun 2024 01:00:06 +0200 Subject: [PATCH 0929/1432] vecs1 solution --- exercises/05_vecs/vecs1.rs | 14 ++++++-------- rustlings-macros/info.toml | 5 +++-- solutions/05_vecs/vecs1.rs | 24 +++++++++++++++++++++++- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index ddcad84b9f..68e1affaa7 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -1,11 +1,9 @@ -// 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! - fn array_and_vec() -> ([i32; 4], Vec) { - let a = [10, 20, 30, 40]; // a plain array - let v = // TODO: declare your vector here with the macro for vectors + let a = [10, 20, 30, 40]; // Array + + // TODO: Create a vector called `v` which contains the exact same elements as in the array `a`. + // Use the vector macro. + // let v = ???; (a, v) } @@ -21,6 +19,6 @@ mod tests { #[test] fn test_array_and_vec_similarity() { let (a, v) = array_and_vec(); - assert_eq!(a, v[..]); + assert_eq!(a, *v); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index cd85dcc3d0..21a27dd1a3 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -307,8 +307,9 @@ hint = """ In Rust, there are two ways to define a Vector. 1. One way is to use the `Vec::new()` function to create a new vector and fill it with the `push()` method. -2. The second way, which is simpler is to use the `vec![]` macro and - define your elements inside the square brackets. +2. The second way is to use the `vec![]` macro and define your elements + inside the square brackets. This way is simpler when you exactly know + the initial values. Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html of the Rust book to learn more. diff --git a/solutions/05_vecs/vecs1.rs b/solutions/05_vecs/vecs1.rs index 4e18198923..55b5676c0a 100644 --- a/solutions/05_vecs/vecs1.rs +++ b/solutions/05_vecs/vecs1.rs @@ -1 +1,23 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn array_and_vec() -> ([i32; 4], Vec) { + let a = [10, 20, 30, 40]; // Array + + // Used the `vec!` macro. + let v = vec![10, 20, 30, 40]; + + (a, v) +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_array_and_vec_similarity() { + let (a, v) = array_and_vec(); + assert_eq!(a, *v); + } +} From 835ec7262247a341295c1d6f3772901a5fad5148 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 14:52:11 +0200 Subject: [PATCH 0930/1432] vecs2 solution + significant change to have a better comparison between both methods --- exercises/05_vecs/vecs2.rs | 62 +++++++++++++++++++++++--------------- rustlings-macros/info.toml | 11 ++----- solutions/05_vecs/vecs2.rs | 51 ++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index e72209c443..a9be2580f2 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -1,25 +1,32 @@ -// 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! - -fn vec_loop(mut v: Vec) -> Vec { - for element in v.iter_mut() { - // TODO: Fill this up so that each element in the Vec `v` is - // multiplied by 2. - ??? +fn vec_loop(input: &[i32]) -> Vec { + let mut output = Vec::new(); + + for element in input { + // TODO: Multiply each element in the `input` slice by 2 and push it to + // the `output` vector. } - // At this point, `v` should be equal to [4, 8, 12, 16, 20]. - v + output +} + +fn vec_map_example(input: &[i32]) -> Vec { + // An example of collecting a vector after mapping. + // We map each element of the `input` slice to its value plus 1. + // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. + input.iter().map(|element| element + 1).collect() } -fn vec_map(v: &Vec) -> Vec { - v.iter().map(|element| { - // TODO: Do the same thing as above - but instead of mutating the - // Vec, you can just return the new number! - ??? - }).collect() +fn vec_map(input: &[i32]) -> Vec { + // TODO: Here, we also want to multiply each element in the `input` slice + // by 2, but with iterator mapping instead of manually pushing into an empty + // vector. + // See the example in the function `vec_map_example` above. + input + .iter() + .map(|element| { + // ??? + }) + .collect() } fn main() { @@ -32,17 +39,22 @@ mod tests { #[test] fn test_vec_loop() { - let v: Vec = (1..).filter(|x| x % 2 == 0).take(5).collect(); - let ans = vec_loop(v.clone()); + let input = [2, 4, 6, 8, 10]; + let ans = vec_loop(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); + } - assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); + #[test] + fn test_vec_map_example() { + let input = [1, 2, 3]; + let ans = vec_map_example(&input); + assert_eq!(ans, [2, 3, 4]); } #[test] fn test_vec_map() { - let v: Vec = (1..).filter(|x| x % 2 == 0).take(5).collect(); - let ans = vec_map(&v); - - assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); + let input = [2, 4, 6, 8, 10]; + let ans = vec_map(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 21a27dd1a3..3edd1c6745 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -319,15 +319,10 @@ of the Rust book to learn more. name = "vecs2" dir = "05_vecs" hint = """ -In the first function we are looping over the Vector and getting a reference to -one `element` at a time. +In the first function, we create an empty vector and want to push new elements +to it. -To modify the value of that `element` we need to use the `*` dereference -operator. You can learn more in this chapter of the Rust book: -https://doc.rust-lang.org/stable/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector - -In the second function this dereferencing is not necessary, because the `map` -function expects the new value to be returned. +In the second function, we map the values of the input and collect them into a vector. After you've completed both functions, decide for yourself which approach you like better. diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs index 4e18198923..32c1c0f6c3 100644 --- a/solutions/05_vecs/vecs2.rs +++ b/solutions/05_vecs/vecs2.rs @@ -1 +1,50 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn vec_loop(input: &[i32]) -> Vec { + let mut output = Vec::new(); + + for element in input { + output.push(2 * element); + } + + output +} + +fn vec_map_example(input: &[i32]) -> Vec { + // An example of collecting a vector after mapping. + // We map each element of the `input` slice to its value plus 1. + // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. + input.iter().map(|element| element + 1).collect() +} + +fn vec_map(input: &[i32]) -> Vec { + input.iter().map(|element| 2 * element).collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_vec_loop() { + let input = [2, 4, 6, 8, 10]; + let ans = vec_loop(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); + } + + #[test] + fn test_vec_map_example() { + let input = [1, 2, 3]; + let ans = vec_map_example(&input); + assert_eq!(ans, [2, 3, 4]); + } + + #[test] + fn test_vec_map() { + let input = [2, 4, 6, 8, 10]; + let ans = vec_map(&input); + assert_eq!(ans, [4, 8, 12, 16, 20]); + } +} From 6a79ada7f2afc668418c8fd15e2db622f3d833af Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 15:06:50 +0200 Subject: [PATCH 0931/1432] Add comment to vecs2 --- solutions/05_vecs/vecs2.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs index 32c1c0f6c3..87f7625a6e 100644 --- a/solutions/05_vecs/vecs2.rs +++ b/solutions/05_vecs/vecs2.rs @@ -16,6 +16,11 @@ fn vec_map_example(input: &[i32]) -> Vec { } fn vec_map(input: &[i32]) -> Vec { + // We will dive deeper into iterators, but for now, this is all what you + // had to do! + // Advanced note: This method is more efficient because it automatically + // preallocates enough capacity. This can be done manually in `vec_loop` + // using `Vec::with_capacity(input.len())` instead of `Vec::new()`. input.iter().map(|element| 2 * element).collect() } From 946c29679e27433ff455bdb30343551757d87769 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 16:16:52 +0200 Subject: [PATCH 0932/1432] move_semantics1 solution --- .../06_move_semantics/move_semantics1.rs | 3 +-- rustlings-macros/info.toml | 3 +-- .../06_move_semantics/move_semantics1.rs | 26 ++++++++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs index 8c3fe3a7f8..4eb3d618ef 100644 --- a/exercises/06_move_semantics/move_semantics1.rs +++ b/exercises/06_move_semantics/move_semantics1.rs @@ -1,3 +1,4 @@ +// TODO: Fix the compiler error in this function. fn fill_vec(vec: Vec) -> Vec { let vec = vec; @@ -17,9 +18,7 @@ mod tests { #[test] fn move_semantics1() { let vec0 = vec![22, 44, 66]; - let vec1 = fill_vec(vec0); - assert_eq!(vec1, vec![22, 44, 66, 88]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 3edd1c6745..bfe32cdd10 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -342,8 +342,7 @@ error on the line where we push an element to the vector, right? The fix for this is going to be adding one keyword, and the addition is NOT on the line where we push to the vector (where the error is). -Also: Try accessing `vec0` after having called `fill_vec()`. See what -happens!""" +Try accessing `vec0` after having called `fill_vec()`. See what happens!""" [[exercises]] name = "move_semantics2" diff --git a/solutions/06_move_semantics/move_semantics1.rs b/solutions/06_move_semantics/move_semantics1.rs index 4e18198923..ac34e7a07e 100644 --- a/solutions/06_move_semantics/move_semantics1.rs +++ b/solutions/06_move_semantics/move_semantics1.rs @@ -1 +1,25 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn fill_vec(vec: Vec) -> Vec { + let mut vec = vec; + // ^^^ added + + vec.push(88); + + vec +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics1() { + let vec0 = vec![22, 44, 66]; + let vec1 = fill_vec(vec0); + // `vec0` can't be accessed anymore because it is moved to `fill_vec`. + assert_eq!(vec1, vec![22, 44, 66, 88]); + } +} From 68142aff7f439f3a797b4e97a275ca7800eebc45 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 17:02:50 +0200 Subject: [PATCH 0933/1432] move_semantics2 solution --- .../06_move_semantics/move_semantics2.rs | 8 ++--- rustlings-macros/info.toml | 14 +++------ .../06_move_semantics/move_semantics2.rs | 29 ++++++++++++++++++- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index d0879113c2..a3ab7a0f18 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -1,5 +1,3 @@ -// Make the test pass by finding a way to keep both Vecs separate! - fn fill_vec(vec: Vec) -> Vec { let mut vec = vec; @@ -16,13 +14,15 @@ fn main() { mod tests { use super::*; + // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to + // fix the compiler error in the test. #[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]); + assert_eq!(vec0, [22, 44, 66]); + assert_eq!(vec1, [22, 44, 66, 88]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index bfe32cdd10..fb0126c14d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -352,16 +352,10 @@ When running this exercise for the first time, you'll notice an error about "borrow of moved value". In Rust, when an argument is passed to a function and it's not explicitly returned, you can't use the original variable anymore. We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's -being "moved" into `vec1`, meaning we can't access `vec0` anymore after the -fact. - -Rust provides a couple of different ways to mitigate this issue, feel free to -try them all: -1. You could make another, separate version of the data that's in `vec0` and - pass that to `fill_vec` instead. -2. Make `fill_vec` borrow its argument instead of taking ownership of it, - and then copy the data within the function (`vec.clone()`) in order to - return an owned `Vec`. +being "moved" into `vec1`, meaning we can't access `vec0` anymore. + +You could make another, separate version of the data that's in `vec0` and +pass it to `fill_vec` instead. """ [[exercises]] diff --git a/solutions/06_move_semantics/move_semantics2.rs b/solutions/06_move_semantics/move_semantics2.rs index 4e18198923..7bcd33a05d 100644 --- a/solutions/06_move_semantics/move_semantics2.rs +++ b/solutions/06_move_semantics/move_semantics2.rs @@ -1 +1,28 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn fill_vec(vec: Vec) -> Vec { + let mut vec = vec; + + vec.push(88); + + vec +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics2() { + let vec0 = vec![22, 44, 66]; + + // Cloning `vec0` so that the clone is moved into `fill_vec`, not `vec0` + // itself. + let vec1 = fill_vec(vec0.clone()); + + assert_eq!(vec0, [22, 44, 66]); + assert_eq!(vec1, [22, 44, 66, 88]); + } +} From fd558065c7f32d38d1b8e34fda1a23fe40d1b3ab Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 17:04:51 +0200 Subject: [PATCH 0934/1432] move_semantics3 solution --- .../06_move_semantics/move_semantics3.rs | 8 ++----- .../06_move_semantics/move_semantics3.rs | 23 ++++++++++++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs index 24e35971a2..11dbbbebff 100644 --- a/exercises/06_move_semantics/move_semantics3.rs +++ b/exercises/06_move_semantics/move_semantics3.rs @@ -1,6 +1,4 @@ -// Make me compile without adding new lines -- just changing existing lines! (no -// lines with multiple semicolons necessary!) - +// TODO: Fix the compiler error in the function without adding any new line. fn fill_vec(vec: Vec) -> Vec { vec.push(88); @@ -18,9 +16,7 @@ mod tests { #[test] fn move_semantics3() { let vec0 = vec![22, 44, 66]; - let vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); + assert_eq!(vec1, [22, 44, 66, 88]); } } diff --git a/solutions/06_move_semantics/move_semantics3.rs b/solutions/06_move_semantics/move_semantics3.rs index 4e18198923..7ba4006b76 100644 --- a/solutions/06_move_semantics/move_semantics3.rs +++ b/solutions/06_move_semantics/move_semantics3.rs @@ -1 +1,22 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn fill_vec(mut vec: Vec) -> Vec { + // ^^^ added + vec.push(88); + + vec +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics3() { + let vec0 = vec![22, 44, 66]; + let vec1 = fill_vec(vec0); + assert_eq!(vec1, [22, 44, 66, 88]); + } +} From e4dbbbf5f5f5d4ea0ede1ead1f82108968e6cea6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 18:14:19 +0200 Subject: [PATCH 0935/1432] Remove move_semantics4, add rest of move_semantics solutions --- .../06_move_semantics/move_semantics4.rs | 31 ++++++------------- .../06_move_semantics/move_semantics5.rs | 31 ++++++++++--------- .../06_move_semantics/move_semantics6.rs | 21 ------------- rustlings-macros/info.toml | 27 +++------------- .../06_move_semantics/move_semantics4.rs | 22 ++++++++++++- .../06_move_semantics/move_semantics5.rs | 22 ++++++++++++- .../06_move_semantics/move_semantics6.rs | 1 - 7 files changed, 72 insertions(+), 83 deletions(-) delete mode 100644 exercises/06_move_semantics/move_semantics6.rs delete mode 100644 solutions/06_move_semantics/move_semantics6.rs diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index b6622244a0..c225f3ba00 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -1,31 +1,18 @@ -// 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 test function. - -// `fill_vec()` no longer takes `vec: Vec` as argument - don't change this! -fn fill_vec() -> Vec { - // Instead, let's create and fill the Vec in here - how do you do that? - let mut vec = vec; - - vec.push(88); - - vec -} - fn main() { // You can optionally experiment here. } #[cfg(test)] mod tests { - use super::*; - + // TODO: Fix the compiler errors only by reordering the lines in the test. + // Don't add, change or remove any line. #[test] - fn move_semantics4() { - let vec0 = vec![22, 44, 66]; - - let vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); + 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_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index b34560ab54..c9edf41d94 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -1,21 +1,22 @@ -// Make me compile only by reordering the lines in the test, but without adding, -// changing or removing any of them. +// TODO: Fix the compiler erros. Don't change anything except adding or removing +// references (the character `&`). fn main() { - // You can optionally experiment here. + let data = "Rust is great!".to_string(); + + get_char(data); + + string_uppercase(&data); +} + +// Shouldn't take ownership +fn get_char(data: String) -> char { + data.chars().last().unwrap() } -#[cfg(test)] -mod tests { - use super::*; +// Should take ownership +fn string_uppercase(mut data: &String) { + data = &data.to_uppercase(); - #[test] - fn move_semantics5() { - let mut x = 100; - let y = &mut x; - let z = &mut x; - *y += 100; - *z += 1000; - assert_eq!(x, 1200); - } + println!("{data}"); } diff --git a/exercises/06_move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs deleted file mode 100644 index 2ad71db29a..0000000000 --- a/exercises/06_move_semantics/move_semantics6.rs +++ /dev/null @@ -1,21 +0,0 @@ -// You can't change anything except adding or removing references. - -fn main() { - let data = "Rust is great!".to_string(); - - get_char(data); - - string_uppercase(&data); -} - -// Should not take ownership -fn get_char(data: String) -> char { - data.chars().last().unwrap() -} - -// Should take ownership -fn string_uppercase(mut data: &String) { - data = &data.to_uppercase(); - - println!("{}", data); -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index fb0126c14d..d6236c51e9 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -371,28 +371,15 @@ an existing binding to be a mutable binding instead of an immutable one :)""" name = "move_semantics4" dir = "06_move_semantics" hint = """ -Stop reading whenever you feel like you have enough direction :) Or try -doing one step and then fixing the compiler errors that result! -So the end goal is to: - - get rid of the first line in main that creates the new vector - - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` - - `fill_vec` has had its signature changed, which our call should reflect - - since we're not creating a new vec in `main` anymore, we need to create - a new vec in `fill_vec`, and fill it with the expected values""" - -[[exercises]] -name = "move_semantics5" -dir = "06_move_semantics" -hint = """ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of referent (`x`) immediately after -the mutable reference is taken? Read more about 'Mutable References' -in the book's section 'References and Borrowing': +the mutable reference is taken? +Read more about 'Mutable References' in the book's section 'References and Borrowing': https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. """ [[exercises]] -name = "move_semantics6" +name = "move_semantics5" dir = "06_move_semantics" test = false hint = """ @@ -401,14 +388,10 @@ https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html The first problem is that `get_char` is taking ownership of the string. So `data` is moved and can't be used for `string_uppercase`. `data` is moved to -`get_char` first, meaning that `string_uppercase` cannot manipulate the data. +`get_char` first, meaning that `string_uppercase` can't manipulate the data. Once you've fixed that, `string_uppercase`'s function signature will also need -to be adjusted. - -Can you figure out how? - -Another hint: it has to do with the `&` character.""" +to be adjusted.""" # STRUCTS diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs index 4e18198923..b7919ac04d 100644 --- a/solutions/06_move_semantics/move_semantics4.rs +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -1 +1,21 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + // TODO: Fix the compiler errors only by reordering the lines in the test. + // Don't add, change or remove any line. + #[test] + fn move_semantics5() { + let mut x = 100; + let y = &mut x; + // `y` used here. + *y += 100; + // The mutable reference `y` is not used anymore, + // therefore a new reference can be created. + let z = &mut x; + *z += 1000; + assert_eq!(x, 1200); + } +} diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs index 4e18198923..1b3ca4ebf2 100644 --- a/solutions/06_move_semantics/move_semantics5.rs +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -1 +1,21 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + let data = "Rust is great!".to_string(); + + get_char(&data); + + string_uppercase(data); +} + +// Borrows instead of taking ownership. +// It is recommended to use `&str` instead of `&String` here. But this is +// enough for now because we didn't handle strings yet. +fn get_char(data: &String) -> char { + data.chars().last().unwrap() +} + +// Takes ownership instead of borrowing. +fn string_uppercase(mut data: String) { + data = data.to_uppercase(); + + println!("{data}"); +} diff --git a/solutions/06_move_semantics/move_semantics6.rs b/solutions/06_move_semantics/move_semantics6.rs deleted file mode 100644 index 4e18198923..0000000000 --- a/solutions/06_move_semantics/move_semantics6.rs +++ /dev/null @@ -1 +0,0 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° From d768353806f905989b4cc29cd7a97891cbbf8ec3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 18:29:00 +0200 Subject: [PATCH 0936/1432] Fix typo --- exercises/06_move_semantics/move_semantics5.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index c9edf41d94..650656882f 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -1,5 +1,5 @@ -// TODO: Fix the compiler erros. Don't change anything except adding or removing -// references (the character `&`). +// TODO: Fix the compiler errors without changing anything except adding or +// removing references (the character `&`). fn main() { let data = "Rust is great!".to_string(); From ef842d3a946f477a32e26b9674cc5488cd629030 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 22:22:37 +0200 Subject: [PATCH 0937/1432] structs1 solution --- exercises/07_structs/structs1.rs | 25 ++++++++-------- rustlings-macros/info.toml | 9 +++--- solutions/07_structs/structs1.rs | 50 +++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs index 62f1421933..959c4c6a68 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -1,13 +1,12 @@ -// Address all the TODOs to make the tests pass! - -struct ColorClassicStruct { - // TODO: Something goes here +struct ColorRegularStruct { + // TODO: Add the fields that the test `regular_structs` expects. + // What types should the fields have? What are the minimum and maximum values for RGB colors? } -struct ColorTupleStruct(/* TODO: Something goes here */); +struct ColorTupleStruct(/* TODO: Add the fields that the test `tuple_structs` expects */); #[derive(Debug)] -struct UnitLikeStruct; +struct UnitStruct; fn main() { // You can optionally experiment here. @@ -18,8 +17,8 @@ mod tests { use super::*; #[test] - fn classic_c_structs() { - // TODO: Instantiate a classic c struct! + fn regular_structs() { + // TODO: Instantiate a regular struct. // let green = assert_eq!(green.red, 0); @@ -29,7 +28,7 @@ mod tests { #[test] fn tuple_structs() { - // TODO: Instantiate a tuple struct! + // TODO: Instantiate a tuple struct. // let green = assert_eq!(green.0, 0); @@ -39,10 +38,10 @@ mod tests { #[test] fn unit_structs() { - // TODO: Instantiate a unit-like struct! - // let unit_like_struct = - let message = format!("{:?}s are fun!", unit_like_struct); + // TODO: Instantiate a unit struct. + // let unit_struct = + let message = format!("{unit_struct:?}s are fun!"); - assert_eq!(message, "UnitLikeStructs are fun!"); + assert_eq!(message, "UnitStructs are fun!"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index d6236c51e9..81b9895232 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -402,15 +402,14 @@ hint = """ Rust has more than one type of struct. Three actually, all variants are used to package related data together. -There are normal (or classic) structs. These are named collections of related -data stored in fields. +There are regular structs. These are named collections of related data stored in +fields. Tuple structs are basically just named tuples. -Finally, Unit-like structs. These don't have any fields and are useful for -generics. +Finally, unit structs. These don't have any fields and are useful for generics. -In this exercise you need to complete and implement one of each kind. +In this exercise, you need to complete and implement one of each kind. Read more about structs in The Book: https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" diff --git a/solutions/07_structs/structs1.rs b/solutions/07_structs/structs1.rs index 4e18198923..98fafcc52a 100644 --- a/solutions/07_structs/structs1.rs +++ b/solutions/07_structs/structs1.rs @@ -1 +1,49 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +struct ColorRegularStruct { + red: u8, + green: u8, + blue: u8, +} + +struct ColorTupleStruct(u8, u8, u8); + +#[derive(Debug)] +struct UnitStruct; + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn regular_structs() { + let green = ColorRegularStruct { + red: 0, + green: 255, + blue: 0, + }; + + assert_eq!(green.red, 0); + assert_eq!(green.green, 255); + assert_eq!(green.blue, 0); + } + + #[test] + fn tuple_structs() { + let green = ColorTupleStruct(0, 255, 0); + + assert_eq!(green.0, 0); + assert_eq!(green.1, 255); + assert_eq!(green.2, 0); + } + + #[test] + fn unit_structs() { + let unit_struct = UnitStruct; + let message = format!("{unit_struct:?}s are fun!"); + + assert_eq!(message, "UnitStructs are fun!"); + } +} From 1264510fc04de85efc0e2caf17aaa85354b6bffd Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 22:31:06 +0200 Subject: [PATCH 0938/1432] structs2 solution --- exercises/07_structs/structs2.rs | 4 +-- rustlings-macros/info.toml | 2 +- solutions/07_structs/structs2.rs | 52 +++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs index 451dbe7607..79141af95d 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -1,5 +1,3 @@ -// Address all the TODOs to make the tests pass! - #[derive(Debug)] struct Order { name: String, @@ -34,8 +32,10 @@ mod tests { #[test] fn your_order() { let order_template = create_order_template(); + // TODO: Create your own order using the update syntax and template above! // let your_order = + assert_eq!(your_order.name, "Hacker in Rust"); assert_eq!(your_order.year, order_template.year); assert_eq!(your_order.made_by_phone, order_template.made_by_phone); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 81b9895232..08bbd74d17 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -421,7 +421,7 @@ Creating instances of structs is easy, all you need to do is assign some values to its fields. There are however some shortcuts that can be taken when instantiating structs. -Have a look in The Book, to find out more: +Have a look in The Book to find out more: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" [[exercises]] diff --git a/solutions/07_structs/structs2.rs b/solutions/07_structs/structs2.rs index 4e18198923..589dd93315 100644 --- a/solutions/07_structs/structs2.rs +++ b/solutions/07_structs/structs2.rs @@ -1 +1,51 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +#[derive(Debug)] +struct Order { + name: String, + year: u32, + made_by_phone: bool, + made_by_mobile: bool, + made_by_email: bool, + item_number: u32, + count: u32, +} + +fn create_order_template() -> Order { + Order { + name: String::from("Bob"), + year: 2019, + made_by_phone: false, + made_by_mobile: false, + made_by_email: true, + item_number: 123, + count: 0, + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn your_order() { + let order_template = create_order_template(); + + let your_order = Order { + name: String::from("Hacker in Rust"), + count: 1, + // Struct update syntax + ..order_template + }; + + assert_eq!(your_order.name, "Hacker in Rust"); + assert_eq!(your_order.year, order_template.year); + assert_eq!(your_order.made_by_phone, order_template.made_by_phone); + assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile); + assert_eq!(your_order.made_by_email, order_template.made_by_email); + assert_eq!(your_order.item_number, order_template.item_number); + assert_eq!(your_order.count, 1); + } +} From d6fd251a73f2abd96662b09b32f718021568675c Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 22:54:00 +0200 Subject: [PATCH 0939/1432] structs3 solution --- exercises/07_structs/structs3.rs | 33 +++++++------ rustlings-macros/info.toml | 2 +- solutions/07_structs/structs3.rs | 84 +++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 18 deletions(-) diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 10adb48772..18a6cc98d9 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,6 +1,5 @@ // 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! +// defined the `Package` struct and we want to test some logic attached to it. #[derive(Debug)] struct Package { @@ -10,26 +9,28 @@ struct Package { } impl Package { - fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Package { + fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self { if weight_in_grams < 10 { - // This is not how you should handle errors in Rust, - // but we will learn about error handling later. - panic!("Can not ship a package with weight below 10 grams.") - } else { - Package { - sender_country, - recipient_country, - weight_in_grams, - } + // This isn't how you should handle errors in Rust, but we will + // learn about error handling later. + panic!("Can't ship a package with weight below 10 grams"); + } + + Self { + sender_country, + recipient_country, + weight_in_grams, } } - fn is_international(&self) -> ??? { - // Something goes here... + // TODO: Add the correct return type to the function signature. + fn is_international(&self) { + // TODO: Read the tests that use this method to find out when a package is concidered international. } - fn get_fees(&self, cents_per_gram: u32) -> ??? { - // Something goes here... + // TODO: Add the correct return type to the function signature. + fn get_fees(&self, cents_per_gram: u32) { + // TODO: Calculate the package's fees. } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 08bbd74d17..7535b682df 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -434,7 +434,7 @@ the places it goes through right? For `get_fees`: This method takes an additional argument, is there a field in the `Package` struct that this relates to? -Have a look in The Book, to find out more about method implementations: +Have a look in The Book to find out more about method implementations: https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" # ENUMS diff --git a/solutions/07_structs/structs3.rs b/solutions/07_structs/structs3.rs index 4e18198923..3f878cc86c 100644 --- a/solutions/07_structs/structs3.rs +++ b/solutions/07_structs/structs3.rs @@ -1 +1,83 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +#[derive(Debug)] +struct Package { + sender_country: String, + recipient_country: String, + weight_in_grams: u32, +} + +impl Package { + fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self { + if weight_in_grams < 10 { + // This isn't how you should handle errors in Rust, but we will + // learn about error handling later. + panic!("Can't ship a package with weight below 10 grams"); + } + + Self { + sender_country, + recipient_country, + weight_in_grams, + } + } + + fn is_international(&self) -> bool { + // ^^^^^^^ added + self.sender_country != self.recipient_country + } + + fn get_fees(&self, cents_per_gram: u32) -> u32 { + // ^^^^^^ added + self.weight_in_grams * cents_per_gram + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic] + fn fail_creating_weightless_package() { + let sender_country = String::from("Spain"); + let recipient_country = String::from("Austria"); + + Package::new(sender_country, recipient_country, 5); + } + + #[test] + fn create_international_package() { + let sender_country = String::from("Spain"); + let recipient_country = String::from("Russia"); + + let package = Package::new(sender_country, recipient_country, 1200); + + assert!(package.is_international()); + } + + #[test] + fn create_local_package() { + let sender_country = String::from("Canada"); + let recipient_country = sender_country.clone(); + + let package = Package::new(sender_country, recipient_country, 1200); + + assert!(!package.is_international()); + } + + #[test] + fn calculate_transport_fees() { + let sender_country = String::from("Spain"); + let recipient_country = String::from("Spain"); + + let cents_per_gram = 3; + + let package = Package::new(sender_country, recipient_country, 1500); + + assert_eq!(package.get_fees(cents_per_gram), 4500); + assert_eq!(package.get_fees(cents_per_gram * 2), 9000); + } +} From a2dfbd86da3271eb07b57a89a359ce2efdcc3544 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 23:00:38 +0200 Subject: [PATCH 0940/1432] enums1 solution --- exercises/08_enums/enums1.rs | 2 +- solutions/08_enums/enums1.rs | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index d63de83f5a..99f70874c6 100644 --- a/exercises/08_enums/enums1.rs +++ b/exercises/08_enums/enums1.rs @@ -1,6 +1,6 @@ #[derive(Debug)] enum Message { - // TODO: define a few types of messages as used below + // TODO: Define a few types of messages as used below. } fn main() { diff --git a/solutions/08_enums/enums1.rs b/solutions/08_enums/enums1.rs index 4e18198923..9724883490 100644 --- a/solutions/08_enums/enums1.rs +++ b/solutions/08_enums/enums1.rs @@ -1 +1,14 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +#[derive(Debug)] +enum Message { + Quit, + Echo, + Move, + ChangeColor, +} + +fn main() { + println!("{:?}", Message::Quit); + println!("{:?}", Message::Echo); + println!("{:?}", Message::Move); + println!("{:?}", Message::ChangeColor); +} From 020711fa97e3be57f9e0098d6b9a329deec5a754 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 23:05:40 +0200 Subject: [PATCH 0941/1432] enums3 solution --- exercises/08_enums/enums2.rs | 2 +- rustlings-macros/info.toml | 2 +- solutions/08_enums/enums2.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index f3b803ff02..14aa29ada2 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -1,6 +1,6 @@ #[derive(Debug)] enum Message { - // TODO: define the different variants used below + // TODO: Define the different variants used below. } impl Message { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 7535b682df..a99210450a 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -451,7 +451,7 @@ dir = "08_enums" test = false hint = """ You can create enumerations that have different variants with different types -such as no data, anonymous structs, a single string, tuples, ...etc""" +such as no data, anonymous structs, a single string, tuples, etc.""" [[exercises]] name = "enums3" diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs index 4e18198923..13175dd3f9 100644 --- a/solutions/08_enums/enums2.rs +++ b/solutions/08_enums/enums2.rs @@ -1 +1,26 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +#[derive(Debug)] +enum Message { + Move { x: i64, y: i64 }, + Echo(String), + ChangeColor(u8, u8, u8), + Quit, +} + +impl Message { + fn call(&self) { + println!("{:?}", self); + } +} + +fn main() { + let messages = [ + Message::Move { x: 10, y: 30 }, + Message::Echo(String::from("hello world")), + Message::ChangeColor(200, 255, 255), + Message::Quit, + ]; + + for message in &messages { + message.call(); + } +} From 2901d856627889e5a52dcf9f97d1c77032081c08 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Jun 2024 23:18:25 +0200 Subject: [PATCH 0942/1432] enums3 solution --- exercises/08_enums/enums3.rs | 19 +++++---- rustlings-macros/info.toml | 6 +-- solutions/08_enums/enums3.rs | 76 +++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index edac3dfba1..7dd217152a 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -1,7 +1,5 @@ -// Address all the TODOs to make the tests pass! - enum Message { - // TODO: implement the message variant types based on their usage below + // TODO: Implement the message variant types based on their usage below. } struct Point { @@ -26,17 +24,17 @@ impl State { } fn echo(&mut self, s: String) { - self.message = s + self.message = s; } - fn move_position(&mut self, p: Point) { - self.position = p; + fn move_position(&mut self, point: Point) { + self.position = point; } fn process(&mut self, message: Message) { - // TODO: create a match expression to process the different message variants + // TODO: Create a match expression to process the different message variants. // Remember: When passing a tuple as a function argument, you'll need extra parentheses: - // fn function((t, u, p, l, e)) + // e.g. `foo((t, u, p, l, e))` } } @@ -54,8 +52,9 @@ mod tests { quit: false, position: Point { x: 0, y: 0 }, color: (0, 0, 0), - message: "hello world".to_string(), + message: String::from("hello world"), }; + state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Echo(String::from("Hello world!"))); state.process(Message::Move(Point { x: 10, y: 15 })); @@ -64,7 +63,7 @@ mod tests { assert_eq!(state.color, (255, 0, 255)); assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); - assert_eq!(state.quit, true); + assert!(state.quit); assert_eq!(state.message, "Hello world!"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index a99210450a..46a2c4b19f 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -457,12 +457,12 @@ such as no data, anonymous structs, a single string, tuples, etc.""" name = "enums3" dir = "08_enums" hint = """ -As a first step, you can define enums to compile this code without errors. +As a first step, define enums to compile the code without errors. -And then create a match expression in `process()`. +Then, create a match expression in `process()`. Note that you need to deconstruct some message variants in the match expression -to get value in the variant.""" +to get the variant's values.""" # STRINGS diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs index 4e18198923..8baa25c1b3 100644 --- a/solutions/08_enums/enums3.rs +++ b/solutions/08_enums/enums3.rs @@ -1 +1,75 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +enum Message { + ChangeColor(u8, u8, u8), + Echo(String), + Move(Point), + Quit, +} + +struct Point { + x: u8, + y: u8, +} + +struct State { + color: (u8, u8, u8), + position: Point, + quit: bool, + message: String, +} + +impl State { + fn change_color(&mut self, color: (u8, u8, u8)) { + self.color = color; + } + + fn quit(&mut self) { + self.quit = true; + } + + fn echo(&mut self, s: String) { + self.message = s; + } + + fn move_position(&mut self, point: Point) { + self.position = point; + } + + fn process(&mut self, message: Message) { + match message { + Message::ChangeColor(r, g, b) => self.change_color((r, g, b)), + Message::Echo(s) => self.echo(s), + Message::Move(point) => self.move_position(point), + Message::Quit => self.quit(), + } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_match_message_call() { + let mut state = State { + quit: false, + position: Point { x: 0, y: 0 }, + color: (0, 0, 0), + message: String::from("hello world"), + }; + + state.process(Message::ChangeColor(255, 0, 255)); + state.process(Message::Echo(String::from("Hello world!"))); + state.process(Message::Move(Point { x: 10, y: 15 })); + state.process(Message::Quit); + + assert_eq!(state.color, (255, 0, 255)); + assert_eq!(state.position.x, 10); + assert_eq!(state.position.y, 15); + assert!(state.quit); + assert_eq!(state.message, "Hello world!"); + } +} From bd63ece47cbb6bde9e2fe53db543a8d22a246f5e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 12:05:28 +0200 Subject: [PATCH 0943/1432] string1 solution --- exercises/09_strings/strings1.rs | 11 +++++------ solutions/09_strings/strings1.rs | 10 +++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/exercises/09_strings/strings1.rs b/exercises/09_strings/strings1.rs index de762ebfd6..6abdbb48f0 100644 --- a/exercises/09_strings/strings1.rs +++ b/exercises/09_strings/strings1.rs @@ -1,10 +1,9 @@ -// Make me compile without changing the function signature! +// TODO: Fix the compiler error without changing the function signature. +fn current_favorite_color() -> String { + "blue" +} fn main() { let answer = current_favorite_color(); - println!("My current favorite color is {}", answer); -} - -fn current_favorite_color() -> String { - "blue" + println!("My current favorite color is {answer}"); } diff --git a/solutions/09_strings/strings1.rs b/solutions/09_strings/strings1.rs index 4e18198923..f7ba8114ae 100644 --- a/solutions/09_strings/strings1.rs +++ b/solutions/09_strings/strings1.rs @@ -1 +1,9 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn current_favorite_color() -> String { + // Equivalent to `String::from("blue")` + "blue".to_string() +} + +fn main() { + let answer = current_favorite_color(); + println!("My current favorite color is {answer}"); +} From f574905b8e7d3b9320b2cb3a4c18e2039c9a771f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 12:14:04 +0200 Subject: [PATCH 0944/1432] strings2 solution --- exercises/09_strings/strings2.rs | 12 ++++++------ rustlings-macros/info.toml | 2 +- solutions/09_strings/strings2.rs | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/exercises/09_strings/strings2.rs b/exercises/09_strings/strings2.rs index 4768278171..93d9cb6b7f 100644 --- a/exercises/09_strings/strings2.rs +++ b/exercises/09_strings/strings2.rs @@ -1,14 +1,14 @@ -// Make me compile without changing the function signature! +// TODO: Fix the compiler error in the `main` function without changing this function. +fn is_a_color_word(attempt: &str) -> bool { + attempt == "green" || attempt == "blue" || attempt == "red" +} fn main() { - let word = String::from("green"); // Try not changing this line :) + let word = String::from("green"); // Don't change this line. + if is_a_color_word(word) { println!("That is a color word I know!"); } else { println!("That is not a color word I know."); } } - -fn is_a_color_word(attempt: &str) -> bool { - attempt == "green" || attempt == "blue" || attempt == "red" -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 46a2c4b19f..82206fc75d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -486,7 +486,7 @@ dir = "09_strings" test = false hint = """ Yes, it would be really easy to fix this by just changing the value bound to -`word` to be a string slice instead of a `String`, wouldn't it?? There is a way +`word` to be a string slice instead of a `String`, wouldn't it? There is a way to add one character to the `if` statement, though, that will coerce the `String` into a string slice. diff --git a/solutions/09_strings/strings2.rs b/solutions/09_strings/strings2.rs index 4e18198923..7de311fff9 100644 --- a/solutions/09_strings/strings2.rs +++ b/solutions/09_strings/strings2.rs @@ -1 +1,15 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn is_a_color_word(attempt: &str) -> bool { + attempt == "green" || attempt == "blue" || attempt == "red" +} + +fn main() { + let word = String::from("green"); + + if is_a_color_word(&word) { + // ^ added to have `&String` which is automatically + // coerced to `&str` by the compiler. + println!("That is a color word I know!"); + } else { + println!("That is not a color word I know."); + } +} From 613ec23f84d49078ed2e63c6111b7cf30ee764d6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 12:22:24 +0200 Subject: [PATCH 0945/1432] strings 3 solution --- exercises/09_strings/strings3.rs | 21 ++++++++------ rustlings-macros/info.toml | 3 +- solutions/09_strings/strings3.rs | 49 +++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index f83a5310db..39fce18c39 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -1,16 +1,13 @@ -fn trim_me(input: &str) -> String { - // TODO: Remove whitespace from both ends of a string! - ??? +fn trim_me(input: &str) -> &str { + // TODO: Remove whitespace from both ends of a string. } fn compose_me(input: &str) -> String { - // TODO: Add " world!" to the string! There are multiple ways to do this! - ??? + // TODO: Add " world!" to the string! There are multiple ways to do this. } fn replace_me(input: &str) -> String { - // TODO: Replace "cars" in the string with "balloons"! - ??? + // TODO: Replace "cars" in the string with "balloons". } fn main() { @@ -36,7 +33,13 @@ mod tests { #[test] fn replace_a_string() { - assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool"); - assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons"); + assert_eq!( + replace_me("I think cars are cool"), + "I think balloons are cool", + ); + assert_eq!( + replace_me("I love to look at cars"), + "I love to look at balloons", + ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 82206fc75d..618fc9187f 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -499,7 +499,8 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion name = "strings3" dir = "09_strings" hint = """ -There's tons of useful standard library functions for strings. Let's try and use some of them: +There are many useful standard library functions for strings. Let's try and use +some of them: https://doc.rust-lang.org/std/string/struct.String.html#method.trim For the `compose_me` method: You can either use the `format!` macro, or convert diff --git a/solutions/09_strings/strings3.rs b/solutions/09_strings/strings3.rs index 4e18198923..a478e62ad0 100644 --- a/solutions/09_strings/strings3.rs +++ b/solutions/09_strings/strings3.rs @@ -1 +1,48 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn trim_me(input: &str) -> &str { + input.trim() +} + +fn compose_me(input: &str) -> String { + // The macro `format!` has the same syntax as `println!`, but it returns a + // string instead of printing it to the terminal. + // Equivalent to `input.to_string() + " world!"` + format!("{input} world!") +} + +fn replace_me(input: &str) -> String { + input.replace("cars", "balloons") +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trim_a_string() { + assert_eq!(trim_me("Hello! "), "Hello!"); + assert_eq!(trim_me(" What's up!"), "What's up!"); + assert_eq!(trim_me(" Hola! "), "Hola!"); + } + + #[test] + fn compose_a_string() { + assert_eq!(compose_me("Hello"), "Hello world!"); + assert_eq!(compose_me("Goodbye"), "Goodbye world!"); + } + + #[test] + fn replace_a_string() { + assert_eq!( + replace_me("I think cars are cool"), + "I think balloons are cool", + ); + assert_eq!( + replace_me("I love to look at cars"), + "I love to look at balloons", + ); + } +} From 879f0cd5c69b8b0bf93da036d31334f9757bf6a3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 12:51:21 +0200 Subject: [PATCH 0946/1432] strings4 solution --- exercises/09_strings/strings4.rs | 44 ++++++++++++++++++++------------ rustlings-macros/info.toml | 10 +++++++- solutions/09_strings/strings4.rs | 39 +++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index 1f3d88b789..9d9eb480c0 100644 --- a/exercises/09_strings/strings4.rs +++ b/exercises/09_strings/strings4.rs @@ -1,24 +1,36 @@ -// 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! +// Calls of this function should be replaced with calls of `string_slice` or `string`. +fn placeholder() {} fn string_slice(arg: &str) { - println!("{}", arg); + println!("{arg}"); } fn string(arg: String) { - println!("{}", arg); + println!("{arg}"); } +// TODO: Here are a bunch of values - some are `String`, some are `&str`. +// Your task is to replace `placeholder(…)` with either `string_slice(…)` +// or `string(…)` depending on what you think each value is. fn main() { - ???("blue"); - ???("red".to_string()); - ???(String::from("hi")); - ???("rust is fun!".to_owned()); - ???("nice weather".into()); - ???(format!("Interpolation {}", "Station")); - ???(&String::from("abc")[0..1]); - ???(" hello there ".trim()); - ???("Happy Monday!".to_string().replace("Mon", "Tues")); - ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); + placeholder("blue"); + + placeholder("red".to_string()); + + placeholder(String::from("hi")); + + placeholder("rust is fun!".to_owned()); + + placeholder("nice weather".into()); + + placeholder(format!("Interpolation {}", "Station")); + + // WARNING: This is byte indexing, not character indexing. + // Character indexing can be done using `s.chars().nth(INDEX)`. + placeholder(&String::from("abc")[0..1]); + + placeholder(" hello there ".trim()); + + placeholder("Happy Monday!".replace("Mon", "Tues")); + + placeholder("mY sHiFt KeY iS sTiCkY".to_lowercase()); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 618fc9187f..7607650bdc 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -510,7 +510,15 @@ the string slice into an owned string, which you can then freely extend.""" name = "strings4" dir = "09_strings" test = false -hint = "No hints this time ;)" +hint = """ +Replace `placeholder` with either `string` or `string_slice` in the `main` function. + +Example: +`placeholder("blue");` +should become +`string_slice("blue");` +because "blue" is `&str`, not `String`. +""" # MODULES diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs index 4e18198923..9dc6917e70 100644 --- a/solutions/09_strings/strings4.rs +++ b/solutions/09_strings/strings4.rs @@ -1 +1,38 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn string_slice(arg: &str) { + println!("{arg}"); +} +fn string(arg: String) { + println!("{arg}"); +} + +fn main() { + string_slice("blue"); + + string("red".to_string()); + + string(String::from("hi")); + + string("rust is fun!".to_owned()); + + // Here, both answers work. + // `.into()` converts a type into an expected type. + // If it is called where `String` is expected, it will convert `&str` to `String`. + // But if is called where `&str` is expected, then `&str` is kept `&str` since no + // conversion is needed. + string("nice weather".into()); + string_slice("nice weather".into()); + // ^^^^^^^ the compiler recommends removing the `.into()` + // call because it is a useless conversion. + + string(format!("Interpolation {}", "Station")); + + // WARNING: This is byte indexing, not character indexing. + // Character indexing can be done using `s.chars().nth(INDEX)`. + string_slice(&String::from("abc")[0..1]); + + string_slice(" hello there ".trim()); + + string("Happy Monday!".replace("Mon", "Tues")); + + string("mY sHiFt KeY iS sTiCkY".to_lowercase()); +} From ecbe9b7324364e94f7c6b4a4dd279fb90f5a938e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 13:12:39 +0200 Subject: [PATCH 0947/1432] modules1 solution --- exercises/10_modules/modules1.rs | 1 + rustlings-macros/info.toml | 5 ++--- solutions/10_modules/modules1.rs | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/exercises/10_modules/modules1.rs b/exercises/10_modules/modules1.rs index 931a3e26cb..d97ab23a53 100644 --- a/exercises/10_modules/modules1.rs +++ b/exercises/10_modules/modules1.rs @@ -1,3 +1,4 @@ +// TODO: Fix the compiler error about calling a private function. mod sausage_factory { // Don't let anybody outside of this module see this! fn get_secret_recipe() -> String { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 7607650bdc..97d7e07db7 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -527,9 +527,8 @@ name = "modules1" dir = "10_modules" test = false hint = """ -Everything is private in Rust by default-- but there's a keyword we can use -to make something public! The compiler error should point to the thing that -needs to be public.""" +Everything is private in Rust by default. But there's a keyword we can use +to make something public!""" [[exercises]] name = "modules2" diff --git a/solutions/10_modules/modules1.rs b/solutions/10_modules/modules1.rs index 4e18198923..873b4127e6 100644 --- a/solutions/10_modules/modules1.rs +++ b/solutions/10_modules/modules1.rs @@ -1 +1,15 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +mod sausage_factory { + fn get_secret_recipe() -> String { + String::from("Ginger") + } + + // Added `pub` before `fn` to make the function accessible outside the module. + pub fn make_sausage() { + get_secret_recipe(); + println!("sausage!"); + } +} + +fn main() { + sausage_factory::make_sausage(); +} From 98cd00de6378550985d819ac8cd1227c8a10818e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 13:24:06 +0200 Subject: [PATCH 0948/1432] modules2 solution --- exercises/10_modules/modules2.rs | 19 +++++++++---------- rustlings-macros/info.toml | 11 ++++++----- solutions/10_modules/modules2.rs | 24 +++++++++++++++++++++++- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 5f8b0d518f..24dce41fd7 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -1,20 +1,19 @@ // 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. +// the `use` and `as` keywords. mod delicious_snacks { - // TODO: Fix these use statements - use self::fruits::PEAR as ??? - use self::veggies::CUCUMBER as ??? + // TODO: Add the follwing two `use` statements after fixing them. + // use self::fruits::PEAR as ???; + // use self::veggies::CUCUMBER as ???; mod fruits { - pub const PEAR: &'static str = "Pear"; - pub const APPLE: &'static str = "Apple"; + pub const PEAR: &str = "Pear"; + pub const APPLE: &str = "Apple"; } mod veggies { - pub const CUCUMBER: &'static str = "Cucumber"; - pub const CARROT: &'static str = "Carrot"; + pub const CUCUMBER: &str = "Cucumber"; + pub const CARROT: &str = "Carrot"; } } @@ -22,6 +21,6 @@ fn main() { println!( "favorite snacks: {} and {}", delicious_snacks::fruit, - delicious_snacks::veggie + delicious_snacks::veggie, ); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 97d7e07db7..ba414e3486 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -535,12 +535,13 @@ name = "modules2" dir = "10_modules" test = false hint = """ -The delicious_snacks module is trying to present an external interface that is -different than its internal structure (the `fruits` and `veggies` modules and -associated constants). Complete the `use` statements to fit the uses in main and -find the one keyword missing for both constants. +The `delicious_snacks` module is trying to present an external interface that +is different than its internal structure (the `fruits` and `veggies` modules +and associated constants). Complete the `use` statements to fit the uses in +`main` and find the one keyword missing for both constants. -Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" +Learn more in The Book: +https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use""" [[exercises]] name = "modules3" diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs index 4e18198923..55c316d706 100644 --- a/solutions/10_modules/modules2.rs +++ b/solutions/10_modules/modules2.rs @@ -1 +1,23 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +mod delicious_snacks { + // Added `pub` and used the expected alias after `as`. + pub use self::fruits::PEAR as fruit; + pub use self::veggies::CUCUMBER as veggie; + + mod fruits { + pub const PEAR: &str = "Pear"; + pub const APPLE: &str = "Apple"; + } + + mod veggies { + pub const CUCUMBER: &str = "Cucumber"; + pub const CARROT: &str = "Carrot"; + } +} + +fn main() { + println!( + "favorite snacks: {} and {}", + delicious_snacks::fruit, + delicious_snacks::veggie, + ); +} From 3d540ed946ee9fd522ba9ec26f68055f5c498317 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 22 Jun 2024 13:35:54 +0200 Subject: [PATCH 0949/1432] modules3 solution --- exercises/10_modules/modules3.rs | 11 +++++------ rustlings-macros/info.toml | 2 +- solutions/10_modules/modules3.rs | 9 ++++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/exercises/10_modules/modules3.rs b/exercises/10_modules/modules3.rs index eff24a9c5f..691608d275 100644 --- a/exercises/10_modules/modules3.rs +++ b/exercises/10_modules/modules3.rs @@ -1,10 +1,9 @@ -// 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! +// You can use the `use` keyword to bring module paths from modules from +// anywhere and especially from the standard library into your scope. -// TODO: Complete this use statement -use ??? +// TODO: Bring `SystemTime` and `UNIX_EPOCH` from the `std::time` module into +// your scope. Bonus style points if you can do it with one line! +// use ???; fn main() { match SystemTime::now().duration_since(UNIX_EPOCH) { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index ba414e3486..58c0cdd508 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -550,7 +550,7 @@ test = false hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a `use` statement for these two to bring them into scope. You can use nested -paths or the glob operator to bring these two in using only one line.""" +paths to bring these two in using only one line.""" # HASHMAPS diff --git a/solutions/10_modules/modules3.rs b/solutions/10_modules/modules3.rs index 4e18198923..99ff5a77ff 100644 --- a/solutions/10_modules/modules3.rs +++ b/solutions/10_modules/modules3.rs @@ -1 +1,8 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +use std::time::{SystemTime, UNIX_EPOCH}; + +fn main() { + match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), + Err(_) => panic!("SystemTime before UNIX EPOCH!"), + } +} From 5baa503bfc27fc691dbc292b46d37d25c17cffab Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 24 Jun 2024 13:20:50 +0200 Subject: [PATCH 0950/1432] hashmaps1 solution --- exercises/11_hashmaps/hashmaps1.rs | 11 ++++---- rustlings-macros/info.toml | 8 ++---- solutions/11_hashmaps/hashmaps1.rs | 43 +++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index e646ed71be..0df7000081 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -1,20 +1,19 @@ // 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 +// particular fruit is in the basket. You have to put at least 3 different // types of fruits (e.g apple, banana, mango) in the basket and the total count -// of all the fruits should be at least five. -// -// Make me compile and pass the tests! +// of all the fruits should be at least 5. use std::collections::HashMap; fn fruit_basket() -> HashMap { - let mut basket = // TODO: declare your hash map here. + // TODO: Declare the hash map. + // let mut basket = // Two bananas are already given for you :) basket.insert(String::from("banana"), 2); - // TODO: Put more fruits in your basket here. + // TODO: Put more fruits in your basket. basket } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 58c0cdd508..cf70d4d4f7 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -558,12 +558,8 @@ paths to bring these two in using only one line.""" name = "hashmaps1" dir = "11_hashmaps" hint = """ -Hint 1: Take a look at the return type of the function to figure out - the type for the `basket`. - -Hint 2: Number of fruits should be at least 5. And you have to put - at least three different types of fruits. -""" +The number of fruits should be at least 5 and you have to put at least 3 +different types of fruits.""" [[exercises]] name = "hashmaps2" diff --git a/solutions/11_hashmaps/hashmaps1.rs b/solutions/11_hashmaps/hashmaps1.rs index 4e18198923..3a787c4361 100644 --- a/solutions/11_hashmaps/hashmaps1.rs +++ b/solutions/11_hashmaps/hashmaps1.rs @@ -1 +1,42 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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 3 different +// types of fruits (e.g apple, banana, mango) in the basket and the total count +// of all the fruits should be at least 5. + +use std::collections::HashMap; + +fn fruit_basket() -> HashMap { + // Declare the hash map. + let mut basket = HashMap::new(); + + // Two bananas are already given for you :) + basket.insert(String::from("banana"), 2); + + // Put more fruits in your basket. + basket.insert(String::from("apple"), 3); + basket.insert(String::from("mango"), 1); + + basket +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn at_least_three_types_of_fruits() { + let basket = fruit_basket(); + assert!(basket.len() >= 3); + } + + #[test] + fn at_least_five_fruits() { + let basket = fruit_basket(); + assert!(basket.values().sum::() >= 5); + } +} From fbc226a51043f7c9be4c414292d37d3ce97038fe Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 24 Jun 2024 16:50:03 +0200 Subject: [PATCH 0951/1432] hashmaps2 solution --- exercises/11_hashmaps/hashmaps2.rs | 23 +++---- rustlings-macros/info.toml | 5 +- solutions/11_hashmaps/hashmaps2.rs | 96 +++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 17 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index 05b7a87029..b3691b68f2 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -6,8 +6,6 @@ // must add fruit to the basket so that there is at least one of each kind and // more than 11 in total - we have a lot of mouths to feed. You are not allowed // to insert any more of these fruits! -// -// Make me pass the tests! use std::collections::HashMap; @@ -21,7 +19,7 @@ enum Fruit { } fn fruit_basket(basket: &mut HashMap) { - let fruit_kinds = vec![ + let fruit_kinds = [ Fruit::Apple, Fruit::Banana, Fruit::Mango, @@ -46,12 +44,8 @@ mod tests { // Don't modify this function! fn get_fruit_basket() -> HashMap { - let mut basket = HashMap::::new(); - basket.insert(Fruit::Apple, 4); - basket.insert(Fruit::Mango, 2); - basket.insert(Fruit::Lychee, 5); - - basket + let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)]; + HashMap::from_iter(content) } #[test] @@ -81,7 +75,7 @@ mod tests { #[test] fn all_fruit_types_in_basket() { - let fruit_kinds = vec![ + let fruit_kinds = [ Fruit::Apple, Fruit::Banana, Fruit::Mango, @@ -91,11 +85,12 @@ mod tests { let mut basket = get_fruit_basket(); fruit_basket(&mut basket); + for fruit_kind in fruit_kinds { - let amount = basket - .get(&fruit_kind) - .expect(format!("Fruit kind {:?} was not found in basket", fruit_kind).as_str()); - assert_ne!(amount, &0); + let Some(amount) = basket.get(&fruit_kind) else { + panic!("Fruit kind {fruit_kind:?} was not found in basket"); + }; + assert!(*amount > 0); } } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index cf70d4d4f7..0da573bee1 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -566,8 +566,9 @@ name = "hashmaps2" dir = "11_hashmaps" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value -""" + +Learn more in The Book: +https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value""" [[exercises]] name = "hashmaps3" diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs index 4e18198923..a5e6ef9b24 100644 --- a/solutions/11_hashmaps/hashmaps2.rs +++ b/solutions/11_hashmaps/hashmaps2.rs @@ -1 +1,95 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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 +// many of that particular fruit we have collected. Three types of fruits - +// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You +// must add fruit to the basket so that there is at least one of each kind and +// more than 11 in total - we have a lot of mouths to feed. You are not allowed +// to insert any more of these fruits! + +use std::collections::HashMap; + +#[derive(Hash, PartialEq, Eq, Debug)] +enum Fruit { + Apple, + Banana, + Mango, + Lychee, + Pineapple, +} + +fn fruit_basket(basket: &mut HashMap) { + let fruit_kinds = [ + Fruit::Apple, + Fruit::Banana, + Fruit::Mango, + Fruit::Lychee, + Fruit::Pineapple, + ]; + + for fruit in fruit_kinds { + // If fruit doesn't exist, insert it with some value. + basket.entry(fruit).or_insert(5); + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + // Don't modify this function! + fn get_fruit_basket() -> HashMap { + let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)]; + HashMap::from_iter(content) + } + + #[test] + fn test_given_fruits_are_not_modified() { + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4); + assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2); + assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5); + } + + #[test] + fn at_least_five_types_of_fruits() { + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + let count_fruit_kinds = basket.len(); + assert!(count_fruit_kinds >= 5); + } + + #[test] + fn greater_than_eleven_fruits() { + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + let count = basket.values().sum::(); + assert!(count > 11); + } + + #[test] + fn all_fruit_types_in_basket() { + let fruit_kinds = [ + Fruit::Apple, + Fruit::Banana, + Fruit::Mango, + Fruit::Lychee, + Fruit::Pineapple, + ]; + + let mut basket = get_fruit_basket(); + fruit_basket(&mut basket); + + for fruit_kind in fruit_kinds { + let Some(amount) = basket.get(&fruit_kind) else { + panic!("Fruit kind {fruit_kind:?} was not found in basket"); + }; + assert!(*amount > 0); + } + } +} From f1bd4447924e797e8fb0012f6bc47a507438f3f5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 01:52:33 +0200 Subject: [PATCH 0952/1432] hashmaps3 solution --- exercises/11_hashmaps/hashmaps3.rs | 71 ++++++++++++------------- rustlings-macros/info.toml | 12 +++-- solutions/11_hashmaps/hashmaps3.rs | 84 +++++++++++++++++++++++++++++- 3 files changed, 122 insertions(+), 45 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 070c37095c..9f8fdd7858 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -1,39 +1,38 @@ // A list of scores (one per line) of a soccer match is given. Each line is of -// the form : ",,," -// Example: England,France,4,2 (England scored 4 goals, France 2). +// the form ",,," +// 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, -// complete it to pass the test. -// -// Make me pass the tests! +// conceded. use std::collections::HashMap; // A structure to store the goal details of a team. +#[derive(Default)] struct Team { goals_scored: u8, goals_conceded: u8, } -fn build_scores_table(results: String) -> HashMap { +fn build_scores_table(results: &str) -> HashMap<&str, Team> { // The name of the team is the key and its associated struct is the value. - let mut scores: HashMap = HashMap::new(); + let mut scores = HashMap::new(); + + for line in results.lines() { + let mut split_iterator = line.split(','); + // NOTE: We use `unwrap` because we didn't deal with error handling yet. + let team_1_name = split_iterator.next().unwrap(); + let team_2_name = split_iterator.next().unwrap(); + let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap(); + let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); - for r in results.lines() { - let v: Vec<&str> = r.split(',').collect(); - let team_1_name = v[0].to_string(); - let team_1_score: u8 = v[2].parse().unwrap(); - let team_2_name = v[1].to_string(); - let team_2_score: u8 = v[3].parse().unwrap(); - // TODO: Populate the scores table with details extracted from the - // current line. Keep in mind that goals scored by team_1 - // will be the number of goals conceded by team_2, and similarly - // goals scored by team_2 will be the number of goals conceded by - // team_1. + // TODO: Populate the scores table with the extracted details. + // Keep in mind that goals scored by team 1 will be the number of goals + // conceded by team 2. Similarly, goals scored by team 2 will be the + // number of goals conceded by team 1. } + scores } @@ -45,40 +44,34 @@ fn main() { mod tests { use super::*; - fn get_results() -> String { - let results = "".to_string() - + "England,France,4,2\n" - + "France,Italy,3,1\n" - + "Poland,Spain,2,0\n" - + "Germany,England,2,1\n"; - results - } + const RESULTS: &str = "England,France,4,2 +France,Italy,3,1 +Poland,Spain,2,0 +Germany,England,2,1 +England,Spain,1,0"; #[test] fn build_scores() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); - let mut keys: Vec<&String> = scores.keys().collect(); - keys.sort(); - assert_eq!( - keys, - vec!["England", "France", "Germany", "Italy", "Poland", "Spain"] - ); + assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"] + .into_iter() + .all(|team_name| scores.contains_key(team_name))); } #[test] fn validate_team_score_1() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); let team = scores.get("England").unwrap(); - assert_eq!(team.goals_scored, 5); + assert_eq!(team.goals_scored, 6); assert_eq!(team.goals_conceded, 4); } #[test] fn validate_team_score_2() { - let scores = build_scores_table(get_results()); + let scores = build_scores_table(RESULTS); let team = scores.get("Spain").unwrap(); assert_eq!(team.goals_scored, 0); - assert_eq!(team.goals_conceded, 2); + assert_eq!(team.goals_conceded, 3); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 0da573bee1..c5ce8479da 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -574,16 +574,18 @@ https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-va name = "hashmaps3" dir = "11_hashmaps" hint = """ -Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert - entries corresponding to each team in the scores table. +Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of + `HashMap` to insert the default value of `Team` if a team doesn't + exist in the table yet. -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +Learn more in The Book: +https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value. -Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value -""" +Learn more in The Book: +https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value""" # QUIZ 2 diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 4e18198923..f4059bbe87 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -1 +1,83 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// A list of scores (one per line) of a soccer match is given. Each line is of +// the form ",,," +// Example: "England,France,4,2" (England scored 4 goals, France 2). +// +// 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. + +use std::collections::HashMap; + +// A structure to store the goal details of a team. +#[derive(Default)] +struct Team { + goals_scored: u8, + goals_conceded: u8, +} + +fn build_scores_table(results: &str) -> HashMap<&str, Team> { + // The name of the team is the key and its associated struct is the value. + let mut scores = HashMap::new(); + + for line in results.lines() { + let mut split_iterator = line.split(','); + // NOTE: We use `unwrap` because we didn't deal with error handling yet. + let team_1_name = split_iterator.next().unwrap(); + let team_2_name = split_iterator.next().unwrap(); + let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap(); + let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); + + // Insert the default with zeros if a team doesn't exist yet. + let mut team_1 = scores.entry(team_1_name).or_insert_with(|| Team::default()); + // Update the values. + team_1.goals_scored += team_1_score; + team_1.goals_conceded += team_2_score; + + // Similarely for the second team. + let mut team_2 = scores.entry(team_2_name).or_insert_with(|| Team::default()); + team_2.goals_scored += team_2_score; + team_2.goals_conceded += team_1_score; + } + + scores +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + const RESULTS: &str = "England,France,4,2 +France,Italy,3,1 +Poland,Spain,2,0 +Germany,England,2,1 +England,Spain,1,0"; + + #[test] + fn build_scores() { + let scores = build_scores_table(RESULTS); + + assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"] + .into_iter() + .all(|team_name| scores.contains_key(team_name))); + } + + #[test] + fn validate_team_score_1() { + let scores = build_scores_table(RESULTS); + let team = scores.get("England").unwrap(); + assert_eq!(team.goals_scored, 6); + assert_eq!(team.goals_conceded, 4); + } + + #[test] + fn validate_team_score_2() { + let scores = build_scores_table(RESULTS); + let team = scores.get("Spain").unwrap(); + assert_eq!(team.goals_scored, 0); + assert_eq!(team.goals_conceded, 3); + } +} From 29bcb282dacead96df6e6cdbec9ac1ba8008d90f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 02:25:59 +0200 Subject: [PATCH 0953/1432] quiz2 solution --- exercises/quizzes/quiz2.rs | 45 ++++++++-------- solutions/quizzes/quiz2.rs | 108 ++++++++++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 23 deletions(-) diff --git a/exercises/quizzes/quiz2.rs b/exercises/quizzes/quiz2.rs index e01e3f1dcf..fd6bc773ec 100644 --- a/exercises/quizzes/quiz2.rs +++ b/exercises/quizzes/quiz2.rs @@ -11,10 +11,11 @@ // - Uppercase the string // - Trim the string // - Append "bar" to the string a specified amount of times +// // The exact form of this will be: -// - The input is going to be a Vector of a 2-length tuple, +// - 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. +// - The output element is going to be a vector of strings. enum Command { Uppercase, @@ -25,15 +26,8 @@ enum Command { mod my_module { use super::Command; - // TODO: Complete the function signature! - pub fn transformer(input: ???) -> ??? { - // TODO: Complete the output declaration! - let mut output: ??? = vec![]; - for (string, command) in input.iter() { - // TODO: Complete the function body. You can do it! - } - output - } + // TODO: Complete the function. + // pub fn transformer(input: ???) -> ??? { ??? } } fn main() { @@ -43,20 +37,27 @@ fn main() { #[cfg(test)] mod tests { // TODO: What do we need to import to have `transformer` in scope? - use ???; + // use ???; use super::Command; #[test] fn it_works() { - let output = transformer(vec![ - ("hello".into(), Command::Uppercase), - (" all roads lead to rome! ".into(), Command::Trim), - ("foo".into(), Command::Append(1)), - ("bar".into(), Command::Append(5)), - ]); - assert_eq!(output[0], "HELLO"); - assert_eq!(output[1], "all roads lead to rome!"); - assert_eq!(output[2], "foobar"); - assert_eq!(output[3], "barbarbarbarbarbar"); + let input = vec![ + ("hello".to_string(), Command::Uppercase), + (" all roads lead to rome! ".to_string(), Command::Trim), + ("foo".to_string(), Command::Append(1)), + ("bar".to_string(), Command::Append(5)), + ]; + let output = transformer(input); + + assert_eq!( + output, + [ + "HELLO", + "all roads lead to rome!", + "foobar", + "barbarbarbarbarbar", + ] + ); } } diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs index 4e18198923..0d2a51324b 100644 --- a/solutions/quizzes/quiz2.rs +++ b/solutions/quizzes/quiz2.rs @@ -1 +1,107 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// This is a quiz for the following sections: +// - Strings +// - Vecs +// - Move semantics +// - Modules +// - Enums +// +// Let's build a little machine in the form of a function. As input, we're going +// to give a list of strings and commands. These commands determine what action +// is going to be applied to the string. It can either be: +// - Uppercase the string +// - Trim the string +// - Append "bar" to the string a specified amount of times +// +// The exact form of this will be: +// - 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. + +enum Command { + Uppercase, + Trim, + Append(usize), +} + +mod my_module { + use super::Command; + + // The solution with a loop. Check out `transformer_iter` for a version + // with iterators. + pub fn transformer(input: Vec<(String, Command)>) -> Vec { + let mut output = Vec::new(); + + for (mut string, command) in input { + // Create the new string. + let new_string = match command { + Command::Uppercase => string.to_uppercase(), + Command::Trim => string.trim().to_string(), + Command::Append(n) => { + for _ in 0..n { + string += "bar"; + } + string + } + }; + + // Push the new string to the output vector. + output.push(new_string); + } + + output + } + + // Equivalent to `transform` but uses an iterator instead of a loop for + // comparison. Don't worry, we will practice iterators later ;) + pub fn transformer_iter(input: Vec<(String, Command)>) -> Vec { + input + .into_iter() + .map(|(mut string, command)| match command { + Command::Uppercase => string.to_uppercase(), + Command::Trim => string.trim().to_string(), + Command::Append(n) => { + for _ in 0..n { + string += "bar"; + } + string + } + }) + .collect() + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + // Import `transformer`. + use super::my_module::transformer; + + use super::my_module::transformer_iter; + use super::Command; + + #[test] + fn it_works() { + for transformer in [transformer, transformer_iter] { + let input = vec![ + ("hello".to_string(), Command::Uppercase), + (" all roads lead to rome! ".to_string(), Command::Trim), + ("foo".to_string(), Command::Append(1)), + ("bar".to_string(), Command::Append(5)), + ]; + let output = transformer(input); + + assert_eq!( + output, + [ + "HELLO", + "all roads lead to rome!", + "foobar", + "barbarbarbarbarbar", + ] + ); + } + } +} From 1694682aa4ee848033169449a3f64d2e163f5638 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 02:26:04 +0200 Subject: [PATCH 0954/1432] Fix typos --- exercises/07_structs/structs3.rs | 3 ++- exercises/10_modules/modules2.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 18a6cc98d9..93b57fefd7 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -25,7 +25,8 @@ impl Package { // TODO: Add the correct return type to the function signature. fn is_international(&self) { - // TODO: Read the tests that use this method to find out when a package is concidered international. + // TODO: Read the tests that use this method to find out when a package + // is considered international. } // TODO: Add the correct return type to the function signature. diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 24dce41fd7..782a70eace 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -2,7 +2,7 @@ // the `use` and `as` keywords. mod delicious_snacks { - // TODO: Add the follwing two `use` statements after fixing them. + // TODO: Add the following two `use` statements after fixing them. // use self::fruits::PEAR as ???; // use self::veggies::CUCUMBER as ???; From c31e15c4cf5085adcf544a33ac256364fc2bcfbf Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 12:59:10 +0200 Subject: [PATCH 0955/1432] options1 solution --- exercises/12_options/options1.rs | 30 ++++++++++++------------ rustlings-macros/info.toml | 2 +- solutions/12_options/options1.rs | 39 +++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index b7cf7b0b62..5009f8b61b 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -1,12 +1,9 @@ // 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 :( -fn maybe_icecream(time_of_day: u16) -> Option { - // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a - // value of 0. The Option output should gracefully handle cases where - // time_of_day > 23. - // TODO: Complete the function body - remember to return an Option! - ??? +// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, +// someone eats it all, so no icecream is left (value 0). Return `None` if +// `hour_of_day` is higher than 23. +fn maybe_icecream(hour_of_day: u16) -> Option { + // TODO: Complete the function body. } fn main() { @@ -17,6 +14,14 @@ fn main() { mod tests { use super::*; + #[test] + fn raw_value() { + // TODO: Fix this test. How do you get the value contained in the + // Option? + let icecreams = maybe_icecream(12); + assert_eq!(icecreams, 5); + } + #[test] fn check_icecream() { assert_eq!(maybe_icecream(0), Some(5)); @@ -24,14 +29,7 @@ mod tests { assert_eq!(maybe_icecream(18), Some(5)); assert_eq!(maybe_icecream(22), Some(0)); assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(24), None); assert_eq!(maybe_icecream(25), None); } - - #[test] - fn raw_value() { - // TODO: Fix this test. How do you get at the value contained in the - // Option? - let icecreams = maybe_icecream(12); - assert_eq!(icecreams, 5); - } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index c5ce8479da..3694b94fb5 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -603,7 +603,7 @@ hint = """ Options can have a `Some` value, with an inner value, or a `None` value, without an inner value. -There's multiple ways to get at the inner value, you can use `unwrap`, or +There are multiple ways to get at the inner value, you can use `unwrap`, or pattern match. Unwrapping is the easiest, but how do you do it safely so that it doesn't panic in your face later?""" diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs index 4e18198923..1ffbb04592 100644 --- a/solutions/12_options/options1.rs +++ b/solutions/12_options/options1.rs @@ -1 +1,38 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// This function returns how much icecream there is left in the fridge. +// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, +// someone eats it all, so no icecream is left (value 0). Return `None` if +// `hour_of_day` is higher than 23. +fn maybe_icecream(hour_of_day: u16) -> Option { + match hour_of_day { + 0..22 => Some(5), + 22..24 => Some(0), + _ => None, + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn raw_value() { + // Using `unwrap` is fine in a test. + let icecreams = maybe_icecream(12).unwrap(); + assert_eq!(icecreams, 5); + } + + #[test] + fn check_icecream() { + assert_eq!(maybe_icecream(0), Some(5)); + assert_eq!(maybe_icecream(9), Some(5)); + assert_eq!(maybe_icecream(18), Some(5)); + assert_eq!(maybe_icecream(22), Some(0)); + assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(24), None); + assert_eq!(maybe_icecream(25), None); + } +} From a91888e79e69e04e57c2049cdf940a70201e1d6e Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 14:35:05 +0200 Subject: [PATCH 0956/1432] option2 solution --- exercises/12_options/options2.rs | 10 ++++----- rustlings-macros/info.toml | 4 ++-- solutions/12_options/options2.rs | 38 +++++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index 01f84c5838..07c27c6ec2 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -9,7 +9,7 @@ mod tests { let target = "rustlings"; let optional_target = Some(target); - // TODO: Make this an if let statement whose value is "Some" type + // TODO: Make this an if-let statement whose value is `Some`. word = optional_target { assert_eq!(word, target); } @@ -20,15 +20,15 @@ mod tests { let range = 10; let mut optional_integers: Vec> = vec![None]; - for i in 1..(range + 1) { + for i in 1..=range { optional_integers.push(Some(i)); } let mut cursor = range; - // TODO: make this a while let statement - remember that vector.pop also - // adds another layer of Option. You can stack `Option`s into - // while let and if let. + // TODO: Make this a while-let statement. Remember that `Vec::pop()` + // adds another layer of `Option`. You can do nested pattern matching + // in if-let and while-let statements. integer = optional_integers.pop() { assert_eq!(integer, cursor); cursor -= 1; diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 3694b94fb5..6027b6b14d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -616,9 +616,9 @@ Check out: - https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html - https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html -Remember that `Option`s can be stacked in `if let` and `while let`. +Remember that `Option`s can be nested in if-let and while-let statements. -For example: `Some(Some(variable)) = variable2` +For example: `if let Some(Some(x)) = y` Also see `Option::flatten` """ diff --git a/solutions/12_options/options2.rs b/solutions/12_options/options2.rs index 4e18198923..0f24665ff5 100644 --- a/solutions/12_options/options2.rs +++ b/solutions/12_options/options2.rs @@ -1 +1,37 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn simple_option() { + let target = "rustlings"; + let optional_target = Some(target); + + // if-let + if let Some(word) = optional_target { + assert_eq!(word, target); + } + } + + #[test] + fn layered_option() { + let range = 10; + let mut optional_integers: Vec> = vec![None]; + + for i in 1..=range { + optional_integers.push(Some(i)); + } + + let mut cursor = range; + + // while-let with nested pattern matching + while let Some(Some(integer)) = optional_integers.pop() { + assert_eq!(integer, cursor); + cursor -= 1; + } + + assert_eq!(cursor, 0); + } +} From 25b5686dd2ab2e3d5a228a71e9631c50ea50fffe Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 14:47:57 +0200 Subject: [PATCH 0957/1432] options3 solution --- exercises/12_options/options3.rs | 13 ++++++++----- rustlings-macros/info.toml | 3 ++- solutions/12_options/options3.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 5b70a79268..4cedb51242 100644 --- a/exercises/12_options/options3.rs +++ b/exercises/12_options/options3.rs @@ -1,14 +1,17 @@ +#[derive(Debug)] struct Point { x: i32, y: i32, } fn main() { - let y: Option = Some(Point { x: 100, y: 200 }); + let optional_point = Some(Point { x: 100, y: 200 }); - match y { - Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y), - _ => panic!("no match!"), + // TODO: Fix the compiler error by adding something to this match statement. + match optional_point { + Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), + _ => panic!("No match!"), } - y; // Fix without deleting this line. + + println!("{optional_point:?}"); // Don't change this line. } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 6027b6b14d..5a47c85f18 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -631,7 +631,8 @@ hint = """ The compiler says a partial move happened in the `match` statement. How can this be avoided? The compiler shows the correction needed. -After making the correction as suggested by the compiler, do read: +After making the correction as suggested by the compiler, read the related docs +page: https://doc.rust-lang.org/std/keyword.ref.html""" # ERROR HANDLING diff --git a/solutions/12_options/options3.rs b/solutions/12_options/options3.rs index 4e18198923..0081eeb2c7 100644 --- a/solutions/12_options/options3.rs +++ b/solutions/12_options/options3.rs @@ -1 +1,26 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let optional_point = Some(Point { x: 100, y: 200 }); + + // Solution 1: Matching over the `Option` (not `&Option`) but without moving + // out of the `Some` variant. + match optional_point { + Some(ref p) => println!("Co-ordinates are {},{}", p.x, p.y), + // ^^^ added + _ => panic!("No match!"), + } + + // Solution 2: Matching over a reference (`&Option`) by added `&` before + // `optional_point`. + match &optional_point { + Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), + _ => panic!("No match!"), + } + + println!("{optional_point:?}"); +} From 097f3c74ea16bad95a659fc41a494f24e07656d1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:06:29 +0200 Subject: [PATCH 0958/1432] errors1 solution --- exercises/13_error_handling/errors1.rs | 30 ++++++++++----------- rustlings-macros/info.toml | 4 +-- solutions/13_error_handling/errors1.rs | 36 +++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index e3e0482310..6d9701b1e6 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -1,22 +1,22 @@ -// 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! - -fn main() { - // You can optionally experiment here. -} - +// TODO: 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 returning `None`. Thankfully, Rust has a similar +// construct to `Option` that can be used to express error conditions. Change +// the function signature and body to return `Result` instead +// of `Option`. fn generate_nametag_text(name: String) -> Option { if name.is_empty() { // Empty names aren't allowed. None } else { - Some(format!("Hi! My name is {}", name)) + Some(format!("Hi! My name is {name}")) } } +fn main() { + // You can optionally experiment here. +} + #[cfg(test)] mod tests { use super::*; @@ -24,17 +24,17 @@ mod tests { #[test] fn generates_nametag_text_for_a_nonempty_name() { assert_eq!( - generate_nametag_text("BeyoncΓ©".into()), - Ok("Hi! My name is BeyoncΓ©".into()) + generate_nametag_text("BeyoncΓ©".to_string()).as_deref(), + Ok("Hi! My name is BeyoncΓ©"), ); } #[test] fn explains_why_generating_nametag_text_fails() { assert_eq!( - generate_nametag_text("".into()), + generate_nametag_text(String::new()).as_deref(), // Don't change this line - Err("`name` was empty; it must be nonempty.".into()) + Err("`name` was empty; it must be nonempty."), ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5a47c85f18..3d8da58ffe 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -647,8 +647,8 @@ is that `generate_nametag_text` should return a `Result` instead of an `Option`. To make this change, you'll need to: - update the return type in the function signature to be a `Result` that could be the variants `Ok(String)` and `Err(String)` - - change the body of the function to return `Ok(stuff)` where it currently - returns `Some(stuff)` + - change the body of the function to return `Ok(…)` where it currently + returns `Some(…)` - change the body of the function to return `Err(error message)` where it currently returns `None`""" diff --git a/solutions/13_error_handling/errors1.rs b/solutions/13_error_handling/errors1.rs index 4e18198923..2a13bfdd69 100644 --- a/solutions/13_error_handling/errors1.rs +++ b/solutions/13_error_handling/errors1.rs @@ -1 +1,35 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn generate_nametag_text(name: String) -> Result { + // ^^^^^^ ^^^^^^ + if name.is_empty() { + // `Err(String)` instead of `None`. + Err("Empty names aren't allowed".to_string()) + } else { + // `Ok` instead of `Some`. + Ok(format!("Hi! My name is {name}")) + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn generates_nametag_text_for_a_nonempty_name() { + assert_eq!( + generate_nametag_text("BeyoncΓ©".to_string()).as_deref(), + Ok("Hi! My name is BeyoncΓ©"), + ); + } + + #[test] + fn explains_why_generating_nametag_text_fails() { + assert_eq!( + generate_nametag_text(String::new()).as_deref(), + Err("`name` was empty; it must be nonempty."), + ); + } +} From 2afe6b38d3fe8d851b0d37f85c0d058388603127 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:12:58 +0200 Subject: [PATCH 0959/1432] Fix tests --- exercises/13_error_handling/errors1.rs | 7 ++++--- solutions/13_error_handling/errors1.rs | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index 6d9701b1e6..ec7cb3cb66 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -32,9 +32,10 @@ mod tests { #[test] fn explains_why_generating_nametag_text_fails() { assert_eq!( - generate_nametag_text(String::new()).as_deref(), - // Don't change this line - Err("`name` was empty; it must be nonempty."), + generate_nametag_text(String::new()) + .as_ref() + .map_err(|e| e.as_str()), + Err("Empty names aren't allowed"), ); } } diff --git a/solutions/13_error_handling/errors1.rs b/solutions/13_error_handling/errors1.rs index 2a13bfdd69..f552ca7f6a 100644 --- a/solutions/13_error_handling/errors1.rs +++ b/solutions/13_error_handling/errors1.rs @@ -28,8 +28,10 @@ mod tests { #[test] fn explains_why_generating_nametag_text_fails() { assert_eq!( - generate_nametag_text(String::new()).as_deref(), - Err("`name` was empty; it must be nonempty."), + generate_nametag_text(String::new()) + .as_ref() + .map_err(|e| e.as_str()), + Err("Empty names aren't allowed"), ); } } From 050a23ce6763fedf0906cd1c04b76888aae12f7d Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:36:14 +0200 Subject: [PATCH 0960/1432] errors2 solution --- exercises/13_error_handling/errors2.rs | 19 +++++---- rustlings-macros/info.toml | 7 ++-- solutions/13_error_handling/errors2.rs | 58 +++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 345a0eef70..e50a92991d 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -2,16 +2,16 @@ // 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 // the `total_cost` function will calculate the total cost of the items. Since -// the player typed in the quantity, though, we get it as a string-- and they -// might have typed anything, not just numbers! +// the player typed in the quantity, we get it as a string. They might have +// typed anything, not just numbers! // // Right now, this function isn't handling the error case at all (and isn't -// handling the success case properly either). What we want to do is: if we call +// handling the success case properly either). What we want to do is: If we call // the `total_cost` function on a string that is not a number, that function -// will return a `ParseIntError`, and in that case, we want to immediately -// return that error from our function and not try to multiply and add. +// will return a `ParseIntError`. In that case, we want to immediately return +// that error from our function and not try to multiply and add. // -// There are at least two ways to implement this that are both correct-- but one +// There are at least two ways to implement this that are both correct. But one // is a lot shorter! use std::num::ParseIntError; @@ -19,6 +19,8 @@ use std::num::ParseIntError; fn total_cost(item_quantity: &str) -> Result { let processing_fee = 1; let cost_per_item = 5; + + // TODO: Handle the error case as described above. let qty = item_quantity.parse::(); Ok(qty * cost_per_item + processing_fee) @@ -31,6 +33,7 @@ fn main() { #[cfg(test)] mod tests { use super::*; + use std::num::IntErrorKind; #[test] fn item_quantity_is_a_valid_number() { @@ -40,8 +43,8 @@ mod tests { #[test] fn item_quantity_is_an_invalid_number() { assert_eq!( - total_cost("beep boop").unwrap_err().to_string(), - "invalid digit found in string" + total_cost("beep boop").unwrap_err().kind(), + &IntErrorKind::InvalidDigit, ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 3d8da58ffe..2a4a24ea36 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -660,12 +660,11 @@ One way to handle this is using a `match` statement on `item_quantity.parse::()` where the cases are `Ok(something)` and `Err(something)`. -This pattern is very common in Rust, though, so there's a `?` operator that +This pattern is very common in Rust, though, so there's the `?` operator that does pretty much what you would make that match statement do for you! -Take a look at this section of the 'Error Handling' chapter: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -and give it a try!""" +Take a look at this section of the "Error Handling" chapter: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator""" [[exercises]] name = "errors3" diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs index 4e18198923..de7c32b5d3 100644 --- a/solutions/13_error_handling/errors2.rs +++ b/solutions/13_error_handling/errors2.rs @@ -1 +1,57 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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 +// the `total_cost` function will calculate the total cost of the items. Since +// the player typed in the quantity, we get it as a string. They might have +// typed anything, not just numbers! +// +// Right now, this function isn't handling the error case at all (and isn't +// handling the success case properly either). What we want to do is: If we call +// the `total_cost` function on a string that is not a number, that function +// will return a `ParseIntError`. In that case, we want to immediately return +// that error from our function and not try to multiply and add. +// +// There are at least two ways to implement this that are both correct. But one +// is a lot shorter! + +use std::num::ParseIntError; + +fn total_cost(item_quantity: &str) -> Result { + let processing_fee = 1; + let cost_per_item = 5; + + // Added `?` to propagate the error. + let qty = item_quantity.parse::()?; + // ^ added + + // Equivalent to this verbose version: + let qty = match item_quantity.parse::() { + Ok(v) => v, + Err(e) => return Err(e), + }; + + Ok(qty * cost_per_item + processing_fee) +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + use std::num::IntErrorKind; + + #[test] + fn item_quantity_is_a_valid_number() { + assert_eq!(total_cost("34"), Ok(171)); + } + + #[test] + fn item_quantity_is_an_invalid_number() { + assert_eq!( + total_cost("beep boop").unwrap_err().kind(), + &IntErrorKind::InvalidDigit, + ); + } +} From c46d8bdf95c9a2025ee943feb208102a94b25ee6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:44:33 +0200 Subject: [PATCH 0961/1432] errors3 solution --- exercises/13_error_handling/errors3.rs | 21 +++++++++------- rustlings-macros/info.toml | 4 ++-- solutions/13_error_handling/errors3.rs | 33 +++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index 2ef84f981f..33a7b877ba 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -4,6 +4,17 @@ use std::num::ParseIntError; +// Don't change this function. +fn total_cost(item_quantity: &str) -> Result { + let processing_fee = 1; + let cost_per_item = 5; + let qty = item_quantity.parse::()?; + + Ok(qty * cost_per_item + processing_fee) +} + +// TODO: Fix the compiler error by changing the signature and body of the +// `main` function. fn main() { let mut tokens = 100; let pretend_user_input = "8"; @@ -14,14 +25,6 @@ fn main() { println!("You can't afford that many!"); } else { tokens -= cost; - println!("You now have {} tokens.", tokens); + println!("You now have {tokens} tokens."); } } - -fn total_cost(item_quantity: &str) -> Result { - let processing_fee = 1; - let cost_per_item = 5; - let qty = item_quantity.parse::()?; - - Ok(qty * cost_per_item + processing_fee) -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 2a4a24ea36..74cb79dda1 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -675,8 +675,8 @@ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your `main` function. -The unit (`()`) type is there because nothing is really needed in terms of -positive results.""" +The unit type `()` is there because nothing is really needed in terms of a +positive result.""" [[exercises]] name = "errors4" diff --git a/solutions/13_error_handling/errors3.rs b/solutions/13_error_handling/errors3.rs index 4e18198923..63f4aba182 100644 --- a/solutions/13_error_handling/errors3.rs +++ b/solutions/13_error_handling/errors3.rs @@ -1 +1,32 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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? + +use std::num::ParseIntError; + +// Don't change this function. +fn total_cost(item_quantity: &str) -> Result { + let processing_fee = 1; + let cost_per_item = 5; + let qty = item_quantity.parse::()?; + + Ok(qty * cost_per_item + processing_fee) +} + +fn main() -> Result<(), ParseIntError> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added + let mut tokens = 100; + let pretend_user_input = "8"; + + let cost = total_cost(pretend_user_input)?; + + if cost > tokens { + println!("You can't afford that many!"); + } else { + tokens -= cost; + println!("You now have {tokens} tokens."); + } + + // Added this line to return the `Ok` variant of the expected `Result`. + Ok(()) +} From 9b7a5c041e9856379154b109b2ee2f3e979d70f7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 15:54:18 +0200 Subject: [PATCH 0962/1432] errors4 solution --- exercises/13_error_handling/errors4.rs | 21 +++++++------ rustlings-macros/info.toml | 8 ++--- solutions/13_error_handling/errors4.rs | 43 +++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index 993d42a17b..ba01e54bf5 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -1,16 +1,16 @@ -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); - #[derive(PartialEq, Debug)] enum CreationError { Negative, Zero, } +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { - // Hmm... Why is this always returning an Ok value? - Ok(PositiveNonzeroInteger(value as u64)) + fn new(value: i64) -> Result { + // TODO: This function shouldn't always return an `Ok`. + Ok(Self(value as u64)) } } @@ -24,11 +24,14 @@ mod tests { #[test] fn test_creation() { - assert!(PositiveNonzeroInteger::new(10).is_ok()); assert_eq!( + PositiveNonzeroInteger::new(10), + Ok(PositiveNonzeroInteger(10)), + ); + assert_eq!( + PositiveNonzeroInteger::new(-10), Err(CreationError::Negative), - PositiveNonzeroInteger::new(-10) ); - assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); + assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 74cb79dda1..d39044c06a 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -683,11 +683,9 @@ name = "errors4" dir = "13_error_handling" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning -an `Ok` result. - -It should be doing some checking, returning an `Err` result if those checks -fail, and only returning an `Ok` result if those checks determine that -everything is... okay :)""" +an `Ok` result. But it should be doing some checking, returning an `Err` if +those checks fail, and only returning an `Ok` if those checks determine that +everything is… okay :)""" [[exercises]] name = "errors5" diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs index 4e18198923..c43f493b71 100644 --- a/solutions/13_error_handling/errors4.rs +++ b/solutions/13_error_handling/errors4.rs @@ -1 +1,42 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + if value == 0 { + Err(CreationError::Zero) + } else if value < 0 { + Err(CreationError::Negative) + } else { + Ok(Self(value as u64)) + } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creation() { + assert_eq!( + PositiveNonzeroInteger::new(10), + Ok(PositiveNonzeroInteger(10)), + ); + assert_eq!( + PositiveNonzeroInteger::new(-10), + Err(CreationError::Negative), + ); + assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero)); + } +} From 720b280bc1d8230821b4e6f2466eddb43245b8ff Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 16:59:13 +0200 Subject: [PATCH 0963/1432] Update deps --- Cargo.lock | 50 ++++++++++++++++++++++++++++++-------------------- Cargo.toml | 4 ++-- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 82c06f9bfe..f41ba86e41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,9 +113,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bstr" @@ -229,7 +229,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "libc", "mio", @@ -262,9 +262,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -363,6 +363,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -450,7 +459,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -559,9 +568,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -577,19 +586,20 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" +checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", - "itertools", + "itertools 0.13.0", "lru", "paste", "stability", "strum", + "strum_macros", "unicode-segmentation", "unicode-truncate", "unicode-width", @@ -610,7 +620,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -719,9 +729,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", @@ -797,9 +807,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] @@ -819,9 +829,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -874,7 +884,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" dependencies = [ - "itertools", + "itertools 0.12.1", "unicode-width", ] diff --git a/Cargo.toml b/Cargo.toml index 8e67312dfc..64844c3a39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,9 +52,9 @@ crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" -ratatui = { version = "0.26.3", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } -serde_json = "1.0.117" +serde_json = "1.0.118" serde.workspace = true toml_edit.workspace = true From 129884aff74964d13aba8309014554b5625d6e5b Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 18:21:19 +0200 Subject: [PATCH 0964/1432] errors5 solution --- exercises/13_error_handling/errors5.rs | 76 ++++++++++++-------------- rustlings-macros/info.toml | 17 +++--- solutions/13_error_handling/errors5.rs | 55 ++++++++++++++++++- 3 files changed, 96 insertions(+), 52 deletions(-) diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 719256262a..d0044db213 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -1,38 +1,18 @@ -// 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 -// course, like `Box` and the `From` trait. It's not important to understand -// them in detail right now, but you can read ahead if you like. For now, think -// of the `Box` type as an "I want anything that does ???" type, which, -// given Rust's usual standards for runtime safety, should strike you as -// somewhat lenient! +// This exercise is an altered version of the `errors4` exercise. It uses some +// concepts that we won't get to until later in the course, like `Box` and the +// `From` trait. It's not important to understand them in detail right now, but +// you can read ahead if you like. For now, think of the `Box` type as +// an "I want anything that does ???" type. // // In short, this particular use case for boxes is for when you want to own a // value and you care only that it is a type which implements a particular -// trait. To do so, The Box is declared as of type Box where Trait is -// the trait the compiler looks for on any value used in that context. For this -// exercise, that context is the potential errors which can be returned in a -// Result. -// -// What can we use to describe both errors? In other words, is there a trait -// which both errors implement? +// trait. To do so, The `Box` is declared as of type `Box` where +// `Trait` is the trait the compiler looks for on any value used in that +// context. For this exercise, that context is the potential errors which +// can be returned in a `Result`. -use std::error; +use std::error::Error; use std::fmt; -use std::num::ParseIntError; - -// TODO: update the return type of `main()` to make this compile. -fn main() -> Result<(), Box> { - let pretend_user_input = "42"; - let x: i64 = pretend_user_input.parse()?; - println!("output={:?}", PositiveNonzeroInteger::new(x)?); - Ok(()) -} - -// Don't change anything below this line. - -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); #[derive(PartialEq, Debug)] enum CreationError { @@ -40,17 +20,7 @@ enum CreationError { Zero, } -impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { - match value { - x if x < 0 => Err(CreationError::Negative), - x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)), - } - } -} - -// This is required so that `CreationError` can implement `error::Error`. +// This is required so that `CreationError` can implement `Error`. impl fmt::Display for CreationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let description = match *self { @@ -61,4 +31,26 @@ impl fmt::Display for CreationError { } } -impl error::Error for CreationError {} +impl Error for CreationError {} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + 0 => Err(CreationError::Zero), + x if x < 0 => Err(CreationError::Negative), + x => Ok(PositiveNonzeroInteger(x as u64)), + } + } +} + +// TODO: Add the correct return type `Result<(), Box>`. What can we +// use to describe both errors? Is there a trait which both errors implement? +fn main() { + let pretend_user_input = "42"; + let x: i64 = pretend_user_input.parse()?; + println!("output={:?}", PositiveNonzeroInteger::new(x)?); + Ok(()) +} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index d39044c06a..700c1792a3 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -692,24 +692,23 @@ name = "errors5" dir = "13_error_handling" test = false hint = """ -There are two different possible `Result` types produced within `main()`, which -are propagated using `?` operators. How do we declare a return type from -`main()` that allows both? +There are two different possible `Result` types produced within the `main` +function, which are propagated using the `?` operators. How do we declare a +return type for the `main` function that allows both? Under the hood, the `?` operator calls `From::from` on the error value to -convert it to a boxed trait object, a `Box`. This boxed trait -object is polymorphic, and since all errors implement the `error::Error` trait, -we can capture lots of different errors in one "Box" object. +convert it to a boxed trait object, a `Box`. This boxed trait object +is polymorphic, and since all errors implement the `Error` trait, we can capture +lots of different errors in one `Box` object. -Check out this section of the book: +Check out this section of The Book: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator Read more about boxing errors: https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html Read more about using the `?` operator with boxed errors: -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html""" [[exercises]] name = "errors6" diff --git a/solutions/13_error_handling/errors5.rs b/solutions/13_error_handling/errors5.rs index 4e18198923..c1424eeee5 100644 --- a/solutions/13_error_handling/errors5.rs +++ b/solutions/13_error_handling/errors5.rs @@ -1 +1,54 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// This exercise is an altered version of the `errors4` exercise. It uses some +// concepts that we won't get to until later in the course, like `Box` and the +// `From` trait. It's not important to understand them in detail right now, but +// you can read ahead if you like. For now, think of the `Box` type as +// an "I want anything that does ???" type. +// +// In short, this particular use case for boxes is for when you want to own a +// value and you care only that it is a type which implements a particular +// trait. To do so, The `Box` is declared as of type `Box` where +// `Trait` is the trait the compiler looks for on any value used in that +// context. For this exercise, that context is the potential errors which +// can be returned in a `Result`. + +use std::error::Error; +use std::fmt; + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// This is required so that `CreationError` can implement `Error`. +impl fmt::Display for CreationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let description = match *self { + CreationError::Negative => "number is negative", + CreationError::Zero => "number is zero", + }; + f.write_str(description) + } +} + +impl Error for CreationError {} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + 0 => Err(CreationError::Zero), + x => Ok(PositiveNonzeroInteger(x as u64)), + } + } +} + +fn main() -> Result<(), Box> { + let pretend_user_input = "42"; + let x: i64 = pretend_user_input.parse()?; + println!("output={:?}", PositiveNonzeroInteger::new(x)?); + Ok(()) +} From b1daea1fe8536d7b7b4463cb8fc36d69848ef77a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 01:12:50 +0200 Subject: [PATCH 0965/1432] errors6 solution --- exercises/13_error_handling/errors6.rs | 71 ++++++++++---------- rustlings-macros/info.toml | 10 +-- solutions/13_error_handling/errors6.rs | 93 +++++++++++++++++++++++++- 3 files changed, 130 insertions(+), 44 deletions(-) diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index 8b08e08677..0652abda54 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -1,12 +1,18 @@ -// Using catch-all error types like `Box` isn't recommended -// for library code, where callers might want to make decisions based on the -// error content, instead of printing it out or propagating it further. Here, we -// define a custom error type to make it possible for callers to decide what to -// do next when our function returns an error. +// Using catch-all error types like `Box` isn't recommended for +// library code where callers might want to make decisions based on the error +// content instead of printing it out or propagating it further. Here, we define +// a custom error type to make it possible for callers to decide what to do next +// when our function returns an error. use std::num::ParseIntError; -// This is a custom error type that we will be using in `parse_pos_nonzero()`. +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// A custom error type that we will be using in `PositiveNonzeroInteger::parse`. #[derive(PartialEq, Debug)] enum ParsePosNonzeroError { Creation(CreationError), @@ -14,39 +20,32 @@ enum ParsePosNonzeroError { } impl ParsePosNonzeroError { - fn from_creation(err: CreationError) -> ParsePosNonzeroError { - ParsePosNonzeroError::Creation(err) + fn from_creation(err: CreationError) -> Self { + Self::Creation(err) } - // TODO: add another error conversion function here. - // fn from_parseint... -} -fn parse_pos_nonzero(s: &str) -> Result { - // TODO: change this to return an appropriate error instead of panicking - // when `parse()` returns an error. - let x: i64 = s.parse().unwrap(); - PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation) + // TODO: Add another error conversion function here. + // fn from_parseint(???) -> Self { ??? } } -// Don't change anything below this line. - #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); -#[derive(PartialEq, Debug)] -enum CreationError { - Negative, - Zero, -} - impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { + fn new(value: i64) -> Result { match value { x if x < 0 => Err(CreationError::Negative), x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)), + x => Ok(Self(x as u64)), } } + + fn parse(s: &str) -> Result { + // TODO: change this to return an appropriate error instead of panicking + // when `parse()` returns an error. + let x: i64 = s.parse().unwrap(); + Self::new(x).map_err(ParsePosNonzeroError::from_creation) + } } fn main() { @@ -56,36 +55,36 @@ fn main() { #[cfg(test)] mod test { use super::*; + use std::num::IntErrorKind; #[test] fn test_parse_error() { - // We can't construct a ParseIntError, so we have to pattern match. assert!(matches!( - parse_pos_nonzero("not a number"), - Err(ParsePosNonzeroError::ParseInt(_)) + PositiveNonzeroInteger::parse("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)), )); } #[test] fn test_negative() { assert_eq!( - parse_pos_nonzero("-555"), - Err(ParsePosNonzeroError::Creation(CreationError::Negative)) + PositiveNonzeroInteger::parse("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)), ); } #[test] fn test_zero() { assert_eq!( - parse_pos_nonzero("0"), - Err(ParsePosNonzeroError::Creation(CreationError::Zero)) + PositiveNonzeroInteger::parse("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)), ); } #[test] fn test_positive() { - let x = PositiveNonzeroInteger::new(42); - assert!(x.is_ok()); - assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap())); + let x = PositiveNonzeroInteger::new(42).unwrap(); + assert_eq!(x.0, 42); + assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 700c1792a3..dc288c0c70 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -714,17 +714,13 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen name = "errors6" dir = "13_error_handling" hint = """ -This exercise uses a completed version of `PositiveNonzeroInteger` from -errors4. +This exercise uses a completed version of `PositiveNonzeroInteger` from the +previous exercises. Below the line that `TODO` asks you to change, there is an example of using the `map_err()` method on a `Result` to transform one type of error into another. Try using something similar on the `Result` from `parse()`. You -might use the `?` operator to return early from the function, or you might -use a `match` expression, or maybe there's another way! - -You can create another function inside `impl ParsePosNonzeroError` to use -with `map_err()`. +can then use the `?` operator to return early. Read more about `map_err()` in the `std::result` documentation: https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs index 4e18198923..70680cf0b6 100644 --- a/solutions/13_error_handling/errors6.rs +++ b/solutions/13_error_handling/errors6.rs @@ -1 +1,92 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Using catch-all error types like `Box` isn't recommended for +// library code where callers might want to make decisions based on the error +// content instead of printing it out or propagating it further. Here, we define +// a custom error type to make it possible for callers to decide what to do next +// when our function returns an error. + +use std::num::ParseIntError; + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +// A custom error type that we will be using in `PositiveNonzeroInteger::parse`. +#[derive(PartialEq, Debug)] +enum ParsePosNonzeroError { + Creation(CreationError), + ParseInt(ParseIntError), +} + +impl ParsePosNonzeroError { + fn from_creation(err: CreationError) -> Self { + Self::Creation(err) + } + + fn from_parseint(err: ParseIntError) -> Self { + Self::ParseInt(err) + } +} + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + x if x == 0 => Err(CreationError::Zero), + x => Ok(Self(x as u64)), + } + } + + fn parse(s: &str) -> Result { + // Return an appropriate error instead of panicking when `parse()` + // returns an error. + let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Self::new(x).map_err(ParsePosNonzeroError::from_creation) + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod test { + use super::*; + use std::num::IntErrorKind; + + #[test] + fn test_parse_error() { + assert!(matches!( + PositiveNonzeroInteger::parse("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)), + )); + } + + #[test] + fn test_negative() { + assert_eq!( + PositiveNonzeroInteger::parse("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)), + ); + } + + #[test] + fn test_zero() { + assert_eq!( + PositiveNonzeroInteger::parse("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)), + ); + } + + #[test] + fn test_positive() { + let x = PositiveNonzeroInteger::new(42).unwrap(); + assert_eq!(x.0, 42); + assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x)); + } +} From 46121b71cf2f4da296e80fad025eaee03c67dcd5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 02:00:08 +0200 Subject: [PATCH 0966/1432] generics1 rewrite and solution --- exercises/14_generics/generics1.rs | 19 +++++++++++++++---- rustlings-macros/info.toml | 7 ++++++- solutions/14_generics/generics1.rs | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs index c023e644a8..87ed990b65 100644 --- a/exercises/14_generics/generics1.rs +++ b/exercises/14_generics/generics1.rs @@ -1,7 +1,18 @@ -// This shopping list program isn't compiling! Use your knowledge of generics to -// fix it. +// `Vec` is generic over the type `T`. In most cases, the compiler is able to +// infer `T`, for example after pushing a value with a concrete type to the vector. +// But in this exercise, the compiler needs some help through a type annotation. fn main() { - let mut shopping_list: Vec = Vec::new(); - shopping_list.push("milk"); + // TODO: Fix the compiler error by annotating the type of the vector + // `Vec`. Choose `T` as some integer type that can be created from + // `u8` and `i8`. + let mut numbers = Vec::new(); + + // Don't change the lines below. + let n1: u8 = 42; + numbers.push(n1.into()); + let n2: i8 = -1; + numbers.push(n2.into()); + + println!("{numbers:?}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index dc288c0c70..23eb304840 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -734,8 +734,13 @@ test = false hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any type. +If the vector `numbers` has the type `Vec`, then we can only push values of +type `T` to it. By using `into()` before pushing, we ask the compiler to convert +`n1` and `n2` to `T`. But the compiler doesn't know what `T` is yet and needs a +type annotation. -You need to tell the compiler what type we are pushing onto this vector.""" +`u8` and `i8` can both be converted to `i16`, `i32` and `i64`. Choose one for +the generic of the vector.""" [[exercises]] name = "generics2" diff --git a/solutions/14_generics/generics1.rs b/solutions/14_generics/generics1.rs index 4e18198923..e2195fd360 100644 --- a/solutions/14_generics/generics1.rs +++ b/solutions/14_generics/generics1.rs @@ -1 +1,17 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// `Vec` is generic over the type `T`. In most cases, the compiler is able to +// infer `T`, for example after pushing a value with a concrete type to the vector. +// But in this exercise, the compiler needs some help through a type annotation. + +fn main() { + // `u8` and `i8` can both be converted to `i16`. + let mut numbers: Vec = Vec::new(); + // ^^^^^^^^^^ added + + // Don't change the lines below. + let n1: u8 = 42; + numbers.push(n1.into()); + let n2: i8 = -1; + numbers.push(n2.into()); + + println!("{numbers:?}"); +} From de3f846a53055bbca5ec9dd6d536a31c82d39648 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 02:25:11 +0200 Subject: [PATCH 0967/1432] generics2 solution --- exercises/14_generics/generics2.rs | 4 ++-- rustlings-macros/info.toml | 8 ++------ solutions/14_generics/generics2.rs | 29 ++++++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index 6cdcdaf532..8908725bf9 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -1,10 +1,10 @@ // This powerful wrapper provides the ability to store a positive integer value. -// Rewrite it using generics so that it supports wrapping ANY type. - +// TODO: Rewrite it using a generic so that it supports wrapping ANY type. struct Wrapper { value: u32, } +// TODO: Adapt the struct's implementation to be generic over the wrapped value. impl Wrapper { fn new(value: u32) -> Self { Wrapper { value } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 23eb304840..11d6d59f3c 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -746,12 +746,8 @@ the generic of the vector.""" name = "generics2" dir = "14_generics" hint = """ -Currently we are wrapping only values of type `u32`. - -Maybe we could update the explicit references to this data type somehow? - -If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions -""" +Related section in The Book: +https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions""" # TRAITS diff --git a/solutions/14_generics/generics2.rs b/solutions/14_generics/generics2.rs index 4e18198923..14f3f7afea 100644 --- a/solutions/14_generics/generics2.rs +++ b/solutions/14_generics/generics2.rs @@ -1 +1,28 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +struct Wrapper { + value: T, +} + +impl Wrapper { + fn new(value: T) -> Self { + Wrapper { value } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn store_u32_in_wrapper() { + assert_eq!(Wrapper::new(42).value, 42); + } + + #[test] + fn store_str_in_wrapper() { + assert_eq!(Wrapper::new("Foo").value, "Foo"); + } +} From 789223cc9e247eb9da90698b1c3011c26cdc863c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 03:04:57 +0200 Subject: [PATCH 0968/1432] traits1 solution --- exercises/15_traits/traits1.rs | 17 ++++++----------- solutions/15_traits/traits1.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs index b17c9c6ebb..85be17ea82 100644 --- a/exercises/15_traits/traits1.rs +++ b/exercises/15_traits/traits1.rs @@ -1,19 +1,17 @@ -// 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. - +// The trait `AppendBar` has only one function which appends "Bar" to any object +// implementing this trait. trait AppendBar { fn append_bar(self) -> Self; } impl AppendBar for String { - // TODO: Implement `AppendBar` for type `String`. + // TODO: Implement `AppendBar` for the type `String`. } fn main() { let s = String::from("Foo"); let s = s.append_bar(); - println!("s: {}", s); + println!("s: {s}"); } #[cfg(test)] @@ -22,14 +20,11 @@ mod tests { #[test] fn is_foo_bar() { - assert_eq!(String::from("Foo").append_bar(), String::from("FooBar")); + assert_eq!(String::from("Foo").append_bar(), "FooBar"); } #[test] fn is_bar_bar() { - assert_eq!( - String::from("").append_bar().append_bar(), - String::from("BarBar") - ); + assert_eq!(String::from("").append_bar().append_bar(), "BarBar"); } } diff --git a/solutions/15_traits/traits1.rs b/solutions/15_traits/traits1.rs index 4e18198923..790873f4da 100644 --- a/solutions/15_traits/traits1.rs +++ b/solutions/15_traits/traits1.rs @@ -1 +1,32 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// The trait `AppendBar` has only one function which appends "Bar" to any object +// implementing this trait. +trait AppendBar { + fn append_bar(self) -> Self; +} + +impl AppendBar for String { + fn append_bar(self) -> Self { + self + "Bar" + } +} + +fn main() { + let s = String::from("Foo"); + let s = s.append_bar(); + println!("s: {s}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_foo_bar() { + assert_eq!(String::from("Foo").append_bar(), "FooBar"); + } + + #[test] + fn is_bar_bar() { + assert_eq!(String::from("").append_bar().append_bar(), "BarBar"); + } +} From f0849447adb1f92f3ca48f7326ad493667160464 Mon Sep 17 00:00:00 2001 From: David Brownman Date: Wed, 26 Jun 2024 19:05:04 -0700 Subject: [PATCH 0969/1432] chore(from_into): Add missing period in docs --- exercises/23_conversions/from_into.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 11787c37dc..10836a6d0f 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -39,7 +39,7 @@ impl Default for Person { // 5. Extract the other element from the split operation and parse it into a // `usize` as the age. // If while parsing the age, something goes wrong, then return the default of -// Person Otherwise, then return an instantiated Person object with the results +// Person. Otherwise, then return an instantiated Person object with the results // I AM NOT DONE From 3e9c4c8bb83ee97ed49ef2004afba20ef8d387c1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 11:18:21 +0200 Subject: [PATCH 0970/1432] Update deps --- Cargo.lock | 199 +++++++++++++++++++++++++---------------------------- Cargo.toml | 12 ++-- 2 files changed, 98 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0988dbcdf5..3d0b16b25a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,47 +13,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -61,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "assert_cmd" @@ -82,9 +83,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" @@ -94,9 +95,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bstr" @@ -117,9 +118,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -127,9 +128,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -139,9 +140,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", @@ -151,15 +152,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "console" @@ -176,18 +177,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "difflib" @@ -203,9 +204,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" @@ -221,9 +222,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -267,9 +268,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -331,13 +332,19 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itoa" version = "1.0.11" @@ -366,21 +373,21 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" @@ -390,9 +397,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mio" @@ -418,7 +425,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -444,9 +451,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -495,9 +502,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -522,9 +529,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -534,9 +541,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -545,17 +552,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -584,9 +591,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -599,18 +606,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -619,9 +626,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", @@ -630,9 +637,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -651,9 +658,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -668,18 +675,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.11" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb686a972ccef8537b39eead3968b0e8616cb5040dbb9bba93007c8e07c9215f" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -696,15 +703,15 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "wait-timeout" @@ -743,37 +750,15 @@ dependencies = [ "winsafe", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" @@ -915,9 +900,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 6f523abfce..44409fcd3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,17 +9,17 @@ authors = [ edition = "2021" [dependencies] -anyhow = "1.0.82" -clap = { version = "4.5.4", features = ["derive"] } +anyhow = "1.0.86" +clap = { version = "4.5.7", features = ["derive"] } console = "0.15.8" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" -serde_json = "1.0.116" -serde = { version = "1.0.198", features = ["derive"] } +serde_json = "1.0.118" +serde = { version = "1.0.203", features = ["derive"] } shlex = "1.3.0" -toml_edit = { version = "0.22.11", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] } which = "6.0.1" -winnow = "0.6.6" +winnow = "0.6.13" [[bin]] name = "rustlings" From 091e1e7f7a1afad539479674e06cae7c8d8dab7f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 11:58:44 +0200 Subject: [PATCH 0971/1432] traits2 solution --- exercises/15_traits/traits2.rs | 13 ++++--------- rustlings-macros/info.toml | 15 ++++++--------- solutions/15_traits/traits2.rs | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index 170779b256..e9040162d6 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -1,14 +1,9 @@ -// 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! - trait AppendBar { fn append_bar(self) -> Self; } -// TODO: Implement trait `AppendBar` for a vector of strings. +// TODO: Implement the trait `AppendBar` for a vector of strings. +// `appned_bar` should push the string "Bar" into the vector. fn main() { // You can optionally experiment here. @@ -21,7 +16,7 @@ mod tests { #[test] fn is_vec_pop_eq_bar() { let mut foo = vec![String::from("Foo")].append_bar(); - assert_eq!(foo.pop().unwrap(), String::from("Bar")); - assert_eq!(foo.pop().unwrap(), String::from("Foo")); + assert_eq!(foo.pop().unwrap(), "Bar"); + assert_eq!(foo.pop().unwrap(), "Foo"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 2cc1db6508..604f674160 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -755,21 +755,18 @@ https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions" name = "traits1" dir = "15_traits" hint = """ -A discussion about Traits in Rust can be found at: -https://doc.rust-lang.org/book/ch10-02-traits.html -""" +More about traits in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html""" [[exercises]] name = "traits2" dir = "15_traits" hint = """ -Notice how the trait takes ownership of `self`, and returns `Self`. - -Try mutating the incoming string vector. Have a look at the tests to see -what the result should look like! +Notice how the trait takes ownership of `self` and returns `Self`. -Vectors provide suitable methods for adding an element at the end. See -the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" +Although the signature of `append_bar` in the trait takes `self` as argument, +the implementation can take `mut self` instead. This is possible because the +the value is owned anyway.""" [[exercises]] name = "traits3" diff --git a/solutions/15_traits/traits2.rs b/solutions/15_traits/traits2.rs index 4e18198923..0db93e0f4f 100644 --- a/solutions/15_traits/traits2.rs +++ b/solutions/15_traits/traits2.rs @@ -1 +1,27 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +trait AppendBar { + fn append_bar(self) -> Self; +} + +impl AppendBar for Vec { + fn append_bar(mut self) -> Self { + // ^^^ this is important + self.push(String::from("Bar")); + self + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_vec_pop_eq_bar() { + let mut foo = vec![String::from("Foo")].append_bar(); + assert_eq!(foo.pop().unwrap(), "Bar"); + assert_eq!(foo.pop().unwrap(), "Foo"); + } +} From c07209b635d6adf6fcadcbcca14942bf76a0a978 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:00:28 +0200 Subject: [PATCH 0972/1432] Unify info.toml --- rustlings-macros/info.toml | 40 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 604f674160..f5dd82ad5b 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -776,8 +776,7 @@ Traits can have a default implementation for functions. Structs that implement the trait can then use the default version of these functions if they choose not to implement the function themselves. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations -""" +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations""" [[exercises]] name = "traits4" @@ -786,8 +785,7 @@ hint = """ Instead of using concrete types as parameters you can use traits. Try replacing the '??' with 'impl [what goes here?]' -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters -""" +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" [[exercises]] name = "traits5" @@ -797,8 +795,7 @@ hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the '??' with 'impl [what goes here?] + [what goes here?]'. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax -""" +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax""" # QUIZ 3 @@ -913,8 +910,7 @@ Step 4: An iterator goes through all elements in a collection, but what if we've run out of elements? What should we expect here? If you're stuck, take a look at -https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. -""" +https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.""" [[exercises]] name = "iterators2" @@ -1008,8 +1004,7 @@ assertions). For a non-empty list keep in mind that we want to use our `Cons` "list builder". Although the current list is one of integers (`i32`), feel free to change the -definition and try other types! -""" +definition and try other types!""" [[exercises]] name = "rc1" @@ -1026,8 +1021,7 @@ In the end the `Sun` only has one reference again, to itself. See more at: https://doc.rust-lang.org/book/ch15-04-rc.html -* Unfortunately Pluto is no longer considered a planet :( -""" +* Unfortunately Pluto is no longer considered a planet :(""" [[exercises]] name = "arc1" @@ -1042,10 +1036,9 @@ inside the loop but still in the main thread. thread-local copy of the numbers. This is a simple exercise if you understand the underlying concepts, but if this -is too much of a struggle, consider reading through all of Chapter 16 in the -book: -https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html -""" +is too much of a struggle, consider reading through all of Chapter 16 in The +Book: +https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html""" [[exercises]] name = "cow1" @@ -1055,8 +1048,7 @@ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is called. Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation -on the `Cow` type. -""" +on the `Cow` type.""" # THREADS @@ -1075,8 +1067,7 @@ https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-f Use the `JoinHandle`s to wait for each thread to finish and collect their results. -https://doc.rust-lang.org/std/thread/struct.JoinHandle.html -""" +https://doc.rust-lang.org/std/thread/struct.JoinHandle.html""" [[exercises]] name = "threads2" @@ -1097,8 +1088,7 @@ let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); ``` Similar to the code in the following example in the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads -""" +https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads""" [[exercises]] name = "threads3" @@ -1113,8 +1103,7 @@ one thread and receive them in another. Multiple producers are possible by using clone() to create a duplicate of the original sending end. -See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. -""" +See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info.""" # MACROS @@ -1230,8 +1219,7 @@ or a closure to wrap the error from `parse::`. Yet another hint: If you would like to propagate errors by using the `?` operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html -""" +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html""" [[exercises]] name = "try_from_into" From b4b7ae63ada9128d4798d301cfc757a60904c6b8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:11:57 +0200 Subject: [PATCH 0973/1432] traits3 solution --- exercises/15_traits/traits3.rs | 15 +++++++------- rustlings-macros/info.toml | 9 +++++---- solutions/15_traits/traits3.rs | 37 +++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 66da235f46..c244650da6 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -1,9 +1,8 @@ -// 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. - trait Licensed { + // TODO: Add a default implementation for `licensing_info` so that + // implementors like the two structs below can share that default behavior + // without repeating the function. + // The default license information should be the string "Default license". fn licensing_info(&self) -> String; } @@ -15,8 +14,8 @@ struct OtherSoftware { version_number: String, } -impl Licensed for SomeSoftware {} // Don't edit this line -impl Licensed for OtherSoftware {} // Don't edit this line +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. @@ -28,7 +27,7 @@ mod tests { #[test] fn is_licensing_info_the_same() { - let licensing_info = String::from("Some information"); + let licensing_info = "Default license"; let some_software = SomeSoftware { version_number: 1 }; let other_software = OtherSoftware { version_number: "v2.0.0".to_string(), diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index f5dd82ad5b..92e440aecd 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -772,11 +772,12 @@ the value is owned anyway.""" name = "traits3" dir = "15_traits" hint = """ -Traits can have a default implementation for functions. Structs that implement -the trait can then use the default version of these functions if they choose not -to implement the function themselves. +Traits can have a default implementation for functions. Data types that +implement the trait can then use the default version of these functions +if they choose not to implement the function themselves. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations""" +Related section in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations""" [[exercises]] name = "traits4" diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs index 4e18198923..3d8ec85ead 100644 --- a/solutions/15_traits/traits3.rs +++ b/solutions/15_traits/traits3.rs @@ -1 +1,36 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +trait Licensed { + fn licensing_info(&self) -> String { + "Default license".to_string() + } +} + +struct SomeSoftware { + version_number: i32, +} + +struct OtherSoftware { + version_number: String, +} + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_licensing_info_the_same() { + let licensing_info = "Default license"; + let some_software = SomeSoftware { version_number: 1 }; + let other_software = OtherSoftware { + version_number: "v2.0.0".to_string(), + }; + assert_eq!(some_software.licensing_info(), licensing_info); + assert_eq!(other_software.licensing_info(), licensing_info); + } +} From c0452d160b889b3686409820192797d9e9f9cad7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:23:33 +0200 Subject: [PATCH 0974/1432] traits4 solution --- exercises/15_traits/traits4.rs | 27 ++++++++------------------ rustlings-macros/info.toml | 5 +++-- solutions/15_traits/traits4.rs | 35 +++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index ed63f6e135..80092a6466 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -1,23 +1,18 @@ -// Your task is to replace the '??' sections so the code compiles. -// -// Don't change any line other than the marked one. - trait Licensed { fn licensing_info(&self) -> String { - "some information".to_string() + "Default license".to_string() } } -struct SomeSoftware {} - -struct OtherSoftware {} +struct SomeSoftware; +struct OtherSoftware; impl Licensed for SomeSoftware {} impl Licensed for OtherSoftware {} -// YOU MAY ONLY CHANGE THE NEXT LINE -fn compare_license_types(software: ??, software_two: ??) -> bool { - software.licensing_info() == software_two.licensing_info() +// TODO: Fix the compiler error by only changing the signature of this function. +fn compare_license_types(software1: ???, software2: ???) -> bool { + software1.licensing_info() == software2.licensing_info() } fn main() { @@ -30,17 +25,11 @@ mod tests { #[test] fn compare_license_information() { - let some_software = SomeSoftware {}; - let other_software = OtherSoftware {}; - - assert!(compare_license_types(some_software, other_software)); + assert!(compare_license_types(SomeSoftware, OtherSoftware)); } #[test] fn compare_license_information_backwards() { - let some_software = SomeSoftware {}; - let other_software = OtherSoftware {}; - - assert!(compare_license_types(other_software, some_software)); + assert!(compare_license_types(OtherSoftware, SomeSoftware)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 92e440aecd..43ccdf80be 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -784,9 +784,10 @@ name = "traits4" dir = "15_traits" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing -the '??' with 'impl [what goes here?]' +`???` with `impl [what goes here?]`. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" +Related section in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" [[exercises]] name = "traits5" diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs index 4e18198923..78b5a110ee 100644 --- a/solutions/15_traits/traits4.rs +++ b/solutions/15_traits/traits4.rs @@ -1 +1,34 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +trait Licensed { + fn licensing_info(&self) -> String { + "Default license".to_string() + } +} + +struct SomeSoftware; +struct OtherSoftware; + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool { + software1.licensing_info() == software2.licensing_info() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compare_license_information() { + assert!(compare_license_types(SomeSoftware, OtherSoftware)); + } + + #[test] + fn compare_license_information_backwards() { + assert!(compare_license_types(OtherSoftware, SomeSoftware)); + } +} From db4d649e557f34641f2c7cc197dff2fb29637a7f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:27:53 +0200 Subject: [PATCH 0975/1432] Remove move_semantics6 --- dev/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 3283871a7a..2c5eaf0915 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -60,8 +60,6 @@ bin = [ { name = "move_semantics4_sol", path = "../solutions/06_move_semantics/move_semantics4.rs" }, { name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" }, { name = "move_semantics5_sol", path = "../solutions/06_move_semantics/move_semantics5.rs" }, - { name = "move_semantics6", path = "../exercises/06_move_semantics/move_semantics6.rs" }, - { name = "move_semantics6_sol", path = "../solutions/06_move_semantics/move_semantics6.rs" }, { name = "structs1", path = "../exercises/07_structs/structs1.rs" }, { name = "structs1_sol", path = "../solutions/07_structs/structs1.rs" }, { name = "structs2", path = "../exercises/07_structs/structs2.rs" }, From 45cfe86fb05a21dd52d9d72d07e881037803395d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:29:25 +0200 Subject: [PATCH 0976/1432] traits5 solution --- exercises/15_traits/traits5.rs | 28 ++++++++++++++---------- rustlings-macros/info.toml | 6 ++--- solutions/15_traits/traits5.rs | 40 +++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index 3e62283fb6..5b356ac738 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -1,7 +1,3 @@ -// Your task is to replace the '??' sections so the code compiles. -// -// Don't change any line other than the marked one. - trait SomeTrait { fn some_function(&self) -> bool { true @@ -14,20 +10,30 @@ trait OtherTrait { } } -struct SomeStruct {} -struct OtherStruct {} - +struct SomeStruct; impl SomeTrait for SomeStruct {} impl OtherTrait for SomeStruct {} + +struct OtherStruct; impl SomeTrait for OtherStruct {} impl OtherTrait for OtherStruct {} -// YOU MAY ONLY CHANGE THE NEXT LINE -fn some_func(item: ??) -> bool { +// TODO: Fix the compiler error by only changing the signature of this function. +fn some_func(item: ???) -> bool { item.some_function() && item.other_function() } fn main() { - some_func(SomeStruct {}); - some_func(OtherStruct {}); + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_some_func() { + assert!(some_func(SomeStruct)); + assert!(some_func(OtherStruct)); + } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 43ccdf80be..92c878fd95 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -792,12 +792,12 @@ https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters""" [[exercises]] name = "traits5" dir = "15_traits" -test = false hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try -replacing the '??' with 'impl [what goes here?] + [what goes here?]'. +replacing `???` with 'impl [what goes here?] + [what goes here?]'. -See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax""" +Related section in The Book: +https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax""" # QUIZ 3 diff --git a/solutions/15_traits/traits5.rs b/solutions/15_traits/traits5.rs index 4e18198923..1fb426a474 100644 --- a/solutions/15_traits/traits5.rs +++ b/solutions/15_traits/traits5.rs @@ -1 +1,39 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +trait SomeTrait { + fn some_function(&self) -> bool { + true + } +} + +trait OtherTrait { + fn other_function(&self) -> bool { + true + } +} + +struct SomeStruct; +impl SomeTrait for SomeStruct {} +impl OtherTrait for SomeStruct {} + +struct OtherStruct; +impl SomeTrait for OtherStruct {} +impl OtherTrait for OtherStruct {} + +fn some_func(item: impl SomeTrait + OtherTrait) -> bool { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + item.some_function() && item.other_function() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_some_func() { + assert!(some_func(SomeStruct)); + assert!(some_func(OtherStruct)); + } +} From c1707404231e5a1d7bc837e21faf34fdc51db0bf Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 12:29:35 +0200 Subject: [PATCH 0977/1432] Highlight change in traits4 solution --- solutions/15_traits/traits4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs index 78b5a110ee..3675b8dfe1 100644 --- a/solutions/15_traits/traits4.rs +++ b/solutions/15_traits/traits4.rs @@ -11,6 +11,7 @@ impl Licensed for SomeSoftware {} impl Licensed for OtherSoftware {} fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool { + // ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ software1.licensing_info() == software2.licensing_info() } From 64c2de95ca95c1d23dcb416723b33ccdfca9c956 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 13:01:52 +0200 Subject: [PATCH 0978/1432] quiz3 solution --- exercises/quizzes/quiz3.rs | 18 +++++----- rustlings-macros/info.toml | 12 +++++-- solutions/quizzes/quiz3.rs | 70 +++++++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 13 deletions(-) diff --git a/exercises/quizzes/quiz3.rs b/exercises/quizzes/quiz3.rs index f3cb1bcf26..c877c5f8e6 100644 --- a/exercises/quizzes/quiz3.rs +++ b/exercises/quizzes/quiz3.rs @@ -3,26 +3,27 @@ // - Traits // // An imaginary magical school has a new report card generation system written -// in Rust! Currently the system only supports creating report cards where the +// in Rust! Currently, the system only supports creating report cards where the // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the // school also issues alphabetical grades (A+ -> F-) and needs to be able to // print both types of report card! // -// 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. +// Make the necessary code changes in the struct `ReportCard` and the impl +// block to support alphabetical report cards in addition to numerical ones. +// TODO: Adjust the struct as described above. struct ReportCard { grade: f32, student_name: String, student_age: u8, } +// TODO: Adjust the impl block as described above. impl ReportCard { fn print(&self) -> String { format!( "{} ({}) - achieved a grade of {}", - &self.student_name, &self.student_age, &self.grade + &self.student_name, &self.student_age, &self.grade, ) } } @@ -44,21 +45,20 @@ mod tests { }; assert_eq!( report_card.print(), - "Tom Wriggle (12) - achieved a grade of 2.1" + "Tom Wriggle (12) - achieved a grade of 2.1", ); } #[test] fn generate_alphabetic_report_card() { - // TODO: Make sure to change the grade here after you finish the exercise. let report_card = ReportCard { - grade: 2.1, + grade: "A+", student_name: "Gary Plotter".to_string(), student_age: 11, }; assert_eq!( report_card.print(), - "Gary Plotter (11) - achieved a grade of A+" + "Gary Plotter (11) - achieved a grade of A+", ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 92c878fd95..bed2eafa90 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -805,10 +805,16 @@ https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bou name = "quiz3" dir = "quizzes" hint = """ -To find the best solution to this challenge you're going to need to think back -to your knowledge of traits, specifically 'Trait Bound Syntax' +To find the best solution to this challenge, you need to recall your knowledge +of traits, specifically "Trait Bound Syntax": +https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax -You may also need this: `use std::fmt::Display;`.""" +Here is how to specify a trait bound for an implementation block: +`impl for Foo { … }` + +You may need this: +`use std::fmt::Display;` +""" # LIFETIMES diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs index 4e18198923..e3413fd01e 100644 --- a/solutions/quizzes/quiz3.rs +++ b/solutions/quizzes/quiz3.rs @@ -1 +1,69 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// This quiz tests: +// - Generics +// - Traits +// +// An imaginary magical school has a new report card generation system written +// in Rust! Currently, the system only supports creating report cards where the +// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the +// school also issues alphabetical grades (A+ -> F-) and needs to be able to +// print both types of report card! +// +// Make the necessary code changes in the struct `ReportCard` and the impl +// block to support alphabetical report cards in addition to numerical ones. + +use std::fmt::Display; + +// Make the struct generic over `T`. +struct ReportCard { + // ^^^ + grade: T, + // ^ + student_name: String, + student_age: u8, +} + +// To be able to print the grade, it has to implement the `Display` trait. +impl ReportCard { + // ^^^^^^^ require that `T` implements `Display`. + fn print(&self) -> String { + 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::*; + + #[test] + fn generate_numeric_report_card() { + let report_card = ReportCard { + grade: 2.1, + student_name: "Tom Wriggle".to_string(), + student_age: 12, + }; + assert_eq!( + report_card.print(), + "Tom Wriggle (12) - achieved a grade of 2.1", + ); + } + + #[test] + fn generate_alphabetic_report_card() { + let report_card = ReportCard { + grade: "A+", + student_name: "Gary Plotter".to_string(), + student_age: 11, + }; + assert_eq!( + report_card.print(), + "Gary Plotter (11) - achieved a grade of A+", + ); + } +} From 7efccc36b4c26c444eab2531b6139190af569d6f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 13:24:21 +0200 Subject: [PATCH 0979/1432] lifetimes1 solution --- exercises/16_lifetimes/lifetimes1.rs | 16 +++++++++++---- rustlings-macros/info.toml | 3 +-- solutions/16_lifetimes/lifetimes1.rs | 29 +++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index d34f3abd3e..19e2d398b5 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -3,6 +3,7 @@ // 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? +// TODO: Fix the compiler error by updating the function signature. fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x @@ -12,9 +13,16 @@ fn longest(x: &str, y: &str) -> &str { } fn main() { - let string1 = String::from("abcd"); - let string2 = "xyz"; + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; - let result = longest(string1.as_str(), string2); - println!("The longest string is '{}'", result); + #[test] + fn test_longest() { + assert_eq!(longest("abcd", "123"), "abcd"); + assert_eq!(longest("abc", "1234"), "1234"); + } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index bed2eafa90..e2ebfa5c55 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -821,9 +821,8 @@ You may need this: [[exercises]] name = "lifetimes1" dir = "16_lifetimes" -test = false hint = """ -Let the compiler guide you. Also take a look at the book if you need help: +Let the compiler guide you. Also take a look at The Book if you need help: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs index 4e18198923..ca7b688da5 100644 --- a/solutions/16_lifetimes/lifetimes1.rs +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -1 +1,28 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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? + +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + // ^^^^ ^^ ^^ ^^ + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_longest() { + assert_eq!(longest("abcd", "123"), "abcd"); + assert_eq!(longest("abc", "1234"), "1234"); + } +} From 275a854d6ec71e4cdde9b4d1943a4dd6e3368ab6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 13:24:27 +0200 Subject: [PATCH 0980/1432] lifetimes2 solution --- exercises/16_lifetimes/lifetimes2.rs | 10 ++++---- solutions/16_lifetimes/lifetimes2.rs | 34 +++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs index 6e329e6d90..de5a5dfc79 100644 --- a/exercises/16_lifetimes/lifetimes2.rs +++ b/exercises/16_lifetimes/lifetimes2.rs @@ -1,6 +1,4 @@ -// So if the compiler is just validating the references passed to the annotated -// parameters and the return type, what do we need to change? - +// Don't change this function. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x @@ -10,11 +8,13 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { } fn main() { + // TODO: Fix the compiler error by moving one line. + let string1 = String::from("long string is long"); let result; { let string2 = String::from("xyz"); - result = longest(string1.as_str(), string2.as_str()); + result = longest(&string1, &string2); } - println!("The longest string is '{}'", result); + println!("The longest string is '{result}'"); } diff --git a/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs index 4e18198923..b0f2ef1fa3 100644 --- a/solutions/16_lifetimes/lifetimes2.rs +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -1 +1,33 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + let string1 = String::from("long string is long"); + // Solution1: You can move `strings2` out of the inner block so that it is + // not dropped before the print statement. + let string2 = String::from("xyz"); + let result; + { + result = longest(&string1, &string2); + } + println!("The longest string is '{result}'"); + // `string2` dropped at the end of the function. + + // ========================================================================= + + let string1 = String::from("long string is long"); + let result; + { + let string2 = String::from("xyz"); + result = longest(&string1, &string2); + // Solution2: You can move the print statement into the inner block so + // that it is executed before `string2` is dropped. + println!("The longest string is '{result}'"); + // `string2` dropped here (end of the inner scope). + } +} From 61872166067ab83fbad87e55c562e28d98368bff Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 16:15:53 +0200 Subject: [PATCH 0981/1432] lifetimes3 solution --- exercises/16_lifetimes/lifetimes3.rs | 7 +++---- rustlings-macros/info.toml | 4 +--- solutions/16_lifetimes/lifetimes3.rs | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs index 9b631cab42..1cc27592c6 100644 --- a/exercises/16_lifetimes/lifetimes3.rs +++ b/exercises/16_lifetimes/lifetimes3.rs @@ -1,16 +1,15 @@ // Lifetimes are also needed when structs hold references. +// TODO: Fix the compiler errors about the struct. struct Book { author: &str, title: &str, } fn main() { - let name = String::from("Jill Smith"); - let title = String::from("Fish Flying"); let book = Book { - author: &name, - title: &title, + author: "George Orwell", + title: "1984", }; println!("{} by {}", book.title, book.author); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index e2ebfa5c55..f0e34a5e5e 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -843,9 +843,7 @@ inner block: name = "lifetimes3" dir = "16_lifetimes" test = false -hint = """ -If you use a lifetime annotation in a struct's fields, where else does it need -to be added?""" +hint = """Let the compiler guide you :)""" # TESTS diff --git a/solutions/16_lifetimes/lifetimes3.rs b/solutions/16_lifetimes/lifetimes3.rs index 4e18198923..16a5a6840c 100644 --- a/solutions/16_lifetimes/lifetimes3.rs +++ b/solutions/16_lifetimes/lifetimes3.rs @@ -1 +1,18 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Lifetimes are also needed when structs hold references. + +struct Book<'a> { + // ^^^^ added a lifetime annotation + author: &'a str, + // ^^ + title: &'a str, + // ^^ +} + +fn main() { + let book = Book { + author: "George Orwell", + title: "1984", + }; + + println!("{} by {}", book.title, book.author); +} From a4f8826301c793180d94e891603fab22e9492f5c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 16:29:03 +0200 Subject: [PATCH 0982/1432] tests1 solution --- exercises/17_tests/tests1.rs | 15 ++++++++++----- rustlings-macros/info.toml | 3 --- solutions/17_tests/tests1.rs | 25 ++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index 854a135849..7529f9f049 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -1,9 +1,9 @@ // 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! +// do. + +fn is_even(n: i64) -> bool { + n % 2 == 0 +} fn main() { // You can optionally experiment here. @@ -11,8 +11,13 @@ fn main() { #[cfg(test)] mod tests { + // TODO: Import `is_even`. You can use a wildcard to import everything in + // the outer module. + #[test] fn you_can_assert() { + // TODO: Test the function `is_even` with some values. + assert!(); assert!(); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index f0e34a5e5e..27ed6b9cc6 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -851,9 +851,6 @@ hint = """Let the compiler guide you :)""" name = "tests1" dir = "17_tests" hint = """ -You don't even need to write any code to test -- you can just test values and -run that, even though you wouldn't do that in real life. :) - `assert!` is a macro that needs an argument. Depending on the value of the argument, `assert!` will do nothing (in which case the test will pass) or `assert!` will panic (in which case the test will fail). diff --git a/solutions/17_tests/tests1.rs b/solutions/17_tests/tests1.rs index 4e18198923..c52b8b16d7 100644 --- a/solutions/17_tests/tests1.rs +++ b/solutions/17_tests/tests1.rs @@ -1 +1,24 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Tests are important to ensure that your code does what you think it should +// do. + +fn is_even(n: i64) -> bool { + n % 2 == 0 +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + // When writing unit tests, it is common to import everything from the outer + // module (`super`) using a wildcard. + use super::*; + + #[test] + fn you_can_assert() { + assert!(is_even(0)); + assert!(!is_even(-1)); + // ^ You can assert `false` using the negation operator `!`. + } +} From 803e32dad2395d309b74b9fde6b9e08577cf8a0a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 16:40:26 +0200 Subject: [PATCH 0983/1432] tests2 solution --- exercises/17_tests/tests2.rs | 13 +++++++++++-- rustlings-macros/info.toml | 6 +----- solutions/17_tests/tests2.rs | 23 ++++++++++++++++++++++- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index f0899e1b96..0c6573e8f0 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -1,5 +1,8 @@ -// This test has a problem with it -- make the test compile! Make the test pass! -// Make the test fail! +// Calculates the power of 2 using a bit shift. +// `1 << n` is equivalent to "2 to the power of n". +fn power_of_2(n: u8) -> u64 { + 1 << n +} fn main() { // You can optionally experiment here. @@ -7,8 +10,14 @@ fn main() { #[cfg(test)] mod tests { + use super::*; + #[test] fn you_can_assert_eq() { + // TODO: Test the function `power_of_2` with some values. + assert_eq!(); + assert_eq!(); + assert_eq!(); assert_eq!(); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 27ed6b9cc6..4fd2bd85ab 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -862,13 +862,9 @@ ones pass, and which ones fail :)""" name = "tests2" dir = "17_tests" hint = """ -Like the previous exercise, you don't need to write any code to get this test -to compile and run. - `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two values that are equal! Try giving it two arguments that are different! -Try giving it two values that are of different types! Try switching which -argument comes first and which comes second!""" +Try switching which argument comes first and which comes second!""" [[exercises]] name = "tests3" diff --git a/solutions/17_tests/tests2.rs b/solutions/17_tests/tests2.rs index 4e18198923..39a0005e8e 100644 --- a/solutions/17_tests/tests2.rs +++ b/solutions/17_tests/tests2.rs @@ -1 +1,22 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Calculates the power of 2 using a bit shift. +// `1 << n` is equivalent to "2 to the power of n". +fn power_of_2(n: u8) -> u64 { + 1 << n +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn you_can_assert_eq() { + assert_eq!(power_of_2(0), 1); + assert_eq!(power_of_2(1), 2); + assert_eq!(power_of_2(2), 4); + assert_eq!(power_of_2(3), 8); + } +} From 746cf6863dee8f676596b07e74bad1a19fa2579e Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 27 Jun 2024 17:29:33 +0200 Subject: [PATCH 0984/1432] Remove tests3 and add solution to tests4 --- dev/Cargo.toml | 2 -- exercises/17_tests/tests3.rs | 41 +++++++++++++++++++++++++------- exercises/17_tests/tests4.rs | 45 ----------------------------------- rustlings-macros/info.toml | 19 +++++---------- solutions/17_tests/tests3.rs | 46 +++++++++++++++++++++++++++++++++++- solutions/17_tests/tests4.rs | 1 - 6 files changed, 83 insertions(+), 71 deletions(-) delete mode 100644 exercises/17_tests/tests4.rs delete mode 100644 solutions/17_tests/tests4.rs diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 2c5eaf0915..7f3acb5194 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -140,8 +140,6 @@ bin = [ { name = "tests2_sol", path = "../solutions/17_tests/tests2.rs" }, { name = "tests3", path = "../exercises/17_tests/tests3.rs" }, { name = "tests3_sol", path = "../solutions/17_tests/tests3.rs" }, - { name = "tests4", path = "../exercises/17_tests/tests4.rs" }, - { name = "tests4_sol", path = "../solutions/17_tests/tests4.rs" }, { name = "iterators1", path = "../exercises/18_iterators/iterators1.rs" }, { name = "iterators1_sol", path = "../solutions/18_iterators/iterators1.rs" }, { name = "iterators2", path = "../exercises/18_iterators/iterators2.rs" }, diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index d1cb48922e..9fc9318533 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -1,9 +1,19 @@ -// 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)`. +struct Rectangle { + width: i32, + height: i32, +} + +impl Rectangle { + // Don't change this function. + fn new(width: i32, height: i32) -> Self { + if width <= 0 || height <= 0 { + // Returning a `Result` would be better here. But we want to learn + // how to test functions that can panic. + panic!("Rectangle width and height can't be negative"); + } -fn is_even(num: i32) -> bool { - num % 2 == 0 + Rectangle { width, height } + } } fn main() { @@ -15,12 +25,25 @@ mod tests { use super::*; #[test] - fn is_true_when_even() { - assert!(); + fn correct_width_and_height() { + // TODO: This test should check if the rectangle has the size that we + // pass to its constructor. + let rect = Rectangle::new(10, 20); + assert_eq!(???, 10); // Check width + assert_eq!(???, 20); // Check height + } + + // TODO: This test should check if the program panics when we try to create + // a rectangle with negative width. + #[test] + fn negative_width() { + let _rect = Rectangle::new(-10, 10); } + // TODO: This test should check if the program panics when we try to create + // a rectangle with negative height. #[test] - fn is_false_when_odd() { - assert!(); + fn negative_height() { + let _rect = Rectangle::new(10, -10); } } diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs deleted file mode 100644 index 4303ed069e..0000000000 --- a/exercises/17_tests/tests4.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Make sure that we're testing for the correct conditions! - -struct Rectangle { - width: i32, - height: i32, -} - -impl Rectangle { - // Only change the test functions themselves - fn new(width: i32, height: i32) -> Self { - if width <= 0 || height <= 0 { - panic!("Rectangle width and height cannot be negative!") - } - Rectangle { width, height } - } -} - -fn main() { - // You can optionally experiment here. -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn correct_width_and_height() { - // This test should check if the rectangle is the size that we pass into its constructor - let rect = Rectangle::new(10, 20); - assert_eq!(???, 10); // check width - assert_eq!(???, 20); // check height - } - - #[test] - fn negative_width() { - // This test should check if program panics when we try to create rectangle with negative width - let _rect = Rectangle::new(-10, 10); - } - - #[test] - fn negative_height() { - // This test should check if program panics when we try to create rectangle with negative height - let _rect = Rectangle::new(10, -10); - } -} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4fd2bd85ab..5c24cd3329 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -856,7 +856,10 @@ argument, `assert!` will do nothing (in which case the test will pass) or `assert!` will panic (in which case the test will fail). So try giving different values to `assert!` and see which ones compile, which -ones pass, and which ones fail :)""" +ones pass, and which ones fail :) + +If you want to check for `false`, you can negate the result of what you're +checking using `!`, like `assert!(!…)`.""" [[exercises]] name = "tests2" @@ -870,19 +873,9 @@ Try switching which argument comes first and which comes second!""" name = "tests3" dir = "17_tests" hint = """ -You can call a function right where you're passing arguments to `assert!`. So -you could do something like `assert!(having_fun())`. - -If you want to check that you indeed get `false`, you can negate the result of -what you're doing using `!`, like `assert!(!having_fun())`.""" - -[[exercises]] -name = "tests4" -dir = "17_tests" -hint = """ -We expect method `Rectangle::new()` to panic for negative values. +We expect the method `Rectangle::new` to panic for negative values. -To handle that you need to add a special attribute to the test function. +To handle that, you need to add a special attribute to the test function. You can refer to the docs: https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs index 4e18198923..503f9bc0f9 100644 --- a/solutions/17_tests/tests3.rs +++ b/solutions/17_tests/tests3.rs @@ -1 +1,45 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +struct Rectangle { + width: i32, + height: i32, +} + +impl Rectangle { + // Don't change this function. + fn new(width: i32, height: i32) -> Self { + if width <= 0 || height <= 0 { + // Returning a `Result` would be better here. But we want to learn + // how to test functions that can panic. + panic!("Rectangle width and height can't be negative"); + } + + Rectangle { width, height } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn correct_width_and_height() { + let rect = Rectangle::new(10, 20); + assert_eq!(rect.width, 10); // Check width + assert_eq!(rect.height, 20); // Check height + } + + #[test] + #[should_panic] // Added this attribute to check that the test panics. + fn negative_width() { + let _rect = Rectangle::new(-10, 10); + } + + #[test] + #[should_panic] // Added this attribute to check that the test panics. + fn negative_height() { + let _rect = Rectangle::new(10, -10); + } +} diff --git a/solutions/17_tests/tests4.rs b/solutions/17_tests/tests4.rs deleted file mode 100644 index 4e18198923..0000000000 --- a/solutions/17_tests/tests4.rs +++ /dev/null @@ -1 +0,0 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° From cf9041c0e42120199e09e74e65c52d69c00db19c Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 02:07:56 +0200 Subject: [PATCH 0985/1432] iterators1 solution --- exercises/18_iterators/iterators1.rs | 21 +++++++++------------ rustlings-macros/info.toml | 15 +-------------- solutions/18_iterators/iterators1.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 52b704d500..86278a4923 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -1,8 +1,6 @@ // 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 fn main() { // You can optionally experiment here. @@ -10,19 +8,18 @@ fn main() { #[cfg(test)] mod tests { - use super::*; - #[test] fn iterators() { - let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; + let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; - let mut my_iterable_fav_fruits = ???; // TODO: Step 1 + // TODO: Create an iterator over the array. + let mut fav_fruits_iterator = ???; - 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!(fav_fruits_iterator.next(), Some(&"banana")); + assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` + assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); + assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` + assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); + assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5c24cd3329..5e93986781 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -886,22 +886,9 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa name = "iterators1" dir = "18_iterators" hint = """ -Step 1: - -We need to apply something to the collection `my_fav_fruits` before we start to -go through it. What could that be? Take a look at the struct definition for a -vector for inspiration: -https://doc.rust-lang.org/std/vec/struct.Vec.html - -Step 2 & step 3: - -Very similar to the lines above and below. You've got this! - -Step 4: - An iterator goes through all elements in a collection, but what if we've run out of elements? What should we expect here? If you're stuck, take a look at -https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.""" +https://doc.rust-lang.org/std/iter/trait.Iterator.html""" [[exercises]] name = "iterators2" diff --git a/solutions/18_iterators/iterators1.rs b/solutions/18_iterators/iterators1.rs index 4e18198923..93a6008aa5 100644 --- a/solutions/18_iterators/iterators1.rs +++ b/solutions/18_iterators/iterators1.rs @@ -1 +1,26 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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. + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + #[test] + fn iterators() { + let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; + + // Create an iterator over the array. + let mut fav_fruits_iterator = my_fav_fruits.iter(); + + assert_eq!(fav_fruits_iterator.next(), Some(&"banana")); + assert_eq!(fav_fruits_iterator.next(), Some(&"custard apple")); + assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); + assert_eq!(fav_fruits_iterator.next(), Some(&"peach")); + assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); + assert_eq!(fav_fruits_iterator.next(), None); + // ^^^^ reached the end + } +} From 4f71f74b444ab35e0de0c4bd9a01a7e438057c01 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 02:26:35 +0200 Subject: [PATCH 0986/1432] Use todo!() instead of ??? --- exercises/17_tests/tests3.rs | 4 ++-- exercises/18_iterators/iterators1.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index 9fc9318533..ec994792fb 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -29,8 +29,8 @@ mod tests { // TODO: This test should check if the rectangle has the size that we // pass to its constructor. let rect = Rectangle::new(10, 20); - assert_eq!(???, 10); // Check width - assert_eq!(???, 20); // Check height + assert_eq!(todo!(), 10); // Check width + assert_eq!(todo!(), 20); // Check height } // TODO: This test should check if the program panics when we try to create diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 86278a4923..ca937ed00e 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -13,13 +13,13 @@ mod tests { let my_fav_fruits = ["banana", "custard apple", "avocado", "peach", "raspberry"]; // TODO: Create an iterator over the array. - let mut fav_fruits_iterator = ???; + let mut fav_fruits_iterator = todo!(); assert_eq!(fav_fruits_iterator.next(), Some(&"banana")); - assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` + assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` assert_eq!(fav_fruits_iterator.next(), Some(&"avocado")); - assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` + assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` assert_eq!(fav_fruits_iterator.next(), Some(&"raspberry")); - assert_eq!(fav_fruits_iterator.next(), ???); // TODO: Replace `???` + assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()` } } From eddbb97934b8d358b4fd20cc3063cf4872e39567 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 02:48:21 +0200 Subject: [PATCH 0987/1432] iterators2 solution --- exercises/18_iterators/iterators2.rs | 23 +++++------ rustlings-macros/info.toml | 9 +++-- solutions/18_iterators/iterators2.rs | 57 +++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index 8d8909bfb2..5903e657e9 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -1,31 +1,28 @@ // In this exercise, you'll learn some of the unique advantages that iterators -// can offer. Follow the steps to complete the exercise. +// can offer. -// Step 1. -// Complete the `capitalize_first` function. +// TODO: Complete the `capitalize_first` function. // "hello" -> "Hello" fn capitalize_first(input: &str) -> String { - let mut c = input.chars(); - match c.next() { + let mut chars = input.chars(); + match chars.next() { None => String::new(), - Some(first) => ???, + Some(first) => todo!(), } } -// Step 2. -// Apply the `capitalize_first` function to a slice of string slices. +// TODO: Apply the `capitalize_first` function to a slice of string slices. // Return a vector of strings. // ["hello", "world"] -> ["Hello", "World"] fn capitalize_words_vector(words: &[&str]) -> Vec { - vec![] + // ??? } -// Step 3. -// Apply the `capitalize_first` function again to a slice of string slices. -// Return a single string. +// TODO: Apply the `capitalize_first` function again to a slice of string +// slices. Return a single string. // ["hello", " ", "world"] -> "Hello World" fn capitalize_words_string(words: &[&str]) -> String { - String::new() + // ??? } fn main() { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5e93986781..5a337888ad 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -894,7 +894,7 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html""" name = "iterators2" dir = "18_iterators" hint = """ -Step 1: +`capitalize_first`: The variable `first` is a `char`. It needs to be capitalized and added to the remaining characters in `c` in order to return the correct `String`. @@ -905,12 +905,15 @@ The remaining characters in `c` can be viewed as a string slice using the The documentation for `char` contains many useful methods. https://doc.rust-lang.org/std/primitive.char.html -Step 2: +Use `char::to_uppercase`. It returns an iterator that can be converted to a +`String`. + +`capitalize_words_vector`: Create an iterator from the slice. Transform the iterated values by applying the `capitalize_first` function. Remember to `collect` the iterator. -Step 3: +`capitalize_words_string`: This is surprisingly similar to the previous solution. `collect` is very powerful and very general. Rust just needs to know the desired type.""" diff --git a/solutions/18_iterators/iterators2.rs b/solutions/18_iterators/iterators2.rs index 4e18198923..db05f293d7 100644 --- a/solutions/18_iterators/iterators2.rs +++ b/solutions/18_iterators/iterators2.rs @@ -1 +1,56 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// In this exercise, you'll learn some of the unique advantages that iterators +// can offer. + +// "hello" -> "Hello" +fn capitalize_first(input: &str) -> String { + let mut chars = input.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().to_string() + chars.as_str(), + } +} + +// Apply the `capitalize_first` function to a slice of string slices. +// Return a vector of strings. +// ["hello", "world"] -> ["Hello", "World"] +fn capitalize_words_vector(words: &[&str]) -> Vec { + words.iter().map(|word| capitalize_first(word)).collect() +} + +// Apply the `capitalize_first` function again to a slice of string +// slices. Return a single string. +// ["hello", " ", "world"] -> "Hello World" +fn capitalize_words_string(words: &[&str]) -> String { + words.iter().map(|word| capitalize_first(word)).collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_success() { + assert_eq!(capitalize_first("hello"), "Hello"); + } + + #[test] + fn test_empty() { + assert_eq!(capitalize_first(""), ""); + } + + #[test] + fn test_iterate_string_vec() { + let words = vec!["hello", "world"]; + assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]); + } + + #[test] + fn test_iterate_into_string() { + let words = vec!["hello", " ", "world"]; + assert_eq!(capitalize_words_string(&words), "Hello World"); + } +} From 56a9197f55356a0a6503d6fa6cb2241d676bd051 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 15:00:13 +0200 Subject: [PATCH 0988/1432] iterators3 solution --- exercises/18_iterators/iterators3.rs | 55 ++++++--------------- rustlings-macros/info.toml | 6 +-- solutions/18_iterators/iterators3.rs | 74 +++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index dfe4014908..b5d05f6e5e 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,40 +1,26 @@ -// 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. - #[derive(Debug, PartialEq, Eq)] enum DivisionError { - NotDivisible(NotDivisibleError), DivideByZero, + NotDivisible, } -#[derive(Debug, PartialEq, Eq)] -struct NotDivisibleError { - dividend: i32, - divisor: i32, -} - -// Calculate `a` divided by `b` if `a` is evenly divisible by `b`. +// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // Otherwise, return a suitable error. fn divide(a: i32, b: i32) -> Result { todo!(); } -// Complete the function and return a value of the correct type so the test -// passes. -// Desired output: Ok([1, 11, 1426, 3]) -fn result_with_list() -> () { - let numbers = vec![27, 297, 38502, 81]; +// TODO: Add the correct return type and complete the function body. +// Desired output: `Ok([1, 11, 1426, 3])` +fn result_with_list() { + let numbers = [27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); } -// Complete the function and return a value of the correct type so the test -// passes. -// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)] -fn list_of_results() -> () { - let numbers = vec![27, 297, 38502, 81]; +// TODO: Add the correct return type and complete the function body. +// Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]` +fn list_of_results() { + let numbers = [27, 297, 38502, 81]; let division_results = numbers.into_iter().map(|n| divide(n, 27)); } @@ -52,19 +38,13 @@ mod tests { } #[test] - fn test_not_divisible() { - assert_eq!( - divide(81, 6), - Err(DivisionError::NotDivisible(NotDivisibleError { - dividend: 81, - divisor: 6 - })) - ); + fn test_divide_by_0() { + assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); } #[test] - fn test_divide_by_0() { - assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); + fn test_not_divisible() { + assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); } #[test] @@ -74,14 +54,11 @@ mod tests { #[test] fn test_result_with_list() { - assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])"); + assert_eq!(result_with_list().unwarp(), [1, 11, 1426, 3]); } #[test] fn test_list_of_results() { - assert_eq!( - format!("{:?}", list_of_results()), - "[Ok(1), Ok(11), Ok(1426), Ok(3)]" - ); + assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5a337888ad..8b1feb49d3 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -922,8 +922,8 @@ powerful and very general. Rust just needs to know the desired type.""" name = "iterators3" dir = "18_iterators" hint = """ -The `divide` function needs to return the correct error when even division is -not possible. +The `divide` function needs to return the correct error when the divisor is 0 or +when even division is not possible. The `division_results` variable needs to be collected into a collection type. @@ -934,7 +934,7 @@ The `list_of_results` function needs to return a vector of results. See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how the `FromIterator` trait is used in `collect()`. This trait is REALLY -powerful! It can make the solution to this exercise infinitely easier.""" +powerful! It can make the solution to this exercise much easier.""" [[exercises]] name = "iterators4" diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs index 4e18198923..d66d1ef3b5 100644 --- a/solutions/18_iterators/iterators3.rs +++ b/solutions/18_iterators/iterators3.rs @@ -1 +1,73 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +#[derive(Debug, PartialEq, Eq)] +enum DivisionError { + DivideByZero, + NotDivisible, +} + +fn divide(a: i64, b: i64) -> Result { + if b == 0 { + return Err(DivisionError::DivideByZero); + } + + if a % b != 0 { + return Err(DivisionError::NotDivisible); + } + + Ok(a / b) +} + +fn result_with_list() -> Result, DivisionError> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let numbers = [27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); + // Collects to the expected return type. Returns the first error in the + // division results (if one exists). + division_results.collect() +} + +fn list_of_results() -> Vec> { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let numbers = [27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); + // Collects to the expected return type. + division_results.collect() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_success() { + assert_eq!(divide(81, 9), Ok(9)); + } + + #[test] + fn test_divide_by_0() { + assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); + } + + #[test] + fn test_not_divisible() { + assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); + } + + #[test] + fn test_divide_0_by_something() { + assert_eq!(divide(0, 81), Ok(0)); + } + + #[test] + fn test_result_with_list() { + assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); + } + + #[test] + fn test_list_of_results() { + assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]); + } +} From 2af437fd901345f2613217cbf325718672d04100 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 15:31:15 +0200 Subject: [PATCH 0989/1432] iterators4 solution --- exercises/18_iterators/iterators4.rs | 14 +++--- rustlings-macros/info.toml | 4 +- solutions/18_iterators/iterators4.rs | 72 +++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index ae4d502d79..08ba3650de 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,9 +1,9 @@ -fn factorial(num: u64) -> u64 { - // Complete this function to return the factorial of num +fn factorial(num: u8) -> u64 { + // TODO: Complete this function to return the factorial of `num`. // Do not use: // - early returns (using the `return` keyword explicitly) // Try not to use: - // - imperative style loops (for, while) + // - imperative style loops (for/while) // - additional variables // For an extra challenge, don't use: // - recursion @@ -19,20 +19,20 @@ mod tests { #[test] fn factorial_of_0() { - assert_eq!(1, factorial(0)); + assert_eq!(factorial(0), 1); } #[test] fn factorial_of_1() { - assert_eq!(1, factorial(1)); + assert_eq!(factorial(1), 1); } #[test] fn factorial_of_2() { - assert_eq!(2, factorial(2)); + assert_eq!(factorial(2), 2); } #[test] fn factorial_of_4() { - assert_eq!(24, factorial(4)); + assert_eq!(factorial(4), 24); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 8b1feb49d3..72f956bfbe 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -942,10 +942,10 @@ dir = "18_iterators" hint = """ In an imperative language, you might write a `for` loop that updates a mutable variable. Or, you might write code utilizing recursion and a match clause. In -Rust you can take another functional approach, computing the factorial +Rust, you can take another functional approach, computing the factorial elegantly with ranges and iterators. -Hint 2: Check out the `fold` and `rfold` methods!""" +Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs index 4e18198923..4c3c49d962 100644 --- a/solutions/18_iterators/iterators4.rs +++ b/solutions/18_iterators/iterators4.rs @@ -1 +1,71 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 3 possible solutions are presented. + +// With `for` loop and a mutable variable. +fn factorial_for(num: u64) -> u64 { + let mut result = 1; + + for x in 2..=num { + result *= x; + } + + result +} + +// Equivalent to `factorial_for` but shorter and without a `for` loop and +// mutable variables. +fn factorial_fold(num: u64) -> u64 { + // Case num==0: The iterator 2..=0 is empty + // -> The initial value of `fold` is returned which is 1. + // Case num==1: The iterator 2..=1 is also empty + // -> The initial value 1 is returned. + // Case num==2: The iterator 2..=2 contains one element + // -> The initial value 1 is multiplied by 2 and the result + // is returned. + // Case num==3: The iterator 2..=3 contains 2 elements + // -> 1 * 2 is calculated, then the result 2 is multiplied by + // the second element 3 so the result 6 is returned. + // And so on… + (2..=num).fold(1, |acc, x| acc * x) +} + +// Equivalent to `factorial_fold` but with a built-in method that is suggested +// by Clippy. +fn factorial_product(num: u64) -> u64 { + (2..=num).product() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn factorial_of_0() { + assert_eq!(factorial_for(0), 1); + assert_eq!(factorial_fold(0), 1); + assert_eq!(factorial_product(0), 1); + } + + #[test] + fn factorial_of_1() { + assert_eq!(factorial_for(1), 1); + assert_eq!(factorial_fold(1), 1); + assert_eq!(factorial_product(1), 1); + } + #[test] + fn factorial_of_2() { + assert_eq!(factorial_for(2), 2); + assert_eq!(factorial_fold(2), 2); + assert_eq!(factorial_product(2), 2); + } + + #[test] + fn factorial_of_4() { + assert_eq!(factorial_for(4), 24); + assert_eq!(factorial_fold(4), 24); + assert_eq!(factorial_product(4), 24); + } +} From f53d4589205a5485011a341400eeea0ec3d6b339 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 16:11:14 +0200 Subject: [PATCH 0990/1432] iterators5 solution --- exercises/18_iterators/iterators5.rs | 104 +++++++++--------- solutions/18_iterators/iterators5.rs | 151 ++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 53 deletions(-) diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index 4f052d5129..7e434cc5f9 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -1,10 +1,8 @@ -// Let's define a simple model to track Rustlings exercise progress. Progress +// 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 // number of exercises with a given progress. Recreate this counting -// 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. +// functionality using iterators. Try to not use imperative loops (for/while). use std::collections::HashMap; @@ -18,24 +16,25 @@ enum Progress { fn count_for(map: &HashMap, value: Progress) -> usize { let mut count = 0; for val in map.values() { - if val == &value { + if *val == value { count += 1; } } count } +// TODO: Implement the functionality of `count_for` but with an iterator instead +// of a `for` loop. fn count_iterator(map: &HashMap, value: Progress) -> usize { - // map is a hashmap with String keys and Progress values. - // map = { "variables1": Complete, "from_str": None, ... } - todo!(); + // `map` is a hash map with `String` keys and `Progress` values. + // map = { "variables1": Complete, "from_str": None, … } } fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { let mut count = 0; for map in collection { for val in map.values() { - if val == &value { + if *val == value { count += 1; } } @@ -43,11 +42,12 @@ fn count_collection_for(collection: &[HashMap], value: Progres count } +// TODO: Implement the functionality of `count_collection_for` but with an +// iterator instead of a `for` loop. fn count_collection_iterator(collection: &[HashMap], value: Progress) -> usize { - // collection is a slice of hashmaps. - // collection = [{ "variables1": Complete, "from_str": None, ... }, - // { "variables2": Complete, ... }, ... ] - todo!(); + // `collection` is a slice of hash maps. + // collection = [{ "variables1": Complete, "from_str": None, … }, + // { "variables2": Complete, … }, … ] } fn main() { @@ -58,32 +58,61 @@ fn main() { mod tests { use super::*; + fn get_map() -> HashMap { + use Progress::*; + + let mut map = HashMap::new(); + map.insert(String::from("variables1"), Complete); + map.insert(String::from("functions1"), Complete); + map.insert(String::from("hashmap1"), Complete); + map.insert(String::from("arc1"), Some); + map.insert(String::from("as_ref_mut"), None); + map.insert(String::from("from_str"), None); + + map + } + + fn get_vec_map() -> Vec> { + use Progress::*; + + let map = get_map(); + + let mut other = HashMap::new(); + other.insert(String::from("variables2"), Complete); + other.insert(String::from("functions2"), Complete); + other.insert(String::from("if1"), Complete); + other.insert(String::from("from_into"), None); + other.insert(String::from("try_from_into"), None); + + vec![map, other] + } + #[test] fn count_complete() { let map = get_map(); - assert_eq!(3, count_iterator(&map, Progress::Complete)); + assert_eq!(count_iterator(&map, Progress::Complete), 3); } #[test] fn count_some() { let map = get_map(); - assert_eq!(1, count_iterator(&map, Progress::Some)); + assert_eq!(count_iterator(&map, Progress::Some), 1); } #[test] fn count_none() { let map = get_map(); - assert_eq!(2, count_iterator(&map, Progress::None)); + assert_eq!(count_iterator(&map, Progress::None), 2); } #[test] fn count_complete_equals_for() { let map = get_map(); - let progress_states = vec![Progress::Complete, Progress::Some, Progress::None]; + let progress_states = [Progress::Complete, Progress::Some, Progress::None]; for progress_state in progress_states { assert_eq!( count_for(&map, progress_state), - count_iterator(&map, progress_state) + count_iterator(&map, progress_state), ); } } @@ -92,62 +121,33 @@ mod tests { fn count_collection_complete() { let collection = get_vec_map(); assert_eq!( + count_collection_iterator(&collection, Progress::Complete), 6, - count_collection_iterator(&collection, Progress::Complete) ); } #[test] fn count_collection_some() { let collection = get_vec_map(); - assert_eq!(1, count_collection_iterator(&collection, Progress::Some)); + assert_eq!(count_collection_iterator(&collection, Progress::Some), 1); } #[test] fn count_collection_none() { let collection = get_vec_map(); - assert_eq!(4, count_collection_iterator(&collection, Progress::None)); + assert_eq!(count_collection_iterator(&collection, Progress::None), 4); } #[test] fn count_collection_equals_for() { - let progress_states = vec![Progress::Complete, Progress::Some, Progress::None]; let collection = get_vec_map(); + let progress_states = [Progress::Complete, Progress::Some, Progress::None]; for progress_state in progress_states { assert_eq!( count_collection_for(&collection, progress_state), - count_collection_iterator(&collection, progress_state) + count_collection_iterator(&collection, progress_state), ); } } - - fn get_map() -> HashMap { - use Progress::*; - - let mut map = HashMap::new(); - map.insert(String::from("variables1"), Complete); - map.insert(String::from("functions1"), Complete); - map.insert(String::from("hashmap1"), Complete); - map.insert(String::from("arc1"), Some); - map.insert(String::from("as_ref_mut"), None); - map.insert(String::from("from_str"), None); - - map - } - - fn get_vec_map() -> Vec> { - use Progress::*; - - let map = get_map(); - - let mut other = HashMap::new(); - other.insert(String::from("variables2"), Complete); - other.insert(String::from("functions2"), Complete); - other.insert(String::from("if1"), Complete); - other.insert(String::from("from_into"), None); - other.insert(String::from("try_from_into"), None); - - vec![map, other] - } } diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs index 4e18198923..402c81b0e2 100644 --- a/solutions/18_iterators/iterators5.rs +++ b/solutions/18_iterators/iterators5.rs @@ -1 +1,150 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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 +// number of exercises with a given progress. Recreate this counting +// functionality using iterators. Try to not use imperative loops (for/while). + +use std::collections::HashMap; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Progress { + None, + Some, + Complete, +} + +fn count_for(map: &HashMap, value: Progress) -> usize { + let mut count = 0; + for val in map.values() { + if *val == value { + count += 1; + } + } + count +} + +fn count_iterator(map: &HashMap, value: Progress) -> usize { + // `map` is a hash map with `String` keys and `Progress` values. + // map = { "variables1": Complete, "from_str": None, … } + map.values().filter(|val| **val == value).count() +} + +fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { + let mut count = 0; + for map in collection { + count += count_for(map, value); + } + count +} + +fn count_collection_iterator(collection: &[HashMap], value: Progress) -> usize { + // `collection` is a slice of hash maps. + // collection = [{ "variables1": Complete, "from_str": None, … }, + // { "variables2": Complete, … }, … ] + collection + .iter() + .map(|map| count_iterator(map, value)) + .sum() +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + fn get_map() -> HashMap { + use Progress::*; + + let mut map = HashMap::new(); + map.insert(String::from("variables1"), Complete); + map.insert(String::from("functions1"), Complete); + map.insert(String::from("hashmap1"), Complete); + map.insert(String::from("arc1"), Some); + map.insert(String::from("as_ref_mut"), None); + map.insert(String::from("from_str"), None); + + map + } + + fn get_vec_map() -> Vec> { + use Progress::*; + + let map = get_map(); + + let mut other = HashMap::new(); + other.insert(String::from("variables2"), Complete); + other.insert(String::from("functions2"), Complete); + other.insert(String::from("if1"), Complete); + other.insert(String::from("from_into"), None); + other.insert(String::from("try_from_into"), None); + + vec![map, other] + } + + #[test] + fn count_complete() { + let map = get_map(); + assert_eq!(count_iterator(&map, Progress::Complete), 3); + } + + #[test] + fn count_some() { + let map = get_map(); + assert_eq!(count_iterator(&map, Progress::Some), 1); + } + + #[test] + fn count_none() { + let map = get_map(); + assert_eq!(count_iterator(&map, Progress::None), 2); + } + + #[test] + fn count_complete_equals_for() { + let map = get_map(); + let progress_states = [Progress::Complete, Progress::Some, Progress::None]; + for progress_state in progress_states { + assert_eq!( + count_for(&map, progress_state), + count_iterator(&map, progress_state), + ); + } + } + + #[test] + fn count_collection_complete() { + let collection = get_vec_map(); + assert_eq!( + count_collection_iterator(&collection, Progress::Complete), + 6, + ); + } + + #[test] + fn count_collection_some() { + let collection = get_vec_map(); + assert_eq!(count_collection_iterator(&collection, Progress::Some), 1); + } + + #[test] + fn count_collection_none() { + let collection = get_vec_map(); + assert_eq!(count_collection_iterator(&collection, Progress::None), 4); + } + + #[test] + fn count_collection_equals_for() { + let collection = get_vec_map(); + let progress_states = [Progress::Complete, Progress::Some, Progress::None]; + + for progress_state in progress_states { + assert_eq!( + count_collection_for(&collection, progress_state), + count_collection_iterator(&collection, progress_state), + ); + } + } +} From 2dcf917fa1538cfaf0ddf4eeea4877c35c8285e7 Mon Sep 17 00:00:00 2001 From: "Yung Beef 4.2" <89395745+Yung-Beef@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:49:09 -0300 Subject: [PATCH 0991/1432] docs: clarifying quiz 2 instructions --- exercises/quiz2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 29925cafcd..f9ba953c10 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -14,7 +14,7 @@ // - Trim the string // - Append "bar" to the string a specified amount of times // The exact form of this will be: -// - The input is going to be a Vector of a 2-length tuple, +// - The input is going to be a Vector of 2-length tuples, // the first element is the string, the second one is the command. // - The output element is going to be a Vector of strings. // From 61c7eaed6251fb8a28b00ea97b22d1f1b778a72b Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 28 Jun 2024 21:24:35 +0200 Subject: [PATCH 0992/1432] box1 solution --- exercises/19_smart_pointers/box1.rs | 30 +++++++++--------- rustlings-macros/info.toml | 15 +++------ solutions/19_smart_pointers/box1.rs | 48 ++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index c8c2640da5..d70e1c3d39 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -4,45 +4,43 @@ // `Box` - a smart pointer used to store data on the heap, which also allows us // to wrap a recursive type. // -// The recursive type we're implementing in this exercise is the `cons list` - a +// The recursive type we're implementing in this exercise is the "cons list", a // data structure frequently found in functional programming languages. Each -// item in a cons list contains two elements: the value of the current item and +// item in a cons list contains two elements: The value of the current item and // the next item. The last item is a value called `Nil`. -// -// Step 1: use a `Box` in the enum definition to make the code compile -// Step 2: create both empty and non-empty cons lists by replacing `todo!()` -// -// Note: the tests should not be changed +// TODO: Use a `Box` in the enum definition to make the code compile. #[derive(PartialEq, Debug)] enum List { Cons(i32, List), Nil, } -fn main() { - println!("This is an empty cons list: {:?}", create_empty_list()); - println!( - "This is a non-empty cons list: {:?}", - create_non_empty_list() - ); -} - +// TODO: Create an empty cons list. fn create_empty_list() -> List { todo!() } +// TODO: Create a non-empty cons list. fn create_non_empty_list() -> List { todo!() } +fn main() { + println!("This is an empty cons list: {:?}", create_empty_list()); + println!( + "This is a non-empty cons list: {:?}", + create_non_empty_list(), + ); +} + #[cfg(test)] mod tests { use super::*; #[test] fn test_create_empty_list() { - assert_eq!(List::Nil, create_empty_list()); + assert_eq!(create_empty_list(), List::Nil); } #[test] diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 72f956bfbe..744ad089b8 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -969,21 +969,16 @@ a different method that could make your code more compact than using `fold`.""" name = "box1" dir = "19_smart_pointers" hint = """ -Step 1: - -The compiler's message should help: since we cannot store the value of the +The compiler's message should help: Since we cannot store the value of the actual type when working with recursive types, we need to store a reference (pointer) to its value. -We should, therefore, place our `List` inside a `Box`. More details in the book -here: https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2: +We should, therefore, place our `List` inside a `Box`. More details in The Book: +https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes -Creating an empty list should be fairly straightforward (hint: peek at the -assertions). +Creating an empty list should be fairly straightforward (Hint: Read the tests). -For a non-empty list keep in mind that we want to use our `Cons` "list builder". +For a non-empty list, keep in mind that we want to use our `Cons` list builder. Although the current list is one of integers (`i32`), feel free to change the definition and try other types!""" diff --git a/solutions/19_smart_pointers/box1.rs b/solutions/19_smart_pointers/box1.rs index 4e18198923..189cc5623a 100644 --- a/solutions/19_smart_pointers/box1.rs +++ b/solutions/19_smart_pointers/box1.rs @@ -1 +1,47 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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 +// `Box` - a smart pointer used to store data on the heap, which also allows us +// to wrap a recursive type. +// +// The recursive type we're implementing in this exercise is the "cons list", a +// data structure frequently found in functional programming languages. Each +// item in a cons list contains two elements: The value of the current item and +// the next item. The last item is a value called `Nil`. + +#[derive(PartialEq, Debug)] +enum List { + Cons(i32, Box), + Nil, +} + +fn create_empty_list() -> List { + List::Nil +} + +fn create_non_empty_list() -> List { + List::Cons(42, Box::new(List::Nil)) +} + +fn main() { + println!("This is an empty cons list: {:?}", create_empty_list()); + println!( + "This is a non-empty cons list: {:?}", + create_non_empty_list(), + ); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_empty_list() { + assert_eq!(create_empty_list(), List::Nil); + } + + #[test] + fn test_create_non_empty_list() { + assert_ne!(create_empty_list(), create_non_empty_list()); + } +} From f3842aa746aa77a3fdf0f699951cde0d49f042c4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 29 Jun 2024 01:20:59 +0200 Subject: [PATCH 0993/1432] rc1 solution --- exercises/19_smart_pointers/rc1.rs | 21 +++--- rustlings-macros/info.toml | 4 +- solutions/19_smart_pointers/rc1.rs | 105 ++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 15 deletions(-) diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index 19de3db277..ecd3438701 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -1,15 +1,12 @@ // In this exercise, we want to express the concept of multiple owners via the -// Rc type. This is a model of our solar system - there is a Sun type and -// multiple Planets. The Planets take ownership of the sun, indicating that they -// revolve around the sun. -// -// Make this code compile by using the proper Rc primitives to express that the -// sun has multiple owners. +// `Rc` type. This is a model of our solar system - there is a `Sun` type and +// multiple `Planet`s. The planets take ownership of the sun, indicating that +// they revolve around the sun. use std::rc::Rc; #[derive(Debug)] -struct Sun {} +struct Sun; #[derive(Debug)] enum Planet { @@ -25,7 +22,7 @@ enum Planet { impl Planet { fn details(&self) { - println!("Hi from {:?}!", self) + println!("Hi from {self:?}!"); } } @@ -39,7 +36,7 @@ mod tests { #[test] fn rc1() { - let sun = Rc::new(Sun {}); + let sun = Rc::new(Sun); println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference let mercury = Planet::Mercury(Rc::clone(&sun)); @@ -63,17 +60,17 @@ mod tests { jupiter.details(); // TODO - let saturn = Planet::Saturn(Rc::new(Sun {})); + let saturn = Planet::Saturn(Rc::new(Sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 7 references saturn.details(); // TODO - let uranus = Planet::Uranus(Rc::new(Sun {})); + let uranus = Planet::Uranus(Rc::new(Sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 8 references uranus.details(); // TODO - let neptune = Planet::Neptune(Rc::new(Sun {})); + let neptune = Planet::Neptune(Rc::new(Sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 9 references neptune.details(); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 744ad089b8..5b3f781cdf 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -993,11 +993,11 @@ of the `Sun`. After using `drop()` to move the `Planet`s out of scope individually, the reference count goes down. -In the end the `Sun` only has one reference again, to itself. +In the end, the `Sun` only has one reference again, to itself. See more at: https://doc.rust-lang.org/book/ch15-04-rc.html -* Unfortunately Pluto is no longer considered a planet :(""" +Unfortunately, Pluto is no longer considered a planet :(""" [[exercises]] name = "arc1" diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs index 4e18198923..c0a41abfa8 100644 --- a/solutions/19_smart_pointers/rc1.rs +++ b/solutions/19_smart_pointers/rc1.rs @@ -1 +1,104 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// In this exercise, we want to express the concept of multiple owners via the +// `Rc` type. This is a model of our solar system - there is a `Sun` type and +// multiple `Planet`s. The planets take ownership of the sun, indicating that +// they revolve around the sun. + +use std::rc::Rc; + +#[derive(Debug)] +struct Sun; + +#[derive(Debug)] +enum Planet { + Mercury(Rc), + Venus(Rc), + Earth(Rc), + Mars(Rc), + Jupiter(Rc), + Saturn(Rc), + Uranus(Rc), + Neptune(Rc), +} + +impl Planet { + fn details(&self) { + println!("Hi from {self:?}!"); + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rc1() { + let sun = Rc::new(Sun); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + let mercury = Planet::Mercury(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + mercury.details(); + + let venus = Planet::Venus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + venus.details(); + + let earth = Planet::Earth(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + earth.details(); + + let mars = Planet::Mars(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + mars.details(); + + let jupiter = Planet::Jupiter(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + jupiter.details(); + + let saturn = Planet::Saturn(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + saturn.details(); + + // TODO + let uranus = Planet::Uranus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + uranus.details(); + + // TODO + let neptune = Planet::Neptune(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 9 references + neptune.details(); + + assert_eq!(Rc::strong_count(&sun), 9); + + drop(neptune); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + + drop(uranus); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + + drop(saturn); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + + drop(jupiter); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + + drop(mars); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + + drop(earth); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + + drop(venus); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + + drop(mercury); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + assert_eq!(Rc::strong_count(&sun), 1); + } +} From a943f5ba32412cf5b8fdd8665c1082ecab3ec545 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 29 Jun 2024 01:48:00 +0200 Subject: [PATCH 0994/1432] arc1 solution --- exercises/19_smart_pointers/arc1.rs | 55 +++++++++++++++-------------- rustlings-macros/info.toml | 2 +- solutions/19_smart_pointers/arc1.rs | 43 +++++++++++++++++++++- 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index 7b31fa82aa..c3d714dc30 100644 --- a/exercises/19_smart_pointers/arc1.rs +++ b/exercises/19_smart_pointers/arc1.rs @@ -1,39 +1,42 @@ -// 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 -// going to get the sum of every eighth value, with an offset. +// In this exercise, we are given a `Vec` of u32 called `numbers` with values +// ranging from 0 to 99. We would like to use this set of numbers within 8 +// different threads simultaneously. Each thread is going to get the sum of +// every eighth value with an offset. // -// The first thread (offset 0), will sum 0, 8, 16, ... -// The second thread (offset 1), will sum 1, 9, 17, ... -// The third thread (offset 2), will sum 2, 10, 18, ... -// ... -// The eighth thread (offset 7), will sum 7, 15, 23, ... +// The first thread (offset 0), will sum 0, 8, 16, … +// The second thread (offset 1), will sum 1, 9, 17, … +// The third thread (offset 2), will sum 2, 10, 18, … +// … +// The eighth thread (offset 7), will sum 7, 15, 23, … // -// Because we are using threads, our values need to be thread-safe. Therefore, -// we are using Arc. We need to make a change in each of the two TODOs. -// -// Make this code compile by filling in a value for `shared_numbers` where the -// 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! +// Because we are using threads, our values need to be thread-safe. Therefore, +// we are using `Arc`. -#![forbid(unused_imports)] // Do not change this, (or the next) line. -use std::sync::Arc; -use std::thread; +// Don't change the lines below. +#![forbid(unused_imports)] +use std::{sync::Arc, thread}; fn main() { let numbers: Vec<_> = (0..100u32).collect(); - let shared_numbers = // TODO - let mut joinhandles = Vec::new(); + + // TODO: Define `shared_numbers` by using `Arc`. + // let shared_numbers = ???; + + let mut join_handles = Vec::new(); for offset in 0..8 { - let child_numbers = // TODO - joinhandles.push(thread::spawn(move || { + // TODO: Define `child_numbers` using `shared_numbers`. + // let child_numbers = ???; + + let handle = thread::spawn(move || { let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); - println!("Sum of offset {} is {}", offset, sum); - })); + println!("Sum of offset {offset} is {sum}"); + }); + + join_handles.push(handle); } - for handle in joinhandles.into_iter() { + + for handle in join_handles.into_iter() { handle.join().unwrap(); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 5b3f781cdf..23b6181e9c 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1004,7 +1004,7 @@ name = "arc1" dir = "19_smart_pointers" test = false hint = """ -Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order +Make `shared_numbers` be an `Arc` from the `numbers` vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` inside the loop but still in the main thread. diff --git a/solutions/19_smart_pointers/arc1.rs b/solutions/19_smart_pointers/arc1.rs index 4e18198923..a520dfe6af 100644 --- a/solutions/19_smart_pointers/arc1.rs +++ b/solutions/19_smart_pointers/arc1.rs @@ -1 +1,42 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// In this exercise, we are given a `Vec` of u32 called `numbers` with values +// ranging from 0 to 99. We would like to use this set of numbers within 8 +// different threads simultaneously. Each thread is going to get the sum of +// every eighth value with an offset. +// +// The first thread (offset 0), will sum 0, 8, 16, … +// The second thread (offset 1), will sum 1, 9, 17, … +// The third thread (offset 2), will sum 2, 10, 18, … +// … +// The eighth thread (offset 7), will sum 7, 15, 23, … +// +// Because we are using threads, our values need to be thread-safe. Therefore, +// we are using `Arc`. + +// Don't change the lines below. +#![forbid(unused_imports)] +use std::{sync::Arc, thread}; + +fn main() { + let numbers: Vec<_> = (0..100u32).collect(); + + let shared_numbers = Arc::new(numbers); + // ^^^^^^^^^^^^^^^^^ + + let mut join_handles = Vec::new(); + + for offset in 0..8 { + let child_numbers = Arc::clone(&shared_numbers); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + let handle = thread::spawn(move || { + let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum(); + println!("Sum of offset {offset} is {sum}"); + }); + + join_handles.push(handle); + } + + for handle in join_handles.into_iter() { + handle.join().unwrap(); + } +} From 663a03a17b2d2001f4f3f35a59cd2e2aa5f2bb24 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 29 Jun 2024 02:07:56 +0200 Subject: [PATCH 0995/1432] cow1 solution --- exercises/19_smart_pointers/cow1.rs | 78 +++++++++++++---------------- rustlings-macros/info.toml | 6 +-- solutions/19_smart_pointers/cow1.rs | 69 ++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 47 deletions(-) diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index 754c0bac92..5ecf8482e8 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -1,24 +1,18 @@ -// 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 -// required. The type is designed to work with general borrowed data via the -// Borrow trait. -// -// 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. +// This exercise explores the `Cow` (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 required. The type is designed to work +// with general borrowed data via the `Borrow` trait. use std::borrow::Cow; -fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { - for i in 0..input.len() { - let v = input[i]; - if v < 0 { +fn abs_all(input: &mut Cow<[i32]>) { + for ind in 0..input.len() { + let value = input[ind]; + if value < 0 { // Clones into a vector if not already owned. - input.to_mut()[i] = -v; + input.to_mut()[ind] = -value; } } - input } fn main() { @@ -30,47 +24,45 @@ mod tests { use super::*; #[test] - fn reference_mutation() -> Result<(), &'static str> { + fn reference_mutation() { // Clone occurs because `input` needs to be mutated. - let slice = [-1, 0, 1]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - Cow::Owned(_) => Ok(()), - _ => Err("Expected owned value"), - } + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); } #[test] - fn reference_no_mutation() -> Result<(), &'static str> { + fn reference_no_mutation() { // No clone occurs because `input` doesn't need to be mutated. - let slice = [0, 1, 2]; - let mut input = Cow::from(&slice[..]); - match abs_all(&mut input) { - // TODO - } + let vec = vec![0, 1, 2]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } #[test] - fn owned_no_mutation() -> Result<(), &'static str> { - // We can also pass `slice` without `&` so Cow owns it directly. In this - // case no mutation occurs and thus also no clone, but the result is + fn owned_no_mutation() { + // We can also pass `vec` without `&` so `Cow` owns it directly. In this + // case, no mutation occurs and thus also no clone. But the result is // still owned because it was never borrowed or mutated. - let slice = vec![0, 1, 2]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } + let vec = vec![0, 1, 2]; + let mut input = Cow::from(vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } #[test] - fn owned_mutation() -> Result<(), &'static str> { + fn owned_mutation() { // Of course this is also the case if a mutation does occur. In this - // case the call to `to_mut()` in the abs_all() function returns a + // case, the call to `to_mut()` in the `abs_all` function returns a // reference to the same data as before. - let slice = vec![-1, 0, 1]; - let mut input = Cow::from(slice); - match abs_all(&mut input) { - // TODO - } + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(vec); + abs_all(&mut input); + // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`. + assert!(matches!(input, todo!())); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 23b6181e9c..cacdad9095 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1020,11 +1020,11 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html""" name = "cow1" dir = "19_smart_pointers" hint = """ -If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is +If `Cow` already owns the data, it doesn't need to clone it when `to_mut()` is called. -Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation -on the `Cow` type.""" +Check out the documentation of the `Cow` type: +https://doc.rust-lang.org/std/borrow/enum.Cow.html""" # THREADS diff --git a/solutions/19_smart_pointers/cow1.rs b/solutions/19_smart_pointers/cow1.rs index 4e18198923..0a21a91b12 100644 --- a/solutions/19_smart_pointers/cow1.rs +++ b/solutions/19_smart_pointers/cow1.rs @@ -1 +1,68 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// This exercise explores the `Cow` (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 required. The type is designed to work +// with general borrowed data via the `Borrow` trait. + +use std::borrow::Cow; + +fn abs_all(input: &mut Cow<[i32]>) { + for ind in 0..input.len() { + let value = input[ind]; + if value < 0 { + // Clones into a vector if not already owned. + input.to_mut()[ind] = -value; + } + } +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reference_mutation() { + // Clone occurs because `input` needs to be mutated. + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); + } + + #[test] + fn reference_no_mutation() { + // No clone occurs because `input` doesn't need to be mutated. + let vec = vec![0, 1, 2]; + let mut input = Cow::from(&vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Borrowed(_))); + // ^^^^^^^^^^^^^^^^ + } + + #[test] + fn owned_no_mutation() { + // We can also pass `vec` without `&` so `Cow` owns it directly. In this + // case, no mutation occurs and thus also no clone. But the result is + // still owned because it was never borrowed or mutated. + let vec = vec![0, 1, 2]; + let mut input = Cow::from(vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); + // ^^^^^^^^^^^^^ + } + + #[test] + fn owned_mutation() { + // Of course this is also the case if a mutation does occur. In this + // case, the call to `to_mut()` in the `abs_all` function returns a + // reference to the same data as before. + let vec = vec![-1, 0, 1]; + let mut input = Cow::from(vec); + abs_all(&mut input); + assert!(matches!(input, Cow::Owned(_))); + // ^^^^^^^^^^^^^ + } +} From b000164eedaf5ada18ce0562aa9b7aed25663458 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 10:59:33 +0200 Subject: [PATCH 0996/1432] threads1 solution --- exercises/20_threads/threads1.rs | 24 +++++++++++--------- rustlings-macros/info.toml | 2 +- solutions/20_threads/threads1.rs | 38 +++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index bf0b8e0d83..01f9ff44cf 100644 --- a/exercises/20_threads/threads1.rs +++ b/exercises/20_threads/threads1.rs @@ -3,31 +3,35 @@ // wait until all the spawned threads have finished and should collect their // return values into a vector. -use std::thread; -use std::time::{Duration, Instant}; +use std::{ + thread, + time::{Duration, Instant}, +}; fn main() { - let mut handles = vec![]; + let mut handles = Vec::new(); for i in 0..10 { - handles.push(thread::spawn(move || { + let handle = thread::spawn(move || { let start = Instant::now(); thread::sleep(Duration::from_millis(250)); - println!("thread {} is complete", i); + println!("Thread {i} done"); start.elapsed().as_millis() - })); + }); + handles.push(handle); } - let mut results: Vec = vec![]; + let mut results = Vec::new(); for handle in handles { - // TODO: a struct is returned from thread::spawn, can you use it? + // TODO: Collect the results of all threads into the `results` vector. + // Use the `JoinHandle` struct which is returned by `thread::spawn`. } if results.len() != 10 { - panic!("Oh no! All the spawned threads did not finish!"); + panic!("Oh no! Some thread isn't done yet!"); } println!(); for (i, result) in results.into_iter().enumerate() { - println!("thread {} took {}ms", i, result); + println!("Thread {i} took {result}ms"); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index cacdad9095..37afa178ed 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1037,7 +1037,7 @@ hint = """ https://doc.rust-lang.org/std/thread/fn.spawn.html A challenge with multi-threaded applications is that the main thread can -finish before the spawned threads are completed. +finish before the spawned threads are done. https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles Use the `JoinHandle`s to wait for each thread to finish and collect their diff --git a/solutions/20_threads/threads1.rs b/solutions/20_threads/threads1.rs index 4e18198923..7f3dd29a1a 100644 --- a/solutions/20_threads/threads1.rs +++ b/solutions/20_threads/threads1.rs @@ -1 +1,37 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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. + +use std::{ + thread, + time::{Duration, Instant}, +}; + +fn main() { + let mut handles = Vec::new(); + for i in 0..10 { + let handle = thread::spawn(move || { + let start = Instant::now(); + thread::sleep(Duration::from_millis(250)); + println!("Thread {i} done"); + start.elapsed().as_millis() + }); + handles.push(handle); + } + + let mut results = Vec::new(); + for handle in handles { + // Collect the results of all threads into the `results` vector. + results.push(handle.join().unwrap()); + } + + if results.len() != 10 { + panic!("Oh no! Some thread isn't done yet!"); + } + + println!(); + for (i, result) in results.into_iter().enumerate() { + println!("Thread {i} took {result}ms"); + } +} From dfa2b44f718906dfac54816bb582880066c3dff0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:11:11 +0200 Subject: [PATCH 0997/1432] threads2 solution --- exercises/20_threads/threads2.rs | 27 ++++++++++---------- rustlings-macros/info.toml | 10 ++++---- solutions/20_threads/threads2.rs | 42 +++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs index 2bdeba94e7..7020cb9cea 100644 --- a/exercises/20_threads/threads2.rs +++ b/exercises/20_threads/threads2.rs @@ -1,35 +1,34 @@ // 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 +// work. But this time, the spawned threads need to be in charge of updating a +// shared value: `JobStatus.jobs_done` -use std::sync::Arc; -use std::thread; -use std::time::Duration; +use std::{sync::Arc, thread, time::Duration}; struct JobStatus { - jobs_completed: u32, + jobs_done: u32, } fn main() { - // TODO: `Arc` isn't enough if you want a **mutable** shared state - let status = Arc::new(JobStatus { jobs_completed: 0 }); + // TODO: `Arc` isn't enough if you want a **mutable** shared state. + let status = Arc::new(JobStatus { jobs_done: 0 }); - let mut handles = vec![]; + let mut handles = Vec::new(); for _ in 0..10 { let status_shared = Arc::clone(&status); let handle = thread::spawn(move || { thread::sleep(Duration::from_millis(250)); - // TODO: You must take an action before you update a shared value - status_shared.jobs_completed += 1; + + // TODO: You must take an action before you update a shared value. + status_shared.jobs_done += 1; }); handles.push(handle); } - // Waiting for all jobs to complete + // Waiting for all jobs to complete. for handle in handles { handle.join().unwrap(); } - // TODO: Print the value of `JobStatus.jobs_completed` - println!("Jobs completed: {}", ???); + // TODO: Print the value of `JobStatus.jobs_done`. + println!("Jobs done: {}", todo!()); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 37afa178ed..ab8c121571 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1051,19 +1051,19 @@ dir = "20_threads" test = false hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access -to **immutable** data. But we want to *change* the number of `jobs_completed` -so we'll need to also use another type that will only allow one thread to -mutate the data at a time. Take a look at this section of the book: +to **immutable** data. But we want to *change* the number of `jobs_done` so +we'll need to also use another type that will only allow one thread to mutate +the data at a time. Take a look at this section of the book: https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct Keep reading if you'd like more hints :) Do you now have an `Arc>` at the beginning of `main`? Like: ``` -let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 })); +let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 })); ``` -Similar to the code in the following example in the book: +Similar to the code in the following example in The Book: https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads""" [[exercises]] diff --git a/solutions/20_threads/threads2.rs b/solutions/20_threads/threads2.rs index 4e18198923..bc268d63f6 100644 --- a/solutions/20_threads/threads2.rs +++ b/solutions/20_threads/threads2.rs @@ -1 +1,41 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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_done` + +use std::{ + sync::{Arc, Mutex}, + thread, + time::Duration, +}; + +struct JobStatus { + jobs_done: u32, +} + +fn main() { + // `Arc` isn't enough if you want a **mutable** shared state. + // We need to wrap the value with a `Mutex`. + let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 })); + // ^^^^^^^^^^^ ^ + + let mut handles = Vec::new(); + for _ in 0..10 { + let status_shared = Arc::clone(&status); + let handle = thread::spawn(move || { + thread::sleep(Duration::from_millis(250)); + + // Lock before you update a shared value. + status_shared.lock().unwrap().jobs_done += 1; + // ^^^^^^^^^^^^^^^^ + }); + handles.push(handle); + } + + // Waiting for all jobs to complete. + for handle in handles { + handle.join().unwrap(); + } + + println!("Jobs done: {}", status.lock().unwrap().jobs_done); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} From a13e3cd07f86e8668a326bae98568cced61f5015 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:23:40 +0200 Subject: [PATCH 0998/1432] threads3 solution --- exercises/20_threads/threads3.rs | 23 ++++++----- rustlings-macros/info.toml | 5 ++- solutions/20_threads/threads3.rs | 67 +++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 37810cf9e4..30ac8ddd55 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -1,7 +1,4 @@ -use std::sync::mpsc; -use std::sync::Arc; -use std::thread; -use std::time::Duration; +use std::{sync::mpsc, thread, time::Duration}; struct Queue { length: u32, @@ -11,7 +8,7 @@ struct Queue { impl Queue { fn new() -> Self { - Queue { + Self { length: 10, first_half: vec![1, 2, 3, 4, 5], second_half: vec![6, 7, 8, 9, 10], @@ -19,20 +16,22 @@ impl Queue { } } -fn send_tx(q: Queue, tx: mpsc::Sender) -> () { +fn send_tx(q: Queue, tx: mpsc::Sender) { + // TODO: We want to send `tx` to both threads. But currently, it is moved + // into the frist thread. How could you solve this problem? thread::spawn(move || { for val in q.first_half { - println!("sending {:?}", val); + println!("Sending {val:?}"); tx.send(val).unwrap(); - thread::sleep(Duration::from_secs(1)); + thread::sleep(Duration::from_millis(250)); } }); thread::spawn(move || { for val in q.second_half { - println!("sending {:?}", val); + println!("Sending {val:?}"); tx.send(val).unwrap(); - thread::sleep(Duration::from_secs(1)); + thread::sleep(Duration::from_millis(250)); } }); } @@ -55,11 +54,11 @@ mod tests { let mut total_received: u32 = 0; for received in rx { - println!("Got: {}", received); + println!("Got: {received}"); total_received += 1; } - println!("total numbers received: {}", total_received); + println!("Number of received values: {total_received}"); assert_eq!(total_received, queue_length); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index ab8c121571..24dcdee263 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1076,10 +1076,11 @@ An alternate way to handle concurrency between threads is to use an `mpsc` With both a sending end and a receiving end, it's possible to send values in one thread and receive them in another. -Multiple producers are possible by using clone() to create a duplicate of the +Multiple producers are possible by using `clone()` to create a duplicate of the original sending end. -See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info.""" +Related section in The Book: +https://doc.rust-lang.org/book/ch16-02-message-passing.html""" # MACROS diff --git a/solutions/20_threads/threads3.rs b/solutions/20_threads/threads3.rs index 4e18198923..cd2dfbe39b 100644 --- a/solutions/20_threads/threads3.rs +++ b/solutions/20_threads/threads3.rs @@ -1 +1,66 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +use std::{sync::mpsc, thread, time::Duration}; + +struct Queue { + length: u32, + first_half: Vec, + second_half: Vec, +} + +impl Queue { + fn new() -> Self { + Self { + length: 10, + first_half: vec![1, 2, 3, 4, 5], + second_half: vec![6, 7, 8, 9, 10], + } + } +} + +fn send_tx(q: Queue, tx: mpsc::Sender) { + // Clone the sender `tx` first. + let tx_clone = tx.clone(); + thread::spawn(move || { + for val in q.first_half { + println!("Sending {val:?}"); + // Then use the clone in the first thread. This means that + // `tx_clone` is moved to the first thread and `tx` to the second. + tx_clone.send(val).unwrap(); + thread::sleep(Duration::from_millis(250)); + } + }); + + thread::spawn(move || { + for val in q.second_half { + println!("Sending {val:?}"); + tx.send(val).unwrap(); + thread::sleep(Duration::from_millis(250)); + } + }); +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn threads3() { + let (tx, rx) = mpsc::channel(); + let queue = Queue::new(); + let queue_length = queue.length; + + send_tx(queue, tx); + + let mut total_received: u32 = 0; + for received in rx { + println!("Got: {received}"); + total_received += 1; + } + + println!("Number of received values: {total_received}"); + assert_eq!(total_received, queue_length); + } +} From cf90364fd779255074eac9a7d90c53ad657936ba Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:28:38 +0200 Subject: [PATCH 0999/1432] macros1 solution --- exercises/21_macros/macros1.rs | 1 + rustlings-macros/info.toml | 5 ++--- solutions/21_macros/macros1.rs | 11 ++++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs index 1d415cb161..fb3c3ff9b3 100644 --- a/exercises/21_macros/macros1.rs +++ b/exercises/21_macros/macros1.rs @@ -5,5 +5,6 @@ macro_rules! my_macro { } fn main() { + // TODO: Fix the macro call. my_macro(); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 24dcdee263..7dcf3441df 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1089,9 +1089,8 @@ name = "macros1" dir = "21_macros" test = false hint = """ -When you call a macro, you need to add something special compared to a -regular function call. If you're stuck, take a look at what's inside -`my_macro`.""" +When you call a macro, you need to add something special compared to a regular +function call.""" [[exercises]] name = "macros2" diff --git a/solutions/21_macros/macros1.rs b/solutions/21_macros/macros1.rs index 4e18198923..1b861564be 100644 --- a/solutions/21_macros/macros1.rs +++ b/solutions/21_macros/macros1.rs @@ -1 +1,10 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +macro_rules! my_macro { + () => { + println!("Check out my macro!"); + }; +} + +fn main() { + my_macro!(); + // ^ +} From 9845e046de6f9569519d0e0ae3c50341eb35a8bf Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:31:37 +0200 Subject: [PATCH 1000/1432] macros2 solution --- exercises/21_macros/macros2.rs | 1 + solutions/21_macros/macros2.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/exercises/21_macros/macros2.rs b/exercises/21_macros/macros2.rs index f16712be6d..2d9dec76ca 100644 --- a/exercises/21_macros/macros2.rs +++ b/exercises/21_macros/macros2.rs @@ -2,6 +2,7 @@ fn main() { my_macro!(); } +// TODO: Fix the compiler error by moving the whole definition of this macro. macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/solutions/21_macros/macros2.rs b/solutions/21_macros/macros2.rs index 4e18198923..b6fd5d2c56 100644 --- a/solutions/21_macros/macros2.rs +++ b/solutions/21_macros/macros2.rs @@ -1 +1,10 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Moved the macro definition to be before its call. +macro_rules! my_macro { + () => { + println!("Check out my macro!"); + }; +} + +fn main() { + my_macro!(); +} From 4cb15a4cda4791134a75a0462031b5e86b45fa0d Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:37:48 +0200 Subject: [PATCH 1001/1432] macros3 solution --- exercises/21_macros/macros3.rs | 4 ++-- rustlings-macros/info.toml | 5 +---- solutions/21_macros/macros3.rs | 14 +++++++++++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/exercises/21_macros/macros3.rs b/exercises/21_macros/macros3.rs index 405c397aab..95374948be 100644 --- a/exercises/21_macros/macros3.rs +++ b/exercises/21_macros/macros3.rs @@ -1,5 +1,5 @@ -// Make me compile, without taking the macro out of the module! - +// TODO: Fix the compiler error without taking the macro definition out of this +// module. mod macros { macro_rules! my_macro { () => { diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 7dcf3441df..0ec5fb21ab 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1109,10 +1109,7 @@ dir = "21_macros" test = false hint = """ In order to use a macro outside of its module, you need to do something -special to the module to lift the macro out into its parent. - -The same trick also works on "extern crate" statements for crates that have -exported macros, if you've seen any of those around.""" +special to the module to lift the macro out into its parent.""" [[exercises]] name = "macros4" diff --git a/solutions/21_macros/macros3.rs b/solutions/21_macros/macros3.rs index 4e18198923..df35be4d9a 100644 --- a/solutions/21_macros/macros3.rs +++ b/solutions/21_macros/macros3.rs @@ -1 +1,13 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Added the attribute `macro_use` attribute. +#[macro_use] +mod macros { + macro_rules! my_macro { + () => { + println!("Check out my macro!"); + }; + } +} + +fn main() { + my_macro!(); +} From cc2c0958c9ba038e1584f3cbff0b07df4cc490c1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:54:05 +0200 Subject: [PATCH 1002/1432] macros4 solution --- exercises/21_macros/macros4.rs | 1 + solutions/21_macros/macros4.rs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/exercises/21_macros/macros4.rs b/exercises/21_macros/macros4.rs index 03ece0808e..9d77f6a5c8 100644 --- a/exercises/21_macros/macros4.rs +++ b/exercises/21_macros/macros4.rs @@ -1,3 +1,4 @@ +// TODO: Fix the compiler error by adding one or two characters. #[rustfmt::skip] macro_rules! my_macro { () => { diff --git a/solutions/21_macros/macros4.rs b/solutions/21_macros/macros4.rs index 4e18198923..41bcad166a 100644 --- a/solutions/21_macros/macros4.rs +++ b/solutions/21_macros/macros4.rs @@ -1 +1,15 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Added semicolons to separate the macro arms. +#[rustfmt::skip] +macro_rules! my_macro { + () => { + println!("Check out my macro!"); + }; + ($val:expr) => { + println!("Look at this other macro: {}", $val); + }; +} + +fn main() { + my_macro!(); + my_macro!(7777); +} From 78728d52387730300475cbe8c83497f603a14faf Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:54:35 +0200 Subject: [PATCH 1003/1432] clippy1 solution --- exercises/22_clippy/clippy1.rs | 16 +++++++--------- rustlings-macros/info.toml | 4 ++-- solutions/22_clippy/clippy1.rs | 18 +++++++++++++++++- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index f1eaa83141..b9d1ec1729 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -1,19 +1,17 @@ // 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 +// 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. -use std::f32; +use std::f32::consts::PI; fn main() { - let pi = 3.14f32; - let radius = 5.00f32; + // Use the more accurate `PI` constant. + let pi = PI; + let radius: f32 = 5.0; - let area = pi * f32::powi(radius, 2); + let area = pi * radius.powi(2); - println!( - "The area of a circle with radius {:.2} is {:.5}!", - radius, area - ) + println!("The area of a circle with radius {radius:.2} is {area:.5}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 0ec5fb21ab..4d40726e41 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1134,7 +1134,7 @@ dir = "22_clippy" test = false strict_clippy = true hint = """ -Rust stores the highest precision version of any long or infinite precision +Rust stores the highest precision version of some long or infinite precision mathematical constants in the Rust standard library: https://doc.rust-lang.org/stable/std/f32/consts/index.html @@ -1142,7 +1142,7 @@ We may be tempted to use our own approximations for certain mathematical constants, but clippy recognizes those imprecise mathematical constants as a source of potential error. -See the suggestions of the clippy warning in compile output and use the +See the suggestions of the Clippy warning in the compile output and use the appropriate replacement constant from `std::f32::consts`...""" [[exercises]] diff --git a/solutions/22_clippy/clippy1.rs b/solutions/22_clippy/clippy1.rs index 4e18198923..b9d1ec1729 100644 --- a/solutions/22_clippy/clippy1.rs +++ b/solutions/22_clippy/clippy1.rs @@ -1 +1,17 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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. + +use std::f32::consts::PI; + +fn main() { + // Use the more accurate `PI` constant. + let pi = PI; + let radius: f32 = 5.0; + + let area = pi * radius.powi(2); + + println!("The area of a circle with radius {radius:.2} is {area:.5}"); +} From a0e810b4713bcef60f64f4709ee27c3acec676cd Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 11:55:18 +0200 Subject: [PATCH 1004/1432] clippy2 solution --- exercises/22_clippy/clippy2.rs | 4 +++- rustlings-macros/info.toml | 3 ++- solutions/22_clippy/clippy2.rs | 11 ++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/exercises/22_clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs index c7d400d1e5..8cfe6f80a2 100644 --- a/exercises/22_clippy/clippy2.rs +++ b/exercises/22_clippy/clippy2.rs @@ -1,8 +1,10 @@ fn main() { let mut res = 42; let option = Some(12); + // TODO: Fix the Clippy lint. for x in option { res += x; } - println!("{}", res); + + println!("{res}"); } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4d40726e41..fce5e5a3dc 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1151,7 +1151,8 @@ dir = "22_clippy" test = false strict_clippy = true hint = """ -`for` loops over `Option` values are more clearly expressed as an `if let`""" +`for` loops over `Option` values are more clearly expressed as an `if-let` +statement.""" [[exercises]] name = "clippy3" diff --git a/solutions/22_clippy/clippy2.rs b/solutions/22_clippy/clippy2.rs index 4e18198923..7f6356285e 100644 --- a/solutions/22_clippy/clippy2.rs +++ b/solutions/22_clippy/clippy2.rs @@ -1 +1,10 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +fn main() { + let mut res = 42; + let option = Some(12); + // Use `if-let` instead of iteration. + if let Some(x) = option { + res += x; + } + + println!("{res}"); +} From 09c94bef2dbaf44daf81d8f618289c9425d1f90f Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 12:09:52 +0200 Subject: [PATCH 1005/1432] clippy3 solution --- exercises/22_clippy/clippy3.rs | 12 +++++++----- solutions/22_clippy/clippy3.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/exercises/22_clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs index fd829cf604..4f78834918 100644 --- a/exercises/22_clippy/clippy3.rs +++ b/exercises/22_clippy/clippy3.rs @@ -1,25 +1,27 @@ -// Here's a couple more easy Clippy fixes, so you can see its utility. +// Here are some more easy Clippy fixes so you can see its utility πŸ“Ž +// TODO: Fix all the Clippy lints. +#[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fn main() { let my_option: Option<()> = None; if my_option.is_none() { - my_option.unwrap(); + println!("{:?}", my_option.unwrap()); } let my_arr = &[ -1, -2, -3 -4, -5, -6 ]; - println!("My array! Here it is: {:?}", my_arr); + println!("My array! Here it is: {my_arr:?}"); let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5); - println!("This Vec is empty, see? {:?}", my_empty_vec); + println!("This Vec is empty, see? {my_empty_vec:?}"); let mut value_a = 45; let mut value_b = 66; // Let's swap these two! value_a = value_b; value_b = value_a; - println!("value a: {}; value b: {}", value_a, value_b); + println!("value a: {value_a}; value b: {value_b}"); } diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs index 4e18198923..811d184759 100644 --- a/solutions/22_clippy/clippy3.rs +++ b/solutions/22_clippy/clippy3.rs @@ -1 +1,31 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +use std::mem; + +#[rustfmt::skip] +#[allow(unused_variables, unused_assignments)] +fn main() { + let my_option: Option<()> = None; + // `unwrap` of an `Option` after checking if it is `None` will panic. + // Use `if-let` instead. + if let Some(value) = my_option { + println!("{value:?}"); + } + + // A comma was missing. + let my_arr = &[ + -1, -2, -3, + -4, -5, -6, + ]; + println!("My array! Here it is: {:?}", my_arr); + + let mut my_empty_vec = vec![1, 2, 3, 4, 5]; + // `resize` mutates a vector instead of returning a new one. + // `resize(0, …)` clears a vector, so it is better to use `clear`. + my_empty_vec.clear(); + println!("This Vec is empty, see? {my_empty_vec:?}"); + + let mut value_a = 45; + let mut value_b = 66; + // Use `mem::swap` to correctly swap two values. + mem::swap(&mut value_a, &mut value_b); + println!("value a: {}; value b: {}", value_a, value_b); +} From 428d64ffa01415826e421e00f59f63a77884b923 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 21:41:22 +0200 Subject: [PATCH 1006/1432] using_as solution --- exercises/23_conversions/using_as.rs | 10 ++++------ solutions/23_conversions/using_as.rs | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/exercises/23_conversions/using_as.rs b/exercises/23_conversions/using_as.rs index 94b1bb31a3..c131d1f32f 100644 --- a/exercises/23_conversions/using_as.rs +++ b/exercises/23_conversions/using_as.rs @@ -1,12 +1,10 @@ -// 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. +// Type casting in Rust is done via the usage of the `as` operator. +// Note that the `as` operator is not only used when type casting. It also helps +// with renaming imports. fn average(values: &[f64]) -> f64 { let total = values.iter().sum::(); + // TODO: Make a conversion before dividing. total / values.len() } diff --git a/solutions/23_conversions/using_as.rs b/solutions/23_conversions/using_as.rs index 4e18198923..14b92ebf05 100644 --- a/solutions/23_conversions/using_as.rs +++ b/solutions/23_conversions/using_as.rs @@ -1 +1,24 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// Type casting in Rust is done via the usage of the `as` operator. +// Note that the `as` operator is not only used when type casting. It also helps +// with renaming imports. + +fn average(values: &[f64]) -> f64 { + let total = values.iter().sum::(); + total / values.len() as f64 + // ^^^^^^ +} + +fn main() { + let values = [3.5, 0.3, 13.0, 11.7]; + println!("{}", average(&values)); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn returns_proper_type_and_value() { + assert_eq!(average(&[3.5, 0.3, 13.0, 11.7]), 7.125); + } +} From cddaf4881ea5a03e6deebfa9ec949347e1c2d025 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 1 Jul 2024 22:09:48 +0200 Subject: [PATCH 1007/1432] from_into solution --- exercises/23_conversions/from_into.rs | 62 ++++++------ rustlings-macros/info.toml | 2 +- solutions/23_conversions/from_into.rs | 137 +++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 34 deletions(-) diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 14a62ba82a..bc2783a30f 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -1,81 +1,79 @@ -// 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 +// The `From` trait is used for value-to-value conversions. If `From` is +// implemented, an implementation of `Into` is automatically provided. +// You can read more about it in the documentation: +// https://doc.rust-lang.org/std/convert/trait.From.html #[derive(Debug)] struct Person { name: String, - age: usize, + age: u8, } -// We implement the Default trait to use it as a fallback -// when the provided string is not convertible into a Person object +// We implement the Default trait to use it as a fallback when the provided +// string is not convertible into a `Person` object. impl Default for Person { - fn default() -> Person { - Person { + fn default() -> Self { + Self { name: String::from("John"), age: 30, } } } -// Your task is to complete this implementation in order for the line `let p1 = -// Person::from("Mark,20")` to compile. Please note that you'll need to parse the -// age component into a `usize` with something like `"4".parse::()`. The -// outcome of this needs to be handled appropriately. +// TODO: Complete this `From` implementation to be able to parse a `Person` +// out of a string in the form of "Mark,20". +// Note that you'll need to parse the age component into a `u8` with something +// like `"4".parse::()`. // // Steps: -// 1. If the length of the provided string is 0, then return the default of -// Person. -// 2. Split the given string on the commas present in it. -// 3. Extract the first element from the split operation and use it as the name. -// 4. If the name is empty, then return the default of Person. -// 5. Extract the other element from the split operation and parse it into a -// `usize` as the age. -// If while parsing the age, something goes wrong, then return the default of -// Person. Otherwise, then return an instantiated Person object with the results - +// 1. Split the given string on the commas present in it. +// 2. If the split operation returns less or more than 2 elements, return the +// default of `Person`. +// 3. Use the first element from the split operation as the name. +// 4. If the name is empty, return the default of `Person`. +// 5. Parse the second element from the split operation into a `u8` as the age. +// 6. If parsing the age fails, return the default of `Person`. impl From<&str> for Person { - fn from(s: &str) -> Person {} + fn from(s: &str) -> Self {} } fn main() { - // Use the `from` function + // Use the `from` function. let p1 = Person::from("Mark,20"); - // Since From is implemented for Person, we should be able to use Into + println!("{p1:?}"); + + // Since `From` is implemented for Person, we are able to use `Into`. let p2: Person = "Gerald,70".into(); - println!("{:?}", p1); - println!("{:?}", p2); + println!("{p2:?}"); } #[cfg(test)] mod tests { use super::*; + #[test] fn test_default() { - // Test that the default person is 30 year old John let dp = Person::default(); assert_eq!(dp.name, "John"); assert_eq!(dp.age, 30); } + #[test] fn test_bad_convert() { - // Test that John is returned when bad string is provided let p = Person::from(""); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } + #[test] fn test_good_convert() { - // Test that "Mark,20" works let p = Person::from("Mark,20"); assert_eq!(p.name, "Mark"); assert_eq!(p.age, 20); } + #[test] fn test_bad_age() { - // Test that "Mark,twenty" will return the default person due to an - // error in parsing age let p = Person::from("Mark,twenty"); assert_eq!(p.name, "John"); assert_eq!(p.age, 30); diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index fce5e5a3dc..b848e0efce 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1174,7 +1174,7 @@ Use the `as` operator to cast one of the operands in the last line of the name = "from_into" dir = "23_conversions" hint = """ -Follow the steps provided right before the `From` implementation""" +Follow the steps provided right before the `From` implementation.""" [[exercises]] name = "from_str" diff --git a/solutions/23_conversions/from_into.rs b/solutions/23_conversions/from_into.rs index 4e18198923..cec23cb4be 100644 --- a/solutions/23_conversions/from_into.rs +++ b/solutions/23_conversions/from_into.rs @@ -1 +1,136 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// The `From` trait is used for value-to-value conversions. If `From` is +// implemented, an implementation of `Into` is automatically provided. +// You can read more about it in the documentation: +// https://doc.rust-lang.org/std/convert/trait.From.html + +#[derive(Debug)] +struct Person { + name: String, + age: u8, +} + +// We implement the Default trait to use it as a fallback when the provided +// string is not convertible into a `Person` object. +impl Default for Person { + fn default() -> Self { + Self { + name: String::from("John"), + age: 30, + } + } +} + +impl From<&str> for Person { + fn from(s: &str) -> Self { + let mut split = s.split(','); + let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else { + // ^^^^ there should be no third element + return Self::default(); + }; + + if name.is_empty() { + return Self::default(); + } + + let Ok(age) = age.parse() else { + return Self::default(); + }; + + Self { + name: name.into(), + age, + } + } +} + +fn main() { + // Use the `from` function. + let p1 = Person::from("Mark,20"); + println!("{p1:?}"); + + // Since `From` is implemented for Person, we are able to use `Into`. + let p2: Person = "Gerald,70".into(); + println!("{p2:?}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default() { + let dp = Person::default(); + assert_eq!(dp.name, "John"); + assert_eq!(dp.age, 30); + } + + #[test] + fn test_bad_convert() { + let p = Person::from(""); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_good_convert() { + let p = Person::from("Mark,20"); + assert_eq!(p.name, "Mark"); + assert_eq!(p.age, 20); + } + + #[test] + fn test_bad_age() { + let p = Person::from("Mark,twenty"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_comma_and_age() { + let p: Person = Person::from("Mark"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_age() { + let p: Person = Person::from("Mark,"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_name() { + let p: Person = Person::from(",1"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_name_and_age() { + let p: Person = Person::from(","); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_missing_name_and_invalid_age() { + let p: Person = Person::from(",one"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_trailing_comma() { + let p: Person = Person::from("Mike,32,"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_trailing_comma_and_some_string() { + let p: Person = Person::from("Mike,32,dog"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } +} From e3c8c457ba8744b0f1b799c4d7d4bf24e8e61792 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:03:55 +0200 Subject: [PATCH 1008/1432] from_str solution --- exercises/23_conversions/from_str.rs | 60 +++++++------ rustlings-macros/info.toml | 10 +-- solutions/23_conversions/from_str.rs | 129 ++++++++++++++++++++++++++- 3 files changed, 163 insertions(+), 36 deletions(-) diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index 58270f0246..1b3f553e80 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -1,7 +1,8 @@ -// 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 +// This is similar to the previous `from_into` exercise. 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 in the documentation: // https://doc.rust-lang.org/std/str/trait.FromStr.html use std::num::ParseIntError; @@ -10,43 +11,42 @@ use std::str::FromStr; #[derive(Debug, PartialEq)] struct Person { name: String, - age: usize, + age: u8, } // We will use this error type for the `FromStr` implementation. #[derive(Debug, PartialEq)] enum ParsePersonError { - // Empty input string - Empty, // Incorrect number of fields BadLen, // Empty name field NoName, - // Wrapped error from parse::() + // Wrapped error from parse::() ParseInt(ParseIntError), } +// TODO: Complete this `From` implementation to be able to parse a `Person` +// out of a string in the form of "Mark,20". +// Note that you'll need to parse the age component into a `u8` with something +// like `"4".parse::()`. +// // Steps: -// 1. If the length of the provided string is 0, an error should be returned -// 2. Split the given string on the commas present in it -// 3. Only 2 elements should be returned from the split, otherwise return an -// error -// 4. Extract the first element from the split operation and use it as the name -// 5. Extract the other element from the split operation and parse it into a -// `usize` as the age with something like `"4".parse::()` -// 6. If while extracting the name and the age something goes wrong, an error -// should be returned -// If everything goes well, then return a Result of a Person object - +// 1. Split the given string on the commas present in it. +// 2. If the split operation returns less or more than 2 elements, return the +// error `ParsePersonError::BadLen`. +// 3. Use the first element from the split operation as the name. +// 4. If the name is empty, return the error `ParsePersonError::NoName`. +// 5. Parse the second element from the split operation into a `u8` as the age. +// 6. If parsing the age fails, return the error `ParsePersonError::ParseInt`. impl FromStr for Person { type Err = ParsePersonError; - fn from_str(s: &str) -> Result { - } + + fn from_str(s: &str) -> Result {} } fn main() { - let p = "Mark,20".parse::().unwrap(); - println!("{:?}", p); + let p = "Mark,20".parse::(); + println!("{p:?}"); } #[cfg(test)] @@ -55,8 +55,9 @@ mod tests { #[test] fn empty_input() { - assert_eq!("".parse::(), Err(ParsePersonError::Empty)); + assert_eq!("".parse::(), Err(ParsePersonError::BadLen)); } + #[test] fn good_input() { let p = "John,32".parse::(); @@ -65,11 +66,12 @@ mod tests { assert_eq!(p.name, "John"); assert_eq!(p.age, 32); } + #[test] fn missing_age() { assert!(matches!( "John,".parse::(), - Err(ParsePersonError::ParseInt(_)) + Err(ParsePersonError::ParseInt(_)), )); } @@ -77,7 +79,7 @@ mod tests { fn invalid_age() { assert!(matches!( "John,twenty".parse::(), - Err(ParsePersonError::ParseInt(_)) + Err(ParsePersonError::ParseInt(_)), )); } @@ -95,7 +97,7 @@ mod tests { fn missing_name_and_age() { assert!(matches!( ",".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), )); } @@ -103,7 +105,7 @@ mod tests { fn missing_name_and_invalid_age() { assert!(matches!( ",one".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), )); } @@ -116,7 +118,7 @@ mod tests { fn trailing_comma_and_some_string() { assert_eq!( "John,32,man".parse::(), - Err(ParsePersonError::BadLen) + Err(ParsePersonError::BadLen), ); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index b848e0efce..4ef1a0a39c 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1183,13 +1183,11 @@ hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, or an `Err` with an error if the string is not valid. -This is almost like the `from_into` exercise, but returning errors instead -of falling back to a default value. +This is almost like the previous `from_into` exercise, but returning errors +instead of falling back to a default value. -Look at the test cases to see which error variants to return. - -Another hint: You can use the `map_err` method of `Result` with a function -or a closure to wrap the error from `parse::`. +Another hint: You can use the `map_err` method of `Result` with a function or a +closure to wrap the error from `parse::`. Yet another hint: If you would like to propagate errors by using the `?` operator in your solution, you might want to look at diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs index 4e18198923..301150b9c0 100644 --- a/solutions/23_conversions/from_str.rs +++ b/solutions/23_conversions/from_str.rs @@ -1 +1,128 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// This is similar to the previous `from_into` exercise. 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 in the documentation: +// https://doc.rust-lang.org/std/str/trait.FromStr.html + +use std::num::ParseIntError; +use std::str::FromStr; + +#[derive(Debug, PartialEq)] +struct Person { + name: String, + age: u8, +} + +// We will use this error type for the `FromStr` implementation. +#[derive(Debug, PartialEq)] +enum ParsePersonError { + // Incorrect number of fields + BadLen, + // Empty name field + NoName, + // Wrapped error from parse::() + ParseInt(ParseIntError), +} + +impl FromStr for Person { + type Err = ParsePersonError; + + fn from_str(s: &str) -> Result { + let mut split = s.split(','); + let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else { + // ^^^^ there should be no third element + return Err(ParsePersonError::BadLen); + }; + + if name.is_empty() { + return Err(ParsePersonError::NoName); + } + + let age = age.parse().map_err(ParsePersonError::ParseInt)?; + + Ok(Self { + name: name.into(), + age, + }) + } +} + +fn main() { + let p = "Mark,20".parse::(); + println!("{p:?}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_input() { + assert_eq!("".parse::(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn good_input() { + let p = "John,32".parse::(); + assert!(p.is_ok()); + let p = p.unwrap(); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 32); + } + + #[test] + fn missing_age() { + assert!(matches!( + "John,".parse::(), + Err(ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn invalid_age() { + assert!(matches!( + "John,twenty".parse::(), + Err(ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn missing_comma_and_age() { + assert_eq!("John".parse::(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn missing_name() { + assert_eq!(",1".parse::(), Err(ParsePersonError::NoName)); + } + + #[test] + fn missing_name_and_age() { + assert!(matches!( + ",".parse::(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn missing_name_and_invalid_age() { + assert!(matches!( + ",one".parse::(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + )); + } + + #[test] + fn trailing_comma() { + assert_eq!("John,32,".parse::(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn trailing_comma_and_some_string() { + assert_eq!( + "John,32,man".parse::(), + Err(ParsePersonError::BadLen), + ); + } +} From 5217cdc5e2c49d179497e5ef65d0dc8bff1e0950 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:26:09 +0200 Subject: [PATCH 1009/1432] try_from_into solution --- exercises/23_conversions/try_from_into.rs | 113 ++++++------- rustlings-macros/info.toml | 17 +- solutions/23_conversions/try_from_into.rs | 193 +++++++++++++++++++++- 3 files changed, 246 insertions(+), 77 deletions(-) diff --git a/exercises/23_conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs index da45e5a47b..f3ae80a9e4 100644 --- a/exercises/23_conversions/try_from_into.rs +++ b/exercises/23_conversions/try_from_into.rs @@ -1,9 +1,10 @@ -// 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 +// `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 in the documentation: // https://doc.rust-lang.org/std/convert/trait.TryFrom.html +#![allow(clippy::useless_vec)] use std::convert::{TryFrom, TryInto}; #[derive(Debug, PartialEq)] @@ -13,7 +14,7 @@ struct Color { blue: u8, } -// We will use this error type for these `TryFrom` conversions. +// We will use this error type for the `TryFrom` conversions. #[derive(Debug, PartialEq)] enum IntoColorError { // Incorrect length of slice @@ -22,78 +23,67 @@ enum IntoColorError { IntConversion, } -// Your task is to complete this implementation and return an Ok result of inner -// type Color. You need to create an implementation for a tuple of three -// integers, an array of three integers, and a slice of integers. -// -// Note that the implementation for tuple and array will be checked at compile -// time, but the slice implementation needs to check the slice length! Also note -// that correct RGB color values must be integers in the 0..=255 range. - -// Tuple implementation +// TODO: Tuple implementation. +// Correct RGB color values must be integers in the 0..=255 range. impl TryFrom<(i16, i16, i16)> for Color { type Error = IntoColorError; - fn try_from(tuple: (i16, i16, i16)) -> Result { - } + + fn try_from(tuple: (i16, i16, i16)) -> Result {} } -// Array implementation +// TODO: Array implementation. impl TryFrom<[i16; 3]> for Color { type Error = IntoColorError; - fn try_from(arr: [i16; 3]) -> Result { - } + + fn try_from(arr: [i16; 3]) -> Result {} } -// Slice implementation +// TODO: Slice implementation. +// This implementation needs to check the slice length. impl TryFrom<&[i16]> for Color { type Error = IntoColorError; - fn try_from(slice: &[i16]) -> Result { - } + + fn try_from(slice: &[i16]) -> Result {} } fn main() { - // Use the `try_from` function + // Using the `try_from` function. let c1 = Color::try_from((183, 65, 14)); - println!("{:?}", c1); + println!("{c1:?}"); - // Since TryFrom is implemented for Color, we should be able to use TryInto + // Since `TryFrom` is implemented for `Color`, we can use `TryInto`. let c2: Result = [183, 65, 14].try_into(); - println!("{:?}", c2); + println!("{c2:?}"); let v = vec![183, 65, 14]; - // With slice we should use `try_from` function + // With slice we should use the `try_from` function let c3 = Color::try_from(&v[..]); - println!("{:?}", c3); - // or take slice within round brackets and use TryInto + println!("{c3:?}"); + // or put the slice within round brackets and use `try_into`. let c4: Result = (&v[..]).try_into(); - println!("{:?}", c4); + println!("{c4:?}"); } #[cfg(test)] mod tests { use super::*; + use IntoColorError::*; #[test] fn test_tuple_out_of_range_positive() { - assert_eq!( - Color::try_from((256, 1000, 10000)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion)); } + #[test] fn test_tuple_out_of_range_negative() { - assert_eq!( - Color::try_from((-1, -10, -256)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion)); } + #[test] fn test_tuple_sum() { - assert_eq!( - Color::try_from((-1, 255, 255)), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion)); } + #[test] fn test_tuple_correct() { let c: Result = (183, 65, 14).try_into(); @@ -103,25 +93,29 @@ mod tests { Color { red: 183, green: 65, - blue: 14 + blue: 14, } ); } + #[test] fn test_array_out_of_range_positive() { let c: Result = [1000, 10000, 256].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_out_of_range_negative() { let c: Result = [-10, -256, -1].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_sum() { let c: Result = [-1, 255, 255].try_into(); - assert_eq!(c, Err(IntoColorError::IntConversion)); + assert_eq!(c, Err(IntConversion)); } + #[test] fn test_array_correct() { let c: Result = [183, 65, 14].try_into(); @@ -135,30 +129,25 @@ mod tests { } ); } + #[test] fn test_slice_out_of_range_positive() { let arr = [10000, 256, 1000]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_out_of_range_negative() { let arr = [-256, -1, -10]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_sum() { let arr = [-1, 255, 255]; - assert_eq!( - Color::try_from(&arr[..]), - Err(IntoColorError::IntConversion) - ); + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); } + #[test] fn test_slice_correct() { let v = vec![183, 65, 14]; @@ -169,18 +158,20 @@ mod tests { Color { red: 183, green: 65, - blue: 14 + blue: 14, } ); } + #[test] fn test_slice_excess_length() { let v = vec![0, 0, 0, 0]; - assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); } + #[test] fn test_slice_insufficient_length() { let v = vec![0, 0]; - assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4ef1a0a39c..488fdacff2 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1197,21 +1197,8 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen name = "try_from_into" dir = "23_conversions" hint = """ -Follow the steps provided right before the `TryFrom` implementation. -You can also use the example at -https://doc.rust-lang.org/std/convert/trait.TryFrom.html - -Is there an implementation of `TryFrom` in the standard library that -can both do the required integer conversion and check the range of the input? - -Another hint: Look at the test cases to see which error variants to return. - -Yet another hint: You can use the `map_err` or `or` methods of `Result` to -convert errors. - -Yet another hint: If you would like to propagate errors by using the `?` -operator in your solution, you might want to look at -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +Is there an implementation of `TryFrom` in the standard library that can both do +the required integer conversion and check the range of the input? Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" diff --git a/solutions/23_conversions/try_from_into.rs b/solutions/23_conversions/try_from_into.rs index 4e18198923..acb7721d64 100644 --- a/solutions/23_conversions/try_from_into.rs +++ b/solutions/23_conversions/try_from_into.rs @@ -1 +1,192 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// `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 in the documentation: +// https://doc.rust-lang.org/std/convert/trait.TryFrom.html + +use std::convert::{TryFrom, TryInto}; + +#[derive(Debug, PartialEq)] +struct Color { + red: u8, + green: u8, + blue: u8, +} + +// We will use this error type for the `TryFrom` conversions. +#[derive(Debug, PartialEq)] +enum IntoColorError { + // Incorrect length of slice + BadLen, + // Integer conversion error + IntConversion, +} + +impl TryFrom<(i16, i16, i16)> for Color { + type Error = IntoColorError; + + fn try_from(tuple: (i16, i16, i16)) -> Result { + let (Ok(red), Ok(green), Ok(blue)) = ( + u8::try_from(tuple.0), + u8::try_from(tuple.1), + u8::try_from(tuple.2), + ) else { + return Err(IntoColorError::IntConversion); + }; + + Ok(Self { red, green, blue }) + } +} + +impl TryFrom<[i16; 3]> for Color { + type Error = IntoColorError; + + fn try_from(arr: [i16; 3]) -> Result { + // Reuse the implementation for a tuple. + Self::try_from((arr[0], arr[1], arr[2])) + } +} + +impl TryFrom<&[i16]> for Color { + type Error = IntoColorError; + + fn try_from(slice: &[i16]) -> Result { + // Check the length. + if slice.len() != 3 { + return Err(IntoColorError::BadLen); + } + + // Reuse the implementation for a tuple. + Self::try_from((slice[0], slice[1], slice[2])) + } +} + +fn main() { + // Using the `try_from` function. + let c1 = Color::try_from((183, 65, 14)); + println!("{c1:?}"); + + // Since `TryFrom` is implemented for `Color`, we can use `TryInto`. + let c2: Result = [183, 65, 14].try_into(); + println!("{c2:?}"); + + let v = vec![183, 65, 14]; + // With slice we should use the `try_from` function + let c3 = Color::try_from(&v[..]); + println!("{c3:?}"); + // or put the slice within round brackets and use `try_into`. + let c4: Result = (&v[..]).try_into(); + println!("{c4:?}"); +} + +#[cfg(test)] +mod tests { + use super::*; + use IntoColorError::*; + + #[test] + fn test_tuple_out_of_range_positive() { + assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion)); + } + + #[test] + fn test_tuple_out_of_range_negative() { + assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion)); + } + + #[test] + fn test_tuple_sum() { + assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion)); + } + + #[test] + fn test_tuple_correct() { + let c: Result = (183, 65, 14).try_into(); + assert!(c.is_ok()); + assert_eq!( + c.unwrap(), + Color { + red: 183, + green: 65, + blue: 14, + } + ); + } + + #[test] + fn test_array_out_of_range_positive() { + let c: Result = [1000, 10000, 256].try_into(); + assert_eq!(c, Err(IntConversion)); + } + + #[test] + fn test_array_out_of_range_negative() { + let c: Result = [-10, -256, -1].try_into(); + assert_eq!(c, Err(IntConversion)); + } + + #[test] + fn test_array_sum() { + let c: Result = [-1, 255, 255].try_into(); + assert_eq!(c, Err(IntConversion)); + } + + #[test] + fn test_array_correct() { + let c: Result = [183, 65, 14].try_into(); + assert!(c.is_ok()); + assert_eq!( + c.unwrap(), + Color { + red: 183, + green: 65, + blue: 14 + } + ); + } + + #[test] + fn test_slice_out_of_range_positive() { + let arr = [10000, 256, 1000]; + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); + } + + #[test] + fn test_slice_out_of_range_negative() { + let arr = [-256, -1, -10]; + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); + } + + #[test] + fn test_slice_sum() { + let arr = [-1, 255, 255]; + assert_eq!(Color::try_from(&arr[..]), Err(IntConversion)); + } + + #[test] + fn test_slice_correct() { + let v = vec![183, 65, 14]; + let c: Result = Color::try_from(&v[..]); + assert!(c.is_ok()); + assert_eq!( + c.unwrap(), + Color { + red: 183, + green: 65, + blue: 14, + } + ); + } + + #[test] + fn test_slice_excess_length() { + let v = vec![0, 0, 0, 0]; + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); + } + + #[test] + fn test_slice_insufficient_length() { + let v = vec![0, 0]; + assert_eq!(Color::try_from(&v[..]), Err(BadLen)); + } +} From 8ef5d10da2252ec270b9170af25dabc466113e99 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:29:30 +0200 Subject: [PATCH 1010/1432] Import the error variants in the tests --- exercises/23_conversions/from_str.rs | 31 +++++++++------------------- solutions/23_conversions/from_str.rs | 31 +++++++++------------------- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index 1b3f553e80..4b1aaa28f9 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -52,10 +52,11 @@ fn main() { #[cfg(test)] mod tests { use super::*; + use ParsePersonError::*; #[test] fn empty_input() { - assert_eq!("".parse::(), Err(ParsePersonError::BadLen)); + assert_eq!("".parse::(), Err(BadLen)); } #[test] @@ -69,56 +70,44 @@ mod tests { #[test] fn missing_age() { - assert!(matches!( - "John,".parse::(), - Err(ParsePersonError::ParseInt(_)), - )); + assert!(matches!("John,".parse::(), Err(ParseInt(_)))); } #[test] fn invalid_age() { - assert!(matches!( - "John,twenty".parse::(), - Err(ParsePersonError::ParseInt(_)), - )); + assert!(matches!("John,twenty".parse::(), Err(ParseInt(_)))); } #[test] fn missing_comma_and_age() { - assert_eq!("John".parse::(), Err(ParsePersonError::BadLen)); + assert_eq!("John".parse::(), Err(BadLen)); } #[test] fn missing_name() { - assert_eq!(",1".parse::(), Err(ParsePersonError::NoName)); + assert_eq!(",1".parse::(), Err(NoName)); } #[test] fn missing_name_and_age() { - assert!(matches!( - ",".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), - )); + assert!(matches!(",".parse::(), Err(NoName | ParseInt(_)))); } #[test] fn missing_name_and_invalid_age() { assert!(matches!( ",one".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + Err(NoName | ParseInt(_)), )); } #[test] fn trailing_comma() { - assert_eq!("John,32,".parse::(), Err(ParsePersonError::BadLen)); + assert_eq!("John,32,".parse::(), Err(BadLen)); } #[test] fn trailing_comma_and_some_string() { - assert_eq!( - "John,32,man".parse::(), - Err(ParsePersonError::BadLen), - ); + assert_eq!("John,32,man".parse::(), Err(BadLen)); } } diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs index 301150b9c0..005b50125a 100644 --- a/solutions/23_conversions/from_str.rs +++ b/solutions/23_conversions/from_str.rs @@ -56,10 +56,11 @@ fn main() { #[cfg(test)] mod tests { use super::*; + use ParsePersonError::*; #[test] fn empty_input() { - assert_eq!("".parse::(), Err(ParsePersonError::BadLen)); + assert_eq!("".parse::(), Err(BadLen)); } #[test] @@ -73,56 +74,44 @@ mod tests { #[test] fn missing_age() { - assert!(matches!( - "John,".parse::(), - Err(ParsePersonError::ParseInt(_)), - )); + assert!(matches!("John,".parse::(), Err(ParseInt(_)))); } #[test] fn invalid_age() { - assert!(matches!( - "John,twenty".parse::(), - Err(ParsePersonError::ParseInt(_)), - )); + assert!(matches!("John,twenty".parse::(), Err(ParseInt(_)))); } #[test] fn missing_comma_and_age() { - assert_eq!("John".parse::(), Err(ParsePersonError::BadLen)); + assert_eq!("John".parse::(), Err(BadLen)); } #[test] fn missing_name() { - assert_eq!(",1".parse::(), Err(ParsePersonError::NoName)); + assert_eq!(",1".parse::(), Err(NoName)); } #[test] fn missing_name_and_age() { - assert!(matches!( - ",".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), - )); + assert!(matches!(",".parse::(), Err(NoName | ParseInt(_)))); } #[test] fn missing_name_and_invalid_age() { assert!(matches!( ",one".parse::(), - Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)), + Err(NoName | ParseInt(_)), )); } #[test] fn trailing_comma() { - assert_eq!("John,32,".parse::(), Err(ParsePersonError::BadLen)); + assert_eq!("John,32,".parse::(), Err(BadLen)); } #[test] fn trailing_comma_and_some_string() { - assert_eq!( - "John,32,man".parse::(), - Err(ParsePersonError::BadLen), - ); + assert_eq!("John,32,man".parse::(), Err(BadLen)); } } From 825637f32c652a4ca9fb59ba0cf7b2191dacacc9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:35:38 +0200 Subject: [PATCH 1011/1432] as_ref_mut solution --- exercises/23_conversions/as_ref_mut.rs | 7 ++- solutions/23_conversions/as_ref_mut.rs | 60 +++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/exercises/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs index c725dfded5..54f0cd11df 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -3,22 +3,21 @@ // https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. // Obtain the number of bytes (not characters) in the given argument. -// TODO: Add the AsRef trait appropriately as a trait bound. +// TODO: Add the `AsRef` trait appropriately as a trait bound. fn byte_counter(arg: T) -> usize { arg.as_ref().as_bytes().len() } // Obtain the number of characters (not bytes) in the given argument. -// TODO: Add the AsRef trait appropriately as a trait bound. +// TODO: Add the `AsRef` trait appropriately as a trait bound. fn char_counter(arg: T) -> usize { arg.as_ref().chars().count() } -// Squares a number using as_mut(). +// Squares a number using `as_mut()`. // TODO: Add the appropriate trait bound. fn num_sq(arg: &mut T) { // TODO: Implement the function body. - ??? } fn main() { diff --git a/solutions/23_conversions/as_ref_mut.rs b/solutions/23_conversions/as_ref_mut.rs index 4e18198923..91b12bacc1 100644 --- a/solutions/23_conversions/as_ref_mut.rs +++ b/solutions/23_conversions/as_ref_mut.rs @@ -1 +1,59 @@ -// Solutions will be available before the stable release. Thank you for testing the beta version πŸ₯° +// 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. + +// Obtain the number of bytes (not characters) in the given argument. +fn byte_counter>(arg: T) -> usize { + arg.as_ref().as_bytes().len() +} + +// Obtain the number of characters (not bytes) in the given argument. +fn char_counter>(arg: T) -> usize { + arg.as_ref().chars().count() +} + +// Squares a number using `as_mut()`. +fn num_sq>(arg: &mut T) { + let arg = arg.as_mut(); + *arg = *arg * *arg; +} + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn different_counts() { + let s = "CafΓ© au lait"; + assert_ne!(char_counter(s), byte_counter(s)); + } + + #[test] + fn same_counts() { + let s = "Cafe au lait"; + assert_eq!(char_counter(s), byte_counter(s)); + } + + #[test] + fn different_counts_using_string() { + let s = String::from("CafΓ© au lait"); + assert_ne!(char_counter(s.clone()), byte_counter(s)); + } + + #[test] + fn same_counts_using_string() { + let s = String::from("Cafe au lait"); + assert_eq!(char_counter(s.clone()), byte_counter(s)); + } + + #[test] + fn mut_box() { + let mut num: Box = Box::new(3); + num_sq(&mut num); + assert_eq!(*num, 9); + } +} From bcebbb9df6987cd21ecf615907d5d06f8b626edc Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:45:55 +0200 Subject: [PATCH 1012/1432] Update deps --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f41ba86e41..618e422f00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", @@ -416,9 +416,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" @@ -729,9 +729,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.118" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index 64844c3a39..97f1c19480 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,14 +47,14 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.7", features = ["derive"] } +clap = { version = "4.5.8", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } -serde_json = "1.0.118" +serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true From 67ce9b9e56b1d9e8b8b50b9b48c7eebd80b9ec8f Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:50:05 +0200 Subject: [PATCH 1013/1432] Underline "next" --- src/watch/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index dd43c56610..78af30a440 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -101,7 +101,7 @@ impl<'a> WatchState<'a> { } if self.done_status != DoneStatus::Pending { - write!(self.writer, "{}:next / ", 'n'.bold())?; + write!(self.writer, "{}:{} / ", 'n'.bold(), "next".underlined())?; } if !self.show_hint { From 6cf75d569bd0dd33a041e37c59cb75d28664bd7b Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 14:28:08 +0200 Subject: [PATCH 1014/1432] Fix typos --- exercises/15_traits/traits2.rs | 2 +- exercises/20_threads/threads3.rs | 2 +- src/dev/check.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index e9040162d6..d724dc288e 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -3,7 +3,7 @@ trait AppendBar { } // TODO: Implement the trait `AppendBar` for a vector of strings. -// `appned_bar` should push the string "Bar" into the vector. +// `append_bar` should push the string "Bar" into the vector. fn main() { // You can optionally experiment here. diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 30ac8ddd55..8aa7291fd6 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -18,7 +18,7 @@ impl Queue { fn send_tx(q: Queue, tx: mpsc::Sender) { // TODO: We want to send `tx` to both threads. But currently, it is moved - // into the frist thread. How could you solve this problem? + // into the first thread. How could you solve this problem? thread::spawn(move || { for val in q.first_half { println!("Sending {val:?}"); diff --git a/src/dev/check.rs b/src/dev/check.rs index 336360be6f..5074c133db 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -174,7 +174,7 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> { let target_dir = parse_target_dir()?; let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len())); - let error_occured = AtomicBool::new(false); + let error_occurred = AtomicBool::new(false); println!("Running all solutions. This may take a while...\n"); thread::scope(|s| { @@ -188,7 +188,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> .unwrap(); stderr.write_all(exercise_info.name.as_bytes()).unwrap(); stderr.write_all(SEPARATOR).unwrap(); - error_occured.store(true, atomic::Ordering::Relaxed); + error_occurred.store(true, atomic::Ordering::Relaxed); }; let path = exercise_info.sol_path(); @@ -213,7 +213,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> } }); - if error_occured.load(atomic::Ordering::Relaxed) { + if error_occurred.load(atomic::Ordering::Relaxed) { bail!("At least one solution failed. See the output above."); } From 2d792651ea5d500537e23d2825b919cff769d30b Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 14:29:07 +0200 Subject: [PATCH 1015/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 618e422f00..f5908cc4bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,7 +654,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustlings" -version = "6.0.0-beta.9" +version = "6.0.0-beta.10" dependencies = [ "anyhow", "assert_cmd", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.9" +version = "6.0.0-beta.10" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 97f1c19480..3455e2b4b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.9" +version = "6.0.0-beta.10" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.10" } serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true From 43eb014026e1c719bd0926cd2cb5fc7b8a18ae1f Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 14:45:19 +0200 Subject: [PATCH 1016/1432] Update README --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c0de89d3d6..9463038d85 100644 --- a/README.md +++ b/README.md @@ -32,19 +32,15 @@ This'll also install _Cargo_, Rust's package/project manager. The following command will download and compile Rustlings: - - ```bash -cargo install rustlings@6.0.0-beta.9 +cargo install rustlings ```
If the installation fails… (click to expand) - - - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.9 --locked` +- Try adding the `--locked` flag: `cargo install rustlings --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
@@ -88,6 +84,9 @@ We highly recommend that you have a look at them before you start πŸ“šοΈ Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! Some exercises contain tests that need to pass for the exercise to be done βœ… +Search for `TODO` and `todo!()` to find out what you need to change. +Ask for hints by entering `h` in the _watch mode_ πŸ’‘ + ### Watch Mode After [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`. From 4c5573b09f13221572867441618442bf1fef66d7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 14:45:25 +0200 Subject: [PATCH 1017/1432] Update CONTRIBUTING --- CONTRIBUTING.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc00a6b043..95605f70a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,11 +32,14 @@ If you need any help with it or face any Git related problems, don't hesitate to It may take time to review your pull request. Please be patient πŸ˜‡ +When updating an exercise, check if its solution needs to be updated. + ## Adding An Exercise - Name the file `exercises/yourTopic/yourTopicN.rs`. -- Make sure to put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`. -- Add a (possible) solution at `solutions/yourTopic/yourTopicN.rs` with comments and links explaining it. +- Make sure to put in some helpful links, and link to sections of The Book in `exercises/yourTopic/README.md`. +- In the exercise, add a `// TODO: …` comment where user changes are required. +- Add a solution at `solutions/yourTopic/yourTopicN.rs` with comments explaining it. - Add the [metadata for your exercise](#exercise-metadata) in the `rustlings-macros/info.toml` file. - Make sure your exercise runs with `rustlings run yourTopicN`. - [Open a pull request](#pull-requests). @@ -49,7 +52,9 @@ The exercise metadata should contain the following: [[exercises]] name = "yourTopicN" dir = "yourTopic" -hint = """A useful (multi-line) hint for your exercise.""" +hint = """ +A useful (multi-line) hint for your exercise. +Include links to a section in The Book or a documentation page.""" ``` If your exercise doesn't contain any test, add `test = false` to the exercise metadata. From 9bb174e96ef1f719cf946134e7e40fb9f5b331dc Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 16:09:05 +0200 Subject: [PATCH 1018/1432] Add a guide for third-party exercises --- README.md | 8 ++++-- THIRD_PARTY_EXERCISES.md | 53 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 THIRD_PARTY_EXERCISES.md diff --git a/README.md b/README.md index 9463038d85..4cee71cedd 100644 --- a/README.md +++ b/README.md @@ -117,11 +117,15 @@ See the footer of the list for all possible keys. ## Continuing On - - Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. +## Third-Party Exercises + +Do you want to create your own set of Rustlings exercises to focus on some specific topic? +Or did you want to translate the original Rustlings exercises? +Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! + ## Uninstalling Rustlings If you want to remove Rustlings from your system, run the following command: diff --git a/THIRD_PARTY_EXERCISES.md b/THIRD_PARTY_EXERCISES.md new file mode 100644 index 0000000000..2ae8b722eb --- /dev/null +++ b/THIRD_PARTY_EXERCISES.md @@ -0,0 +1,53 @@ +# Third-Party Exercises + +The support of Rustlings for third-party exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. +You could also offer a translatation of the original Rustlings exercises as a third-party exercises. + +## Getting started + +To create third-party exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`. +This command will, similar to `cargo new PROJECT_NAME`, create a template directory called `PROJECT_NAME` with all what you need to get started. + +Read the comments in the generated `info.toml` file to understand the format of this file. +It allows you to set a custom welcome and final message and specify the metadata of every exercise. + +## Create an exercise + +Here is an example of the metadata of one file: + +```toml +[[exercises]] +name = "intro1" +hint = """ +To finish this exercise, you need to … +This link might help you …""" +``` + +After entering this in `info.toml`, create the file `intro1.rs` in the `exercises/` directory. +The exercise needs to contain a `main` function, but it can be empty. +Adding tests is recommended. +Look at the official Rustlings exercises for inspiration. + +You can optionally add a solution file `intro1.rs` to the `solutions/` directory. + +Now, run `rustlings dev check`. +It will tell you about any issues with your exercises. +For example, it will tell you to run `rustlings dev update` to update the `Cargo.toml` file to include the new exercise `intro1`. + +`rustlings dev check` will also run your solutions (if you have any) to make sure that they run successfully. + +That's it! +You finished your first exercise πŸŽ‰ + +## Publish + +Now, add more exercises and publish them as a Git repository. + +Users just have to clone that repository and run `rustlings` in it to start working on your set of exercises just like the official ones. + +One difference to the official exercises is that the solution files will not be hidden until the user finishes an exercise. +But you can trust the user to not look at the solution too early ;) + +## Share + +After publishing your set of exercises, open a pull request in the official Rustlings repository to link to your project in the README πŸ˜ƒ From 95f10c80689db6b156ced7fa43bd56309137cc7f Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 16:10:42 +0200 Subject: [PATCH 1019/1432] Update CHANGELOG --- CHANGELOG.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a199e4decd..38c5b2038c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,64 @@ + +## 6.0.0 (2024-07-02) + +This release is the result of a complete rewrite to deliver a ton of new features and improvements ✨ +The most important changes are highlighted below. + +### Installation + +The installation has been simplified a lot! +To install Rustlings after installing Rust, all what you need to do now is running the following command: + +```bash +cargo install rustlings +``` + +Yes, this means that Rustlings is now on [crates.io](https://crates.io/crates/rustlings) πŸŽ‰ + +You can read about the motivations of this change in [this issue](https://github.com/rust-lang/rustlings/issues/1919). + +### UI/UX + +- The UI is now responsive when the terminal is resized. +- The progress bar was moved to the bottom so that you can always see your progress and the current exercise to work on. +- The current exercise path is now a terminal link. It will open the exercise file in your default editor when you click on it. +- A small prompt is now always shown at the bottom. It allows you to choose an action by entering a character. For example, entering `h` will show you the hint of the current exercise. +- The comment "I AM NOT DONE!" doesn't exist anymore. Instead of needing to remove it to go to the next exercise, you need to enter `n` in the terminal. + +### List mode + +A list mode was added using [`Ratatui`](https://ratatui.rs). +You can enter it by entering `l` in the watch mode. +It offers the following features: + +- Browse all exercises and see their state (pending/done). +- Filter exercises based on their state (done/pending). +- Continue at another exercise. This allows you to skip some exercises or go back to previous ones. +- Reset an exercise so that you can start over and revert your changes. + +### Solutions + +After finishing an exercise, a solution file will be available and Rustlings will show you its path in green. +This allows you to compare your solution with an idiomatic solution and maybe learn about other ways to solve a problem. + +### LSP support out of the box + +Instead of creating a `project.json` file using `rustlings lsp`, Rustlings now works with a `Cargo.toml` file. +This should avoid issues related to the language server or to running exercises, especially the ones with Clippy. + +### Clippy + +Clippy lints are now shown on all exercises! πŸ“Ž +Make Clippy your friend from early on πŸ₯° + +### Third party exercises + +Rustlings now supports third-party exercises! + +Do you want to create your own set of Rustlings exercises to focus on some specific topic? +Or did you want to translate the original Rustlings exercises? +Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! + ## 5.6.1 (2023-09-18) @@ -30,7 +91,7 @@ - Swapped the order of threads and smart pointer exercises. - Rewrote the CLI to use `clap` - it's matured much since we switched to `argh` :) - `structs3`: Switched from i32 to u32. -- `move_semantics`: Switched 1-4 to tests, and rewrote them to be way simpler, while still teaching about the same +- `move_semantics`: Switched 1-4 to tests, and rewrote them to be way simpler, while still teaching about the same concepts. #### Fixed From d3a0c269994eb2b11c0a3418e4db3a275a7ee5ad Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 16:26:28 +0200 Subject: [PATCH 1020/1432] Improve the placement of TODO comments --- exercises/00_intro/intro1.rs | 4 ++-- exercises/00_intro/intro2.rs | 3 +-- exercises/01_variables/variables4.rs | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index bdbf34b059..22544cd466 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,5 +1,5 @@ -// We sometimes encourage you to keep trying things on a given exercise, even -// after you already figured it out. If you got everything working and feel +// TODO: We sometimes encourage you to keep trying things on a given exercise, +// even after you already figured it out. If you got everything working and feel // ready for the next exercise, enter `n` in the terminal. // // The exercise file will be reloaded when you change one of the lines below! diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index e443ec8fc6..c6cb6451ae 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -1,5 +1,4 @@ -// TODO: Fix the code to print "Hello world!". - fn main() { + // TODO: Fix the code to print "Hello world!". printline!("Hello world!"); } diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 8634ceb5c4..6c138b18b9 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -1,5 +1,4 @@ // TODO: Fix the compiler error. - fn main() { let x = 3; println!("Number {x}"); From 2f8fa469ac9ce6b3ccff956215cdaacc16e9dbab Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 16:26:59 +0200 Subject: [PATCH 1021/1432] Small writing changes --- CHANGELOG.md | 15 ++++++++++----- README.md | 2 +- THIRD_PARTY_EXERCISES.md | 8 ++++---- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38c5b2038c..5cc4c980cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,23 +32,28 @@ You can enter it by entering `l` in the watch mode. It offers the following features: - Browse all exercises and see their state (pending/done). -- Filter exercises based on their state (done/pending). +- Filter exercises based on their state (pending/done). - Continue at another exercise. This allows you to skip some exercises or go back to previous ones. -- Reset an exercise so that you can start over and revert your changes. +- Reset an exercise so you can start over and revert your changes. ### Solutions After finishing an exercise, a solution file will be available and Rustlings will show you its path in green. This allows you to compare your solution with an idiomatic solution and maybe learn about other ways to solve a problem. +While writing the solutions, all exercises have been polished 🌟 +For example, every exercise now contains `TODO` comments to highlight what the user needs to change and where. + ### LSP support out of the box -Instead of creating a `project.json` file using `rustlings lsp`, Rustlings now works with a `Cargo.toml` file. +Instead of creating a `project.json` file using `rustlings lsp`, Rustlings now works with a `Cargo.toml` file out of the box. +No actions are needed to activate the language server `rust-analyzer`. + This should avoid issues related to the language server or to running exercises, especially the ones with Clippy. ### Clippy -Clippy lints are now shown on all exercises! πŸ“Ž +Clippy lints are now shown on all exercises, not only the Clippy exercises πŸ“Ž Make Clippy your friend from early on πŸ₯° ### Third party exercises @@ -56,7 +61,7 @@ Make Clippy your friend from early on πŸ₯° Rustlings now supports third-party exercises! Do you want to create your own set of Rustlings exercises to focus on some specific topic? -Or did you want to translate the original Rustlings exercises? +Or do you want to translate the original Rustlings exercises? Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! diff --git a/README.md b/README.md index 4cee71cedd..373b9c7925 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ Continue practicing your Rust skills by building your own projects, contributing ## Third-Party Exercises Do you want to create your own set of Rustlings exercises to focus on some specific topic? -Or did you want to translate the original Rustlings exercises? +Or do you want to translate the original Rustlings exercises? Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! ## Uninstalling Rustlings diff --git a/THIRD_PARTY_EXERCISES.md b/THIRD_PARTY_EXERCISES.md index 2ae8b722eb..5c066941b8 100644 --- a/THIRD_PARTY_EXERCISES.md +++ b/THIRD_PARTY_EXERCISES.md @@ -1,14 +1,14 @@ # Third-Party Exercises The support of Rustlings for third-party exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. -You could also offer a translatation of the original Rustlings exercises as a third-party exercises. +You could also offer a translatation of the original Rustlings exercises as third-party exercises. ## Getting started To create third-party exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`. This command will, similar to `cargo new PROJECT_NAME`, create a template directory called `PROJECT_NAME` with all what you need to get started. -Read the comments in the generated `info.toml` file to understand the format of this file. +Read the comments in the generated `info.toml` file to understand its format. It allows you to set a custom welcome and final message and specify the metadata of every exercise. ## Create an exercise @@ -46,8 +46,8 @@ Now, add more exercises and publish them as a Git repository. Users just have to clone that repository and run `rustlings` in it to start working on your set of exercises just like the official ones. One difference to the official exercises is that the solution files will not be hidden until the user finishes an exercise. -But you can trust the user to not look at the solution too early ;) +But you can trust the users to not look at the solution too early πŸ˜‰ ## Share -After publishing your set of exercises, open a pull request in the official Rustlings repository to link to your project in the README πŸ˜ƒ +After publishing your set of exercises, open an issue or a pull request in the official Rustlings repository to link to your project in the README πŸ˜ƒ From fa452b3a93bd0557270696235f3dd25c3c968d89 Mon Sep 17 00:00:00 2001 From: "Yung Beef 4.2" <89395745+Yung-Beef@users.noreply.github.com> Date: Tue, 2 Jul 2024 18:30:54 -0300 Subject: [PATCH 1022/1432] Update README.md --- exercises/14_generics/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/14_generics/README.md b/exercises/14_generics/README.md index de46d5034a..72cff3f30e 100644 --- a/exercises/14_generics/README.md +++ b/exercises/14_generics/README.md @@ -1,7 +1,7 @@ # Generics Generics is the topic of generalizing types and functionalities to broader cases. -This is extremely useful for reducing code duplication in many ways, but can call for rather involving syntax. +This is extremely useful for reducing code duplication in many ways, but can call for some rather involved syntax. Namely, being generic requires taking great care to specify over which types a generic type is actually considered valid. The simplest and most common use of generics is for type parameters. From 33dfe5331a034a2705c45e420f52983a99e204f5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 15:24:07 +0200 Subject: [PATCH 1023/1432] Update CHANGELOG --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cc4c980cd..11502ed156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ -## 6.0.0 (2024-07-02) + +## 6.0.0 (2024-07-03) This release is the result of a complete rewrite to deliver a ton of new features and improvements ✨ The most important changes are highlighted below. @@ -27,7 +28,7 @@ You can read about the motivations of this change in [this issue](https://github ### List mode -A list mode was added using [`Ratatui`](https://ratatui.rs). +A list mode was added using [Ratatui](https://ratatui.rs). You can enter it by entering `l` in the watch mode. It offers the following features: @@ -65,6 +66,7 @@ Or do you want to translate the original Rustlings exercises? Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! + ## 5.6.1 (2023-09-18) #### Changed @@ -81,6 +83,7 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - `enums3`: Fixed formatting with `rustfmt`. + ## 5.6.0 (2023-09-04) #### Added @@ -121,6 +124,7 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Improved CI workflows, we're now testing on multiple platforms at once. + ## 5.5.1 (2023-05-17) #### Fixed @@ -128,6 +132,7 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix. + ## 5.5.0 (2023-05-17) #### Added @@ -163,6 +168,7 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Split quick installation section into two code blocks + ## 5.4.1 (2023-03-10) #### Changed From 1c010a129ed8165360061abc9f91cc747775e782 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 15:28:53 +0200 Subject: [PATCH 1024/1432] Update deps --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5908cc4bb..3d41e1a531 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,18 +1112,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", From 28d2bb04326d7036514245d73f10fb72b9ed108c Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 15:32:46 +0200 Subject: [PATCH 1025/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d41e1a531..24c1a2f575 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,7 +654,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustlings" -version = "6.0.0-beta.10" +version = "6.0.0" dependencies = [ "anyhow", "assert_cmd", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.10" +version = "6.0.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 3455e2b4b8..42bfb89f41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.10" +version = "6.0.0" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.10" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0" } serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true From 23bc5d23fea77f2587847d8329823ce435c67714 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 16:23:23 +0200 Subject: [PATCH 1026/1432] Remove install page for now, README is enough --- oranda.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/oranda.json b/oranda.json index be88e5e015..ecc490b20f 100644 --- a/oranda.json +++ b/oranda.json @@ -9,16 +9,5 @@ "domain": "rustlings.cool" } } - }, - "components": { - "artifacts": { - "auto": true, - "package_managers": { - "preferred": { - "macos/linux/unix": "curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash", - "windows": "Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1" - } - } - } } } From df64893f2b7abe288d54ab3a7d25e64fee6299b7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 17:49:41 +0200 Subject: [PATCH 1027/1432] Update CI --- .github/workflows/rust.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 689d05e1d8..9edd8eb68a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,25 +18,25 @@ jobs: fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - - uses: DavidAnson/markdownlint-cli2-action@v9 + - uses: DavidAnson/markdownlint-cli2-action@v16 with: globs: "exercises/**/*.md" - name: Run cargo fmt - run: | - cargo fmt --all -- --check + run: cargo fmt --all -- --check test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: swatinem/rust-cache@v2 - name: Run cargo test - run: | - cargo test + run: cargo test + - name: Run rustlings dev check + run: cargo run -- dev check From ad66fe00746f598da929109f934db32018547f90 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 17:51:06 +0200 Subject: [PATCH 1028/1432] Update checkout in web.yml --- .github/workflows/web.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index 5d9abe4f4e..0b998afa20 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -54,7 +54,7 @@ jobs: runs-on: ubuntu-latest steps: # Setup - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: dtolnay/rust-toolchain@stable From fe3292c170c1ca25c62d56b39168bdbd4f8932e1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 17:52:44 +0200 Subject: [PATCH 1029/1432] Remove dtolnay/rust-toolchain --- .github/workflows/rust.yml | 4 ---- .github/workflows/web.yml | 1 - 2 files changed, 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9edd8eb68a..fafd0b1054 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,9 +19,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - uses: DavidAnson/markdownlint-cli2-action@v16 with: globs: "exercises/**/*.md" @@ -34,7 +31,6 @@ jobs: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: swatinem/rust-cache@v2 - name: Run cargo test run: cargo test diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index 0b998afa20..ec5d446221 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -57,7 +57,6 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: dtolnay/rust-toolchain@stable - uses: swatinem/rust-cache@v2 # If you use any mdbook plugins, here's the place to install them! From a72c26bdc3ce59edfbc0d77f83fbcb4e5a7d1fb7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 17:53:30 +0200 Subject: [PATCH 1030/1432] Fix solution of options1 for stable Rust --- solutions/12_options/options1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs index 1ffbb04592..230af3af01 100644 --- a/solutions/12_options/options1.rs +++ b/solutions/12_options/options1.rs @@ -4,8 +4,8 @@ // `hour_of_day` is higher than 23. fn maybe_icecream(hour_of_day: u16) -> Option { match hour_of_day { - 0..22 => Some(5), - 22..24 => Some(0), + 0..=21 => Some(5), + 22..=23 => Some(0), _ => None, } } From fa6b7d77b2798985bdc7d403c8f300a9c289222c Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 3 Jul 2024 17:59:10 +0200 Subject: [PATCH 1031/1432] Run dev check only on Linux --- .github/workflows/rust.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fafd0b1054..3705e6afa0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -34,5 +34,10 @@ jobs: - uses: swatinem/rust-cache@v2 - name: Run cargo test run: cargo test + dev-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: swatinem/rust-cache@v2 - name: Run rustlings dev check run: cargo run -- dev check From c1d5252b87e12c468832b99a4db7332c8cd55ed0 Mon Sep 17 00:00:00 2001 From: Calvin Bochulak Date: Wed, 3 Jul 2024 23:20:59 -0600 Subject: [PATCH 1032/1432] fix: typo s/unwarp/unwrap/ --- exercises/18_iterators/iterators3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index b5d05f6e5e..65a0573437 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -54,7 +54,7 @@ mod tests { #[test] fn test_result_with_list() { - assert_eq!(result_with_list().unwarp(), [1, 11, 1426, 3]); + assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]); } #[test] From dec6772b050bdbc59718ac2a52f6a7752d733bd9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 11:51:33 +0200 Subject: [PATCH 1033/1432] Improve the comment of arc1 --- exercises/19_smart_pointers/arc1.rs | 9 ++++++--- solutions/19_smart_pointers/arc1.rs | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index c3d714dc30..6bb860f93f 100644 --- a/exercises/19_smart_pointers/arc1.rs +++ b/exercises/19_smart_pointers/arc1.rs @@ -1,4 +1,4 @@ -// In this exercise, we are given a `Vec` of u32 called `numbers` with values +// In this exercise, we are given a `Vec` of `u32` called `numbers` with values // ranging from 0 to 99. We would like to use this set of numbers within 8 // different threads simultaneously. Each thread is going to get the sum of // every eighth value with an offset. @@ -9,8 +9,11 @@ // … // The eighth thread (offset 7), will sum 7, 15, 23, … // -// Because we are using threads, our values need to be thread-safe. Therefore, -// we are using `Arc`. +// Each thread should own a reference-counting pointer to the vector of +// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`. +// +// Don't get distracted by how threads are spawned and joined. We will practice +// that later in the exercises about threads. // Don't change the lines below. #![forbid(unused_imports)] diff --git a/solutions/19_smart_pointers/arc1.rs b/solutions/19_smart_pointers/arc1.rs index a520dfe6af..bd76189fee 100644 --- a/solutions/19_smart_pointers/arc1.rs +++ b/solutions/19_smart_pointers/arc1.rs @@ -1,4 +1,4 @@ -// In this exercise, we are given a `Vec` of u32 called `numbers` with values +// In this exercise, we are given a `Vec` of `u32` called `numbers` with values // ranging from 0 to 99. We would like to use this set of numbers within 8 // different threads simultaneously. Each thread is going to get the sum of // every eighth value with an offset. @@ -9,8 +9,11 @@ // … // The eighth thread (offset 7), will sum 7, 15, 23, … // -// Because we are using threads, our values need to be thread-safe. Therefore, -// we are using `Arc`. +// Each thread should own a reference-counting pointer to the vector of +// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`. +// +// Don't get distracted by how threads are spawned and joined. We will practice +// that later in the exercises about threads. // Don't change the lines below. #![forbid(unused_imports)] From 248dd4415efaf090a040d4f9108f290996806a1c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:00:04 +0200 Subject: [PATCH 1034/1432] Add comment to options1 --- exercises/12_options/options1.rs | 3 ++- solutions/12_options/options1.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index 5009f8b61b..9964807859 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -19,7 +19,8 @@ mod tests { // TODO: Fix this test. How do you get the value contained in the // Option? let icecreams = maybe_icecream(12); - assert_eq!(icecreams, 5); + + assert_eq!(icecreams, 5); // Don't change this line. } #[test] diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs index 230af3af01..4d615dd602 100644 --- a/solutions/12_options/options1.rs +++ b/solutions/12_options/options1.rs @@ -22,6 +22,7 @@ mod tests { fn raw_value() { // Using `unwrap` is fine in a test. let icecreams = maybe_icecream(12).unwrap(); + assert_eq!(icecreams, 5); } From d54c05098516f2e8e5958f0b26478c85ebccb67d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:03:05 +0200 Subject: [PATCH 1035/1432] Improve a comment in errors2 --- exercises/13_error_handling/errors2.rs | 10 +++++----- solutions/13_error_handling/errors2.rs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index e50a92991d..defe359b48 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -5,11 +5,11 @@ // the player typed in the quantity, we get it as a string. They might have // typed anything, not just numbers! // -// Right now, this function isn't handling the error case at all (and isn't -// handling the success case properly either). What we want to do is: If we call -// the `total_cost` function on a string that is not a number, that function -// will return a `ParseIntError`. In that case, we want to immediately return -// that error from our function and not try to multiply and add. +// Right now, this function isn't handling the error case at all. What we want +// to do is: If we call the `total_cost` function on a string that is not a +// number, that function will return a `ParseIntError`. In that case, we want to +// immediately return that error from our function and not try to multiply and +// add. // // There are at least two ways to implement this that are both correct. But one // is a lot shorter! diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs index de7c32b5d3..f652ecb503 100644 --- a/solutions/13_error_handling/errors2.rs +++ b/solutions/13_error_handling/errors2.rs @@ -5,11 +5,11 @@ // the player typed in the quantity, we get it as a string. They might have // typed anything, not just numbers! // -// Right now, this function isn't handling the error case at all (and isn't -// handling the success case properly either). What we want to do is: If we call -// the `total_cost` function on a string that is not a number, that function -// will return a `ParseIntError`. In that case, we want to immediately return -// that error from our function and not try to multiply and add. +// Right now, this function isn't handling the error case at all. What we want +// to do is: If we call the `total_cost` function on a string that is not a +// number, that function will return a `ParseIntError`. In that case, we want to +// immediately return that error from our function and not try to multiply and +// add. // // There are at least two ways to implement this that are both correct. But one // is a lot shorter! From b8826dd3b31661927ad66d1bae2fd49f63e8d812 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:08:59 +0200 Subject: [PATCH 1036/1432] Remove comment about removing a semicolon --- exercises/00_intro/intro1.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 22544cd466..21caff5733 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -4,7 +4,6 @@ // // The exercise file will be reloaded when you change one of the lines below! // Try adding a new `println!`. -// Try removing a semicolon and see what happens in the terminal! fn main() { println!("Hello and"); From a4c07ca948dd57c71fe1894d05308af03304dec8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:10:18 +0200 Subject: [PATCH 1037/1432] Improve the comment in intro1 --- exercises/00_intro/intro1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 21caff5733..7b8baa22e9 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -3,7 +3,7 @@ // ready for the next exercise, enter `n` in the terminal. // // The exercise file will be reloaded when you change one of the lines below! -// Try adding a new `println!`. +// Try adding a new `println!` and check the updated output in the terminal. fn main() { println!("Hello and"); From b87aa986345cd80bc44c7fe7bffeb72f5fe0ddb5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:38:35 +0200 Subject: [PATCH 1038/1432] Fix warnings --- exercises/06_move_semantics/move_semantics5.rs | 2 ++ exercises/08_enums/enums2.rs | 3 ++- exercises/10_modules/modules2.rs | 1 + exercises/13_error_handling/errors4.rs | 2 ++ exercises/13_error_handling/errors6.rs | 3 +-- exercises/15_traits/traits3.rs | 2 ++ exercises/19_smart_pointers/rc1.rs | 1 + solutions/01_variables/variables3.rs | 2 ++ solutions/06_move_semantics/move_semantics5.rs | 2 ++ solutions/08_enums/enums2.rs | 3 ++- solutions/10_modules/modules2.rs | 1 + solutions/11_hashmaps/hashmaps3.rs | 4 ++-- solutions/13_error_handling/errors2.rs | 1 + solutions/13_error_handling/errors4.rs | 2 ++ solutions/13_error_handling/errors6.rs | 3 +-- solutions/15_traits/traits3.rs | 2 ++ solutions/19_smart_pointers/rc1.rs | 1 + solutions/23_conversions/try_from_into.rs | 1 + 18 files changed, 28 insertions(+), 8 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index 650656882f..13279baefd 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -1,3 +1,5 @@ +#![allow(clippy::ptr_arg)] + // TODO: Fix the compiler errors without changing anything except adding or // removing references (the character `&`). diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index 14aa29ada2..32cf2a609d 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -1,3 +1,4 @@ +#[allow(dead_code)] #[derive(Debug)] enum Message { // TODO: Define the different variants used below. @@ -5,7 +6,7 @@ enum Message { impl Message { fn call(&self) { - println!("{:?}", self); + println!("{self:?}"); } } diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 782a70eace..02eb80a957 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -1,6 +1,7 @@ // You can bring module paths into scopes and provide new names for them with // the `use` and `as` keywords. +#[allow(dead_code)] mod delicious_snacks { // TODO: Add the following two `use` statements after fixing them. // use self::fruits::PEAR as ???; diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index ba01e54bf5..e41d594576 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -1,3 +1,5 @@ +#![allow(clippy::comparison_chain)] + #[derive(PartialEq, Debug)] enum CreationError { Negative, diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index 0652abda54..b656c61706 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -35,7 +35,7 @@ impl PositiveNonzeroInteger { fn new(value: i64) -> Result { match value { x if x < 0 => Err(CreationError::Negative), - x if x == 0 => Err(CreationError::Zero), + 0 => Err(CreationError::Zero), x => Ok(Self(x as u64)), } } @@ -55,7 +55,6 @@ fn main() { #[cfg(test)] mod test { use super::*; - use std::num::IntErrorKind; #[test] fn test_parse_error() { diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index c244650da6..2e8969ebe3 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + trait Licensed { // TODO: Add a default implementation for `licensing_info` so that // implementors like the two structs below can share that default behavior diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index ecd3438701..48e19dc03c 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -8,6 +8,7 @@ use std::rc::Rc; #[derive(Debug)] struct Sun; +#[allow(dead_code)] #[derive(Debug)] enum Planet { Mercury(Rc), diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs index 7db42a9566..b493917e84 100644 --- a/solutions/01_variables/variables3.rs +++ b/solutions/01_variables/variables3.rs @@ -1,3 +1,5 @@ +#![allow(clippy::needless_late_init)] + fn main() { // Reading uninitialized variables isn't allowed in Rust! // Therefore, we need to assign a value first. diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs index 1b3ca4ebf2..678ec97b6d 100644 --- a/solutions/06_move_semantics/move_semantics5.rs +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -1,3 +1,5 @@ +#![allow(clippy::ptr_arg)] + fn main() { let data = "Rust is great!".to_string(); diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs index 13175dd3f9..b19394c62f 100644 --- a/solutions/08_enums/enums2.rs +++ b/solutions/08_enums/enums2.rs @@ -1,3 +1,4 @@ +#[allow(dead_code)] #[derive(Debug)] enum Message { Move { x: i64, y: i64 }, @@ -8,7 +9,7 @@ enum Message { impl Message { fn call(&self) { - println!("{:?}", self); + println!("{self:?}"); } } diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs index 55c316d706..298d76eb1d 100644 --- a/solutions/10_modules/modules2.rs +++ b/solutions/10_modules/modules2.rs @@ -1,3 +1,4 @@ +#[allow(dead_code)] mod delicious_snacks { // Added `pub` and used the expected alias after `as`. pub use self::fruits::PEAR as fruit; diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index f4059bbe87..54f480b9bc 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -28,13 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, Team> { let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); // Insert the default with zeros if a team doesn't exist yet. - let mut team_1 = scores.entry(team_1_name).or_insert_with(|| Team::default()); + let team_1 = scores.entry(team_1_name).or_insert_with(Team::default); // Update the values. team_1.goals_scored += team_1_score; team_1.goals_conceded += team_2_score; // Similarely for the second team. - let mut team_2 = scores.entry(team_2_name).or_insert_with(|| Team::default()); + let team_2 = scores.entry(team_2_name).or_insert_with(Team::default); team_2.goals_scored += team_2_score; team_2.goals_conceded += team_1_score; } diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs index f652ecb503..0597c8c9d9 100644 --- a/solutions/13_error_handling/errors2.rs +++ b/solutions/13_error_handling/errors2.rs @@ -16,6 +16,7 @@ use std::num::ParseIntError; +#[allow(unused_variables)] fn total_cost(item_quantity: &str) -> Result { let processing_fee = 1; let cost_per_item = 5; diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs index c43f493b71..f4d39bf9c4 100644 --- a/solutions/13_error_handling/errors4.rs +++ b/solutions/13_error_handling/errors4.rs @@ -1,3 +1,5 @@ +#![allow(clippy::comparison_chain)] + #[derive(PartialEq, Debug)] enum CreationError { Negative, diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs index 70680cf0b6..429d3ea319 100644 --- a/solutions/13_error_handling/errors6.rs +++ b/solutions/13_error_handling/errors6.rs @@ -36,7 +36,7 @@ impl PositiveNonzeroInteger { fn new(value: i64) -> Result { match value { x if x < 0 => Err(CreationError::Negative), - x if x == 0 => Err(CreationError::Zero), + 0 => Err(CreationError::Zero), x => Ok(Self(x as u64)), } } @@ -57,7 +57,6 @@ fn main() { #[cfg(test)] mod test { use super::*; - use std::num::IntErrorKind; #[test] fn test_parse_error() { diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs index 3d8ec85ead..747d9190d2 100644 --- a/solutions/15_traits/traits3.rs +++ b/solutions/15_traits/traits3.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + trait Licensed { fn licensing_info(&self) -> String { "Default license".to_string() diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs index c0a41abfa8..512eb9cea2 100644 --- a/solutions/19_smart_pointers/rc1.rs +++ b/solutions/19_smart_pointers/rc1.rs @@ -8,6 +8,7 @@ use std::rc::Rc; #[derive(Debug)] struct Sun; +#[allow(dead_code)] #[derive(Debug)] enum Planet { Mercury(Rc), diff --git a/solutions/23_conversions/try_from_into.rs b/solutions/23_conversions/try_from_into.rs index acb7721d64..ee802eb06e 100644 --- a/solutions/23_conversions/try_from_into.rs +++ b/solutions/23_conversions/try_from_into.rs @@ -4,6 +4,7 @@ // type itself. You can read more about it in the documentation: // https://doc.rust-lang.org/std/convert/trait.TryFrom.html +#![allow(clippy::useless_vec)] use std::convert::{TryFrom, TryInto}; #[derive(Debug, PartialEq)] From b017b87866962a2101016ce28534b9b37f47c4d2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:38:41 +0200 Subject: [PATCH 1039/1432] Fix typo --- solutions/01_variables/variables3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs index b493917e84..15f6557ce7 100644 --- a/solutions/01_variables/variables3.rs +++ b/solutions/01_variables/variables3.rs @@ -7,7 +7,7 @@ fn main() { println!("Number {x}"); - // It possible to declare a variable and initialize it later. + // It is possible to declare a variable and initialize it later. // But it can't be used before initialization. let y: i32; y = 42; From 2f60f4d9ea23679f81b7f91f1b8bb59d20d304e5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:38:57 +0200 Subject: [PATCH 1040/1432] Remove newline at the end of multiline strings --- rustlings-macros/info.toml | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 488fdacff2..bd73195a80 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -16,16 +16,14 @@ get started, here are some notes about how Rustlings operates: 3. If you're stuck on an exercise, enter `h` to show a hint. 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, - other learners do too so you can help each other out! -""" + other learners do too so you can help each other out!""" final_message = """We hope you enjoyed learning about the various aspects of Rust! If you noticed any issues, don't hesitate to report them on Github. You can also contribute your own exercises to help the greater community! Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md -""" +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md""" # INTRO @@ -130,8 +128,7 @@ The type of Constants must always be annotated. Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability': -https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants -""" +https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants""" # FUNCTIONS @@ -312,8 +309,7 @@ In Rust, there are two ways to define a Vector. the initial values. Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html -of the Rust book to learn more. -""" +of the Rust book to learn more.""" [[exercises]] name = "vecs2" @@ -327,8 +323,7 @@ In the second function, we map the values of the input and collect them into a v After you've completed both functions, decide for yourself which approach you like better. -What do you think is the more commonly used pattern under Rust developers? -""" +What do you think is the more commonly used pattern under Rust developers?""" # MOVE SEMANTICS @@ -355,8 +350,7 @@ We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's being "moved" into `vec1`, meaning we can't access `vec0` anymore. You could make another, separate version of the data that's in `vec0` and -pass it to `fill_vec` instead. -""" +pass it to `fill_vec` instead.""" [[exercises]] name = "move_semantics3" @@ -375,8 +369,7 @@ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of `x` immediately after the mutable reference is taken? Read more about 'Mutable References' in the book's section 'References and Borrowing': -https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. -""" +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.""" [[exercises]] name = "move_semantics5" @@ -517,8 +510,7 @@ Example: `placeholder("blue");` should become `string_slice("blue");` -because "blue" is `&str`, not `String`. -""" +because "blue" is `&str`, not `String`.""" # MODULES @@ -620,8 +612,7 @@ Remember that `Option`s can be nested in if-let and while-let statements. For example: `if let Some(Some(x)) = y` -Also see `Option::flatten` -""" +Also see `Option::flatten`""" [[exercises]] name = "options3" @@ -813,8 +804,7 @@ Here is how to specify a trait bound for an implementation block: `impl for Foo { … }` You may need this: -`use std::fmt::Display;` -""" +`use std::fmt::Display;`""" # LIFETIMES From 30d5b7db927b0a69d21e010d676d09de09c03e70 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:41:03 +0200 Subject: [PATCH 1041/1432] Require solutions --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3705e6afa0..b68cb1b30e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -40,4 +40,4 @@ jobs: - uses: actions/checkout@v4 - uses: swatinem/rust-cache@v2 - name: Run rustlings dev check - run: cargo run -- dev check + run: cargo run -- dev check --require-solutions From 05246321997ed256fda4de8f08e7cfccb88bf32e Mon Sep 17 00:00:00 2001 From: Ramkumar Date: Thu, 4 Jul 2024 18:48:09 +0530 Subject: [PATCH 1042/1432] fix move_semantics5 to change misleading compiler error --- exercises/06_move_semantics/move_semantics5.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index 13279baefd..fc59338701 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -18,7 +18,7 @@ fn get_char(data: String) -> char { // Should take ownership fn string_uppercase(mut data: &String) { - data = &data.to_uppercase(); + data = data.to_uppercase(); println!("{data}"); } From 0b220f9fffd02f77de1de79f510ae10c7c53b81f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 19:46:04 +0200 Subject: [PATCH 1043/1432] Fix clippy1 --- exercises/22_clippy/clippy1.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index b9d1ec1729..7165da4559 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -4,11 +4,9 @@ // 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. -use std::f32::consts::PI; - fn main() { - // Use the more accurate `PI` constant. - let pi = PI; + // TODO: Fix the Clippy lint in this line. + let pi = 3.14; let radius: f32 = 5.0; let area = pi * radius.powi(2); From 74831dd88f5f225ff940a9b321dc26c49a12e22d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 19:46:09 +0200 Subject: [PATCH 1044/1432] Add TODO --- exercises/quizzes/quiz1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/quizzes/quiz1.rs b/exercises/quizzes/quiz1.rs index 5f17514be4..afbf1f946f 100644 --- a/exercises/quizzes/quiz1.rs +++ b/exercises/quizzes/quiz1.rs @@ -6,8 +6,8 @@ // Mary is buying apples. The price of an apple is calculated as follows: // - An apple costs 2 rustbucks. // - 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. +// TODO: Write a function that calculates the price of an order of apples given +// the quantity bought. // Put your function here! // fn calculate_price_of_apples(???) -> ??? { From 84b291852c3cb91cd5e91a66e63d0019eb0d1f67 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 19:48:09 +0200 Subject: [PATCH 1045/1432] Update deps --- Cargo.lock | 58 +++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24c1a2f575..f3643d7579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "castaway" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] @@ -527,7 +527,7 @@ dependencies = [ "libc", "redox_syscall 0.5.2", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -977,7 +977,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -997,18 +997,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1019,9 +1019,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1031,9 +1031,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1043,15 +1043,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1061,9 +1061,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1073,9 +1073,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1085,9 +1085,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1097,9 +1097,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" From 48105550383e4536bea9bc6d403cc5d50c51948a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 20:02:30 +0200 Subject: [PATCH 1046/1432] Update CHANGELOG --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11502ed156..9025d4510a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ + + +## 6.0.1 (2024-07-04) + +Small exercise improvements and fixes. +Most importantly, fixed that the exercise `clippy1` was already solved πŸ˜… + ## 6.0.0 (2024-07-03) From b8fcd112866522ca574fd8a0d28562a9ffd3e10d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 20:02:43 +0200 Subject: [PATCH 1047/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3643d7579..5da969ccf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,7 +654,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustlings" -version = "6.0.0" +version = "6.0.1" dependencies = [ "anyhow", "assert_cmd", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0" +version = "6.0.1" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 42bfb89f41..22141144e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0" +version = "6.0.1" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.1" } serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true From a3657188b63d20b33f6770552169a473f021228f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 20:28:46 +0200 Subject: [PATCH 1048/1432] Check for missing TODO comments --- src/dev/check.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dev/check.rs b/src/dev/check.rs index 5074c133db..29b2b67016 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -92,6 +92,10 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result Date: Thu, 4 Jul 2024 11:58:09 -0700 Subject: [PATCH 1049/1432] Fix misleading test name --- exercises/06_move_semantics/move_semantics4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index c225f3ba00..83a03440c0 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -7,7 +7,7 @@ mod tests { // TODO: Fix the compiler errors only by reordering the lines in the test. // Don't add, change or remove any line. #[test] - fn move_semantics5() { + fn move_semantics4() { let mut x = 100; let y = &mut x; let z = &mut x; From 4bf0ddc0e1b6213b9ddd7b804c19326774cdbf61 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 21:12:57 +0200 Subject: [PATCH 1050/1432] Check exercises unsolved --- rustlings-macros/info.toml | 1 + src/dev/check.rs | 58 ++++++++++++++++++++++++++++++++------ src/info_file.rs | 5 +++- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index bd73195a80..d75d73fc1e 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -31,6 +31,7 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md""" name = "intro1" dir = "00_intro" test = false +skip_check_unsolved = true hint = """ Enter `n` to move on to the next exercise. You might need to press ENTER after typing `n`.""" diff --git a/src/dev/check.rs b/src/dev/check.rs index 29b2b67016..5c35462c40 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -162,7 +162,46 @@ fn check_unexpected_files( Ok(()) } -fn check_exercises(info_file: &InfoFile) -> Result<()> { +fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<()> { + let error_occurred = AtomicBool::new(false); + + println!( + "Running all exercises to check that they aren't already solved. This may take a while…\n", + ); + thread::scope(|s| { + for exercise_info in &info_file.exercises { + if exercise_info.skip_check_unsolved { + continue; + } + + s.spawn(|| { + let error = |e| { + let mut stderr = io::stderr().lock(); + stderr.write_all(e).unwrap(); + stderr.write_all(b"\nProblem with the exercise ").unwrap(); + stderr.write_all(exercise_info.name.as_bytes()).unwrap(); + stderr.write_all(SEPARATOR).unwrap(); + error_occurred.store(true, atomic::Ordering::Relaxed); + }; + + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + match exercise_info.run_exercise(&mut output, target_dir) { + Ok(true) => error(b"Already solved!"), + Ok(false) => (), + Err(e) => error(e.to_string().as_bytes()), + } + }); + } + }); + + if error_occurred.load(atomic::Ordering::Relaxed) { + bail!(CHECK_EXERCISES_UNSOLVED_ERR); + } + + Ok(()) +} + +fn check_exercises(info_file: &InfoFile, target_dir: &Path) -> 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"), @@ -172,15 +211,14 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> { let info_file_paths = check_info_file_exercises(info_file)?; check_unexpected_files("exercises", &info_file_paths)?; - Ok(()) + check_exercises_unsolved(info_file, target_dir) } -fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> { - let target_dir = parse_target_dir()?; +fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &Path) -> Result<()> { let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len())); let error_occurred = AtomicBool::new(false); - println!("Running all solutions. This may take a while...\n"); + println!("Running all solutions. This may take a while…\n"); thread::scope(|s| { for exercise_info in &info_file.exercises { s.spawn(|| { @@ -206,7 +244,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile) -> Result<()> } let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - match exercise_info.run_solution(&mut output, &target_dir) { + match exercise_info.run_solution(&mut output, target_dir) { Ok(true) => { paths.lock().unwrap().insert(PathBuf::from(path)); } @@ -242,8 +280,9 @@ pub fn check(require_solutions: bool) -> Result<()> { check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"")?; } - check_exercises(&info_file)?; - check_solutions(require_solutions, &info_file)?; + let target_dir = parse_target_dir()?; + check_exercises(&info_file, &target_dir)?; + check_solutions(require_solutions, &info_file, &target_dir)?; println!("\nEverything looks fine!"); @@ -252,3 +291,6 @@ pub fn check(require_solutions: bool) -> Result<()> { const SEPARATOR: &[u8] = b"\n========================================================================================\n"; + +const CHECK_EXERCISES_UNSOLVED_ERR: &str = "At least one exercise is already solved or failed to run. See the output above. +If this is an intro exercise that is intended to be already solved, add `skip_check_unsolved = true` to the exercise's metadata in the `info.toml` file."; diff --git a/src/info_file.rs b/src/info_file.rs index f226f73540..f27d018593 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -11,14 +11,17 @@ pub struct ExerciseInfo { pub name: String, /// Exercise's directory name inside the `exercises/` directory. pub dir: Option, - #[serde(default = "default_true")] /// Run `cargo test` on the exercise. + #[serde(default = "default_true")] pub test: bool, /// Deny all Clippy warnings. #[serde(default)] pub strict_clippy: bool, /// The exercise's hint to be shown to the user on request. pub hint: String, + /// The exercise is already solved. Ignore it when checking that all exercises are unsolved. + #[serde(default)] + pub skip_check_unsolved: bool, } #[inline(always)] const fn default_true() -> bool { From a33501e6a7b933c1aa9a0a238edcd20920421249 Mon Sep 17 00:00:00 2001 From: Nahor Date: Thu, 4 Jul 2024 14:23:34 -0700 Subject: [PATCH 1051/1432] Unify fn signature in iterators4 exercise and solution Since this is not about conversion, prefer the option that doesn't require one. --- exercises/18_iterators/iterators4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 08ba3650de..8381dbb185 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,4 +1,4 @@ -fn factorial(num: u8) -> u64 { +fn factorial(num: u64) -> u64 { // TODO: Complete this function to return the factorial of `num`. // Do not use: // - early returns (using the `return` keyword explicitly) From 652f0c7676f928ed8a349ce0dc7a309f2e0b7d7a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 23:38:56 +0200 Subject: [PATCH 1052/1432] Fix tests --- src/cargo_toml.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index c4d6700a86..445b6b57b3 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -111,6 +111,7 @@ mod tests { test: true, strict_clippy: true, hint: String::new(), + skip_check_unsolved: false, }, ExerciseInfo { name: String::from("2"), @@ -118,6 +119,7 @@ mod tests { test: false, strict_clippy: false, hint: String::new(), + skip_check_unsolved: false, }, ]; From deed9d3943f837f76fcf2ef30348329f1a706f2a Mon Sep 17 00:00:00 2001 From: Nahor Date: Thu, 4 Jul 2024 15:34:37 -0700 Subject: [PATCH 1053/1432] Add alternative solution for iterators5 --- solutions/18_iterators/iterators5.rs | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs index 402c81b0e2..85d9a4f9bf 100644 --- a/solutions/18_iterators/iterators5.rs +++ b/solutions/18_iterators/iterators5.rs @@ -47,6 +47,23 @@ fn count_collection_iterator(collection: &[HashMap], value: Pr .sum() } +// Equivalent to `count_collection_iterator`+`count_iterator`, iterating as if +// the collection was a single container instead of a container of containers +// (and more accurately, a single iterator instead of an iterator of iterators). +fn count_collection_iterator_flat( + collection: &[HashMap], + value: Progress, +) -> usize { + // `collection` is a slice of hash maps. + // collection = [{ "variables1": Complete, "from_str": None, … }, + // { "variables2": Complete, … }, … ] + collection + .iter() + .flat_map(HashMap::values) // or just `.flatten()` when wanting the default iterator (`HashMap::iter`) + .filter(|val| **val == value) + .count() +} + fn main() { // You can optionally experiment here. } @@ -121,18 +138,30 @@ mod tests { count_collection_iterator(&collection, Progress::Complete), 6, ); + assert_eq!( + count_collection_iterator_flat(&collection, Progress::Complete), + 6, + ); } #[test] fn count_collection_some() { let collection = get_vec_map(); assert_eq!(count_collection_iterator(&collection, Progress::Some), 1); + assert_eq!( + count_collection_iterator_flat(&collection, Progress::Some), + 1 + ); } #[test] fn count_collection_none() { let collection = get_vec_map(); assert_eq!(count_collection_iterator(&collection, Progress::None), 4); + assert_eq!( + count_collection_iterator_flat(&collection, Progress::None), + 4 + ); } #[test] @@ -145,6 +174,10 @@ mod tests { count_collection_for(&collection, progress_state), count_collection_iterator(&collection, progress_state), ); + assert_eq!( + count_collection_for(&collection, progress_state), + count_collection_iterator_flat(&collection, progress_state), + ); } } } From 43d15f09f00a1c4f795550cd3515803d14962211 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Jul 2024 13:41:04 +0200 Subject: [PATCH 1054/1432] Readd "structs" --- rustlings-macros/info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 8b72046f61..715778ddb5 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -445,7 +445,7 @@ dir = "08_enums" test = false hint = """ You can create enumerations that have different variants with different types -such as anonymous structs, a single string, tuples, no data, etc.""" +such as anonymous structs, structs, a single string, tuples, no data, etc.""" [[exercises]] name = "enums3" From e6f6d26d131e3a3ee89248c1fdaba22d8d1d79e2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Jul 2024 11:49:56 +0200 Subject: [PATCH 1055/1432] Import enum variants in all tests --- solutions/18_iterators/iterators5.rs | 43 +++++++++------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs index 85d9a4f9bf..067a117b92 100644 --- a/solutions/18_iterators/iterators5.rs +++ b/solutions/18_iterators/iterators5.rs @@ -47,8 +47,8 @@ fn count_collection_iterator(collection: &[HashMap], value: Pr .sum() } -// Equivalent to `count_collection_iterator`+`count_iterator`, iterating as if -// the collection was a single container instead of a container of containers +// Equivalent to `count_collection_iterator` and `count_iterator`, iterating as +// if the collection was a single container instead of a container of containers // (and more accurately, a single iterator instead of an iterator of iterators). fn count_collection_iterator_flat( collection: &[HashMap], @@ -71,10 +71,9 @@ fn main() { #[cfg(test)] mod tests { use super::*; + use Progress::*; fn get_map() -> HashMap { - use Progress::*; - let mut map = HashMap::new(); map.insert(String::from("variables1"), Complete); map.insert(String::from("functions1"), Complete); @@ -87,8 +86,6 @@ mod tests { } fn get_vec_map() -> Vec> { - use Progress::*; - let map = get_map(); let mut other = HashMap::new(); @@ -104,25 +101,25 @@ mod tests { #[test] fn count_complete() { let map = get_map(); - assert_eq!(count_iterator(&map, Progress::Complete), 3); + assert_eq!(count_iterator(&map, Complete), 3); } #[test] fn count_some() { let map = get_map(); - assert_eq!(count_iterator(&map, Progress::Some), 1); + assert_eq!(count_iterator(&map, Some), 1); } #[test] fn count_none() { let map = get_map(); - assert_eq!(count_iterator(&map, Progress::None), 2); + assert_eq!(count_iterator(&map, None), 2); } #[test] fn count_complete_equals_for() { let map = get_map(); - let progress_states = [Progress::Complete, Progress::Some, Progress::None]; + let progress_states = [Complete, Some, None]; for progress_state in progress_states { assert_eq!( count_for(&map, progress_state), @@ -134,40 +131,28 @@ mod tests { #[test] fn count_collection_complete() { let collection = get_vec_map(); - assert_eq!( - count_collection_iterator(&collection, Progress::Complete), - 6, - ); - assert_eq!( - count_collection_iterator_flat(&collection, Progress::Complete), - 6, - ); + assert_eq!(count_collection_iterator(&collection, Complete), 6); + assert_eq!(count_collection_iterator_flat(&collection, Complete), 6); } #[test] fn count_collection_some() { let collection = get_vec_map(); - assert_eq!(count_collection_iterator(&collection, Progress::Some), 1); - assert_eq!( - count_collection_iterator_flat(&collection, Progress::Some), - 1 - ); + assert_eq!(count_collection_iterator(&collection, Some), 1); + assert_eq!(count_collection_iterator_flat(&collection, Some), 1); } #[test] fn count_collection_none() { let collection = get_vec_map(); - assert_eq!(count_collection_iterator(&collection, Progress::None), 4); - assert_eq!( - count_collection_iterator_flat(&collection, Progress::None), - 4 - ); + assert_eq!(count_collection_iterator(&collection, None), 4); + assert_eq!(count_collection_iterator_flat(&collection, None), 4); } #[test] fn count_collection_equals_for() { let collection = get_vec_map(); - let progress_states = [Progress::Complete, Progress::Some, Progress::None]; + let progress_states = [Complete, Some, None]; for progress_state in progress_states { assert_eq!( From 584164a6ffff87d2b10916f25e8172a3e5f1daa1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Jul 2024 14:11:03 +0200 Subject: [PATCH 1056/1432] Adjust enums exercises --- exercises/08_enums/enums1.rs | 4 +-- exercises/08_enums/enums2.rs | 18 +++++++---- exercises/08_enums/enums3.rs | 53 ++++++++++++++++-------------- solutions/08_enums/enums1.rs | 10 +++--- solutions/08_enums/enums2.rs | 18 +++++++++-- solutions/08_enums/enums3.rs | 63 +++++++++++++++++++++++------------- 6 files changed, 102 insertions(+), 64 deletions(-) diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index c65e641ae1..c0d0c308d2 100644 --- a/exercises/08_enums/enums1.rs +++ b/exercises/08_enums/enums1.rs @@ -4,9 +4,9 @@ enum Message { } fn main() { - println!("{:?}", Message::Quit); - println!("{:?}", Message::Echo); println!("{:?}", Message::Resize); println!("{:?}", Message::Move); + println!("{:?}", Message::Echo); println!("{:?}", Message::ChangeColor); + println!("{:?}", Message::Quit); } diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index 5a74991a3f..29ed1b6f5b 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -1,13 +1,14 @@ -#[allow(dead_code)] +#![allow(dead_code)] + #[derive(Debug)] -enum Message { - // TODO: Define the different variants used below. +struct Point { + x: u64, + y: u64, } #[derive(Debug)] -struct Point { - x: u8, - y: u8, +enum Message { + // TODO: Define the different variants used below. } impl Message { @@ -18,7 +19,10 @@ impl Message { fn main() { let messages = [ - Message::Resize { w: 10, h: 30 }, + Message::Resize { + width: 10, + height: 30, + }, Message::Move(Point { x: 10, y: 15 }), Message::Echo(String::from("hello world")), Message::ChangeColor(200, 255, 255), diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index 2d0d82d4ea..f49707ca14 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -1,41 +1,41 @@ -enum Message { - // TODO: Implement the message variant types based on their usage below. +struct Point { + x: u64, + y: u64, } -struct Point { - x: u8, - y: u8, +enum Message { + // TODO: Implement the message variant types based on their usage below. } struct State { - color: (u8, u8, u8), - width: u8, - height: u8, + width: u64, + height: u64, position: Point, - quit: bool, message: String, + color: (u8, u8, u8), + quit: bool, } impl State { - fn change_color(&mut self, color: (u8, u8, u8)) { - self.color = color; + fn resize(&mut self, width: u64, height: u64) { + self.width = width; + self.height = height; } - fn quit(&mut self) { - self.quit = true; + fn move_position(&mut self, point: Point) { + self.position = point; } fn echo(&mut self, s: String) { self.message = s; } - fn resize(&mut self, width: u8, height: u8) { - self.width = width; - self.height = height; + fn change_color(&mut self, color: (u8, u8, u8)) { + self.color = color; } - fn move_position(&mut self, point: Point) { - self.position = point; + fn quit(&mut self) { + self.quit = true; } fn process(&mut self, message: Message) { @@ -56,26 +56,29 @@ mod tests { #[test] fn test_match_message_call() { let mut state = State { - quit: false, width: 0, height: 0, position: Point { x: 0, y: 0 }, - color: (0, 0, 0), message: String::from("hello world"), + color: (0, 0, 0), + quit: false, }; - state.process(Message::ChangeColor(255, 0, 255)); - state.process(Message::Echo(String::from("Hello world!"))); - state.process(Message::Resize { w: 10, h: 30 }); + state.process(Message::Resize { + width: 10, + height: 30, + }); state.process(Message::Move(Point { x: 10, y: 15 })); + state.process(Message::Echo(String::from("Hello world!"))); + state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Quit); - assert_eq!(state.color, (255, 0, 255)); assert_eq!(state.width, 10); assert_eq!(state.height, 30); assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); - assert!(state.quit); assert_eq!(state.message, "Hello world!"); + assert_eq!(state.color, (255, 0, 255)); + assert!(state.quit); } } diff --git a/solutions/08_enums/enums1.rs b/solutions/08_enums/enums1.rs index 9724883490..97a5cc0639 100644 --- a/solutions/08_enums/enums1.rs +++ b/solutions/08_enums/enums1.rs @@ -1,14 +1,16 @@ #[derive(Debug)] enum Message { - Quit, - Echo, + Resize, Move, + Echo, ChangeColor, + Quit, } fn main() { - println!("{:?}", Message::Quit); - println!("{:?}", Message::Echo); + println!("{:?}", Message::Resize); println!("{:?}", Message::Move); + println!("{:?}", Message::Echo); println!("{:?}", Message::ChangeColor); + println!("{:?}", Message::Quit); } diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs index b19394c62f..2ee0553a66 100644 --- a/solutions/08_enums/enums2.rs +++ b/solutions/08_enums/enums2.rs @@ -1,7 +1,15 @@ -#[allow(dead_code)] +#![allow(dead_code)] + +#[derive(Debug)] +struct Point { + x: u64, + y: u64, +} + #[derive(Debug)] enum Message { - Move { x: i64, y: i64 }, + Resize { width: u64, height: u64 }, + Move(Point), Echo(String), ChangeColor(u8, u8, u8), Quit, @@ -15,7 +23,11 @@ impl Message { fn main() { let messages = [ - Message::Move { x: 10, y: 30 }, + Message::Resize { + width: 10, + height: 30, + }, + Message::Move(Point { x: 10, y: 15 }), Message::Echo(String::from("hello world")), Message::ChangeColor(200, 255, 255), Message::Quit, diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs index 8baa25c1b3..8641fbdb5f 100644 --- a/solutions/08_enums/enums3.rs +++ b/solutions/08_enums/enums3.rs @@ -1,44 +1,53 @@ +struct Point { + x: u64, + y: u64, +} + enum Message { - ChangeColor(u8, u8, u8), - Echo(String), + Resize { width: u64, height: u64 }, Move(Point), + Echo(String), + ChangeColor(u8, u8, u8), Quit, } -struct Point { - x: u8, - y: u8, -} - struct State { - color: (u8, u8, u8), + width: u64, + height: u64, position: Point, - quit: bool, message: String, + color: (u8, u8, u8), + quit: bool, } impl State { - fn change_color(&mut self, color: (u8, u8, u8)) { - self.color = color; + fn resize(&mut self, width: u64, height: u64) { + self.width = width; + self.height = height; } - fn quit(&mut self) { - self.quit = true; + fn move_position(&mut self, point: Point) { + self.position = point; } fn echo(&mut self, s: String) { self.message = s; } - fn move_position(&mut self, point: Point) { - self.position = point; + fn change_color(&mut self, color: (u8, u8, u8)) { + self.color = color; + } + + fn quit(&mut self) { + self.quit = true; } fn process(&mut self, message: Message) { match message { - Message::ChangeColor(r, g, b) => self.change_color((r, g, b)), - Message::Echo(s) => self.echo(s), + Message::Resize { width, height } => self.resize(width, height), Message::Move(point) => self.move_position(point), + Message::Echo(s) => self.echo(s), + Message::ChangeColor(r, g, b) => self.change_color((r, g, b)), Message::Quit => self.quit(), } } @@ -55,21 +64,29 @@ mod tests { #[test] fn test_match_message_call() { let mut state = State { - quit: false, + width: 0, + height: 0, position: Point { x: 0, y: 0 }, - color: (0, 0, 0), message: String::from("hello world"), + color: (0, 0, 0), + quit: false, }; - state.process(Message::ChangeColor(255, 0, 255)); - state.process(Message::Echo(String::from("Hello world!"))); + state.process(Message::Resize { + width: 10, + height: 30, + }); state.process(Message::Move(Point { x: 10, y: 15 })); + state.process(Message::Echo(String::from("Hello world!"))); + state.process(Message::ChangeColor(255, 0, 255)); state.process(Message::Quit); - assert_eq!(state.color, (255, 0, 255)); + assert_eq!(state.width, 10); + assert_eq!(state.height, 30); assert_eq!(state.position.x, 10); assert_eq!(state.position.y, 15); - assert!(state.quit); assert_eq!(state.message, "Hello world!"); + assert_eq!(state.color, (255, 0, 255)); + assert!(state.quit); } } From 2f4e63b443709deb70892b45418d7c85ce714d61 Mon Sep 17 00:00:00 2001 From: Matt Nield <64328730+matthewjnield@users.noreply.github.com> Date: Fri, 5 Jul 2024 08:27:51 -0400 Subject: [PATCH 1057/1432] fix: Add clarification to instructions for hashmaps2.rs In the instructions for hashmaps2.rs, the last sentence of the include the phrase "these fruits", which refers to fruits that were mentioned two sentences prior. Having a sentence in between the first sentence in which the fruits were described and a later sentence in which the phrase "these fruits" is used makes this confusing to read, since the phrase "these fruits" does not come immediately after the mention of the fruits that the phrase refers to. This pull request expands the last sentence to explicitly refer to the fruits being mentioned, in order to add clarity about the requirement of the exercise. --- exercises/11_hashmaps/hashmaps2.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index b3691b68f2..376970d730 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -5,7 +5,8 @@ // Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You // must add fruit to the basket so that there is at least one of each kind and // more than 11 in total - we have a lot of mouths to feed. You are not allowed -// to insert any more of these fruits! +// to insert any more of the fruits that are already in the basket (Apple, +// Mango, and Lyche). use std::collections::HashMap; From db5911eb739d79c9b6fd44a510d77c6b80f49368 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Jul 2024 15:31:39 +0200 Subject: [PATCH 1058/1432] Use *= --- solutions/23_conversions/as_ref_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/23_conversions/as_ref_mut.rs b/solutions/23_conversions/as_ref_mut.rs index 91b12bacc1..af62e2d8ce 100644 --- a/solutions/23_conversions/as_ref_mut.rs +++ b/solutions/23_conversions/as_ref_mut.rs @@ -15,7 +15,7 @@ fn char_counter>(arg: T) -> usize { // Squares a number using `as_mut()`. fn num_sq>(arg: &mut T) { let arg = arg.as_mut(); - *arg = *arg * *arg; + *arg *= *arg; } fn main() { From f5a4965de7174e122c5d99ec0a300285d0a0c658 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Jul 2024 15:38:33 +0200 Subject: [PATCH 1059/1432] Improve wording in quiz1 --- exercises/quizzes/quiz1.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/quizzes/quiz1.rs b/exercises/quizzes/quiz1.rs index afbf1f946f..a79a162443 100644 --- a/exercises/quizzes/quiz1.rs +++ b/exercises/quizzes/quiz1.rs @@ -5,7 +5,8 @@ // // Mary is buying apples. The price of an apple is calculated as follows: // - An apple costs 2 rustbucks. -// - If Mary buys more than 40 apples, each apple only costs 1 rustbuck! +// - However, if Mary buys more than 40 apples, the price of each apple in the +// entire order is reduced to only 1 rustbuck! // TODO: Write a function that calculates the price of an order of apples given // the quantity bought. From 65834fc42012cb6c0412757b830294e9af26549f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Jul 2024 15:38:59 +0200 Subject: [PATCH 1060/1432] Improve quizes --- exercises/quizzes/quiz1.rs | 5 ++--- solutions/quizzes/quiz1.rs | 5 ++--- solutions/quizzes/quiz2.rs | 7 ------- solutions/quizzes/quiz3.rs | 4 ---- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/exercises/quizzes/quiz1.rs b/exercises/quizzes/quiz1.rs index a79a162443..04fb2aaf8d 100644 --- a/exercises/quizzes/quiz1.rs +++ b/exercises/quizzes/quiz1.rs @@ -7,11 +7,10 @@ // - An apple costs 2 rustbucks. // - However, if Mary buys more than 40 apples, the price of each apple in the // entire order is reduced to only 1 rustbuck! + // TODO: Write a function that calculates the price of an order of apples given // the quantity bought. - -// Put your function here! -// fn calculate_price_of_apples(???) -> ??? { +// fn calculate_price_of_apples(???) -> ??? { ??? } fn main() { // You can optionally experiment here. diff --git a/solutions/quizzes/quiz1.rs b/solutions/quizzes/quiz1.rs index bc76166731..5503c8c742 100644 --- a/solutions/quizzes/quiz1.rs +++ b/solutions/quizzes/quiz1.rs @@ -1,8 +1,7 @@ // Mary is buying apples. The price of an apple is calculated as follows: // - An apple costs 2 rustbucks. -// - 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. +// - However, if Mary buys more than 40 apples, the price of each apple in the +// entire order is reduced to only 1 rustbuck! fn calculate_price_of_apples(n_apples: u64) -> u64 { if n_apples > 40 { diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs index 0d2a51324b..2f5cc050ca 100644 --- a/solutions/quizzes/quiz2.rs +++ b/solutions/quizzes/quiz2.rs @@ -1,10 +1,3 @@ -// This is a quiz for the following sections: -// - Strings -// - Vecs -// - Move semantics -// - Modules -// - Enums -// // Let's build a little machine in the form of a function. As input, we're going // to give a list of strings and commands. These commands determine what action // is going to be applied to the string. It can either be: diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs index e3413fd01e..7b9127820d 100644 --- a/solutions/quizzes/quiz3.rs +++ b/solutions/quizzes/quiz3.rs @@ -1,7 +1,3 @@ -// This quiz tests: -// - Generics -// - Traits -// // An imaginary magical school has a new report card generation system written // in Rust! Currently, the system only supports creating report cards where the // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the From fdada8b3d4f8a0d90804cae3e421875be76a109e Mon Sep 17 00:00:00 2001 From: Matt Nield <64328730+matthewjnield@users.noreply.github.com> Date: Fri, 5 Jul 2024 16:04:07 -0400 Subject: [PATCH 1061/1432] chore: Update errors5.rs exercise to be consistent with solution In errors5.rs, there are two lines of a pattern matching block for which the order is reversed between the exercise file and the solution file. Since these lines are not changed as part of the exercise, this commit updates the exercise to make the order of the lines consistent with the solution, so that users will focus only on the lines that change between the exercise and the solution. --- exercises/13_error_handling/errors5.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index d0044db213..5721835102 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -39,8 +39,8 @@ struct PositiveNonzeroInteger(u64); impl PositiveNonzeroInteger { fn new(value: i64) -> Result { match value { - 0 => Err(CreationError::Zero), x if x < 0 => Err(CreationError::Negative), + 0 => Err(CreationError::Zero), x => Ok(PositiveNonzeroInteger(x as u64)), } } From 6b7a27d080924ae4624e07e61d68f24b56e48fbc Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Sat, 6 Jul 2024 14:30:11 +0200 Subject: [PATCH 1062/1432] Fix typo in `THIRD_PARTY_EXERCISES.md` --- THIRD_PARTY_EXERCISES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/THIRD_PARTY_EXERCISES.md b/THIRD_PARTY_EXERCISES.md index 5c066941b8..62646c5bed 100644 --- a/THIRD_PARTY_EXERCISES.md +++ b/THIRD_PARTY_EXERCISES.md @@ -1,7 +1,7 @@ # Third-Party Exercises The support of Rustlings for third-party exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. -You could also offer a translatation of the original Rustlings exercises as third-party exercises. +You could also offer a translation of the original Rustlings exercises as third-party exercises. ## Getting started From a21fa6ff4076a2a0194441a09d18439343cbfb9a Mon Sep 17 00:00:00 2001 From: Yerkebulan Tulibergenov Date: Sat, 6 Jul 2024 12:37:55 -0700 Subject: [PATCH 1063/1432] Fix typo in structs3.rs --- exercises/07_structs/structs3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 93b57fefd7..14716954ef 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,5 +1,5 @@ // 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. +// defined the `Package` struct, and we want to test some logic attached to it. #[derive(Debug)] struct Package { From 1499f681a395693eb3da65aa4f992a1346804657 Mon Sep 17 00:00:00 2001 From: Yerkebulan Tulibergenov Date: Sat, 6 Jul 2024 12:53:14 -0700 Subject: [PATCH 1064/1432] Fix formatting in strings4.rs --- exercises/09_strings/strings4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index 9d9eb480c0..473072636d 100644 --- a/exercises/09_strings/strings4.rs +++ b/exercises/09_strings/strings4.rs @@ -4,6 +4,7 @@ fn placeholder() {} fn string_slice(arg: &str) { println!("{arg}"); } + fn string(arg: String) { println!("{arg}"); } From 5d4363d58d94424d67d1815f5f2c36aabecb3a92 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 6 Jul 2024 22:21:52 +0200 Subject: [PATCH 1065/1432] Add comma --- exercises/07_structs/structs3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 14716954ef..69e5ced7f8 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,4 +1,4 @@ -// Structs contain data, but can also have logic. In this exercise we have +// 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. #[derive(Debug)] From 981a4778a9e452d143e9542a1eb8c8a60644c003 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 6 Jul 2024 22:23:19 +0200 Subject: [PATCH 1066/1432] Add newline between functions --- solutions/09_strings/strings4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs index 9dc6917e70..fe4733e3e9 100644 --- a/solutions/09_strings/strings4.rs +++ b/solutions/09_strings/strings4.rs @@ -1,6 +1,7 @@ fn string_slice(arg: &str) { println!("{arg}"); } + fn string(arg: String) { println!("{arg}"); } From 9b5b652c71bf5f921657b610435b785520857bf3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Jul 2024 00:28:17 +0200 Subject: [PATCH 1067/1432] Fix link on website --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 373b9c7925..f34bb2f9b5 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ Continue practicing your Rust skills by building your own projects, contributing Do you want to create your own set of Rustlings exercises to focus on some specific topic? Or do you want to translate the original Rustlings exercises? -Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! +Then follow the link to the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)! ## Uninstalling Rustlings From 01b8432d58d8c568adfa7dcc0634bd2f24e77fbc Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Jul 2024 13:55:39 +0200 Subject: [PATCH 1068/1432] Mark the last exercise as done --- src/app_state.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app_state.rs b/src/app_state.rs index e9a5b1094d..e08f94c667 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -403,6 +403,9 @@ impl AppState { writeln!(writer, "{}", "ok".green())?; } + // Write that the last exercise is done. + self.write()?; + clear_terminal(writer)?; writer.write_all(FENISH_LINE.as_bytes())?; From 708cfef3f76071d71d42053453e6b9a5b858b991 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Jul 2024 15:29:05 +0200 Subject: [PATCH 1069/1432] enums3: Avoid confusion with parentheses --- exercises/08_enums/enums3.rs | 10 +++++----- solutions/08_enums/enums3.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index f49707ca14..66c4675f9b 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -12,6 +12,7 @@ struct State { height: u64, position: Point, message: String, + // RGB color composed of red, green and blue. color: (u8, u8, u8), quit: bool, } @@ -30,8 +31,8 @@ impl State { self.message = s; } - fn change_color(&mut self, color: (u8, u8, u8)) { - self.color = color; + fn change_color(&mut self, red: u8, green: u8, blue: u8) { + self.color = (red, green, blue); } fn quit(&mut self) { @@ -39,9 +40,8 @@ impl State { } fn process(&mut self, message: Message) { - // TODO: Create a match expression to process the different message variants. - // Remember: When passing a tuple as a function argument, you'll need extra parentheses: - // e.g. `foo((t, u, p, l, e))` + // TODO: Create a match expression to process the different message + // variants using the methods defined above. } } diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs index 8641fbdb5f..4bc26b7c63 100644 --- a/solutions/08_enums/enums3.rs +++ b/solutions/08_enums/enums3.rs @@ -34,8 +34,8 @@ impl State { self.message = s; } - fn change_color(&mut self, color: (u8, u8, u8)) { - self.color = color; + fn change_color(&mut self, red: u8, green: u8, blue: u8) { + self.color = (red, green, blue); } fn quit(&mut self) { @@ -47,7 +47,7 @@ impl State { Message::Resize { width, height } => self.resize(width, height), Message::Move(point) => self.move_position(point), Message::Echo(s) => self.echo(s), - Message::ChangeColor(r, g, b) => self.change_color((r, g, b)), + Message::ChangeColor(r, g, b) => self.change_color(r, g, b), Message::Quit => self.quit(), } } From e764b75aef91607ca9938a1f71b57b01293413b7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Jul 2024 15:41:35 +0200 Subject: [PATCH 1070/1432] This'll -> This will --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f34bb2f9b5..8c1a101cdd 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ It contains code examples and exercises similar to Rustlings, but online. Before installing Rustlings, you need to have _Rust installed_. Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. -This'll also install _Cargo_, Rust's package/project manager. +This will also install _Cargo_, Rust's package/project manager. > 🐧 If you're on Linux, make sure you've installed `gcc` (for a linker). > From a5f221aa39196b00cb12425ba9f6ed5500ae5790 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Jul 2024 15:53:48 +0200 Subject: [PATCH 1071/1432] Improve some messages --- dev/rustlings-repo.txt | 2 +- src/dev/new.rs | 4 ++-- src/main.rs | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/dev/rustlings-repo.txt b/dev/rustlings-repo.txt index 62793612c4..5d456c8803 100644 --- a/dev/rustlings-repo.txt +++ b/dev/rustlings-repo.txt @@ -1 +1 @@ -This file is used to check if the user tries to run Rustlings in the repository (the method before v6) +This file is used to check if the user tries to run Rustlings in the repository (the method before version 6) diff --git a/src/dev/new.rs b/src/dev/new.rs index fefc4fc1a3..55d5f14107 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -139,7 +139,7 @@ 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) βœ… +First, [install Rustlings using the official instructions](https://github.com/rust-lang/rustlings) βœ… -Then, open your terminal in this directory and run `rustlings` to get started with the exercises πŸš€ +Then, clone this repository, open a terminal in this directory and run `rustlings` to get started with the exercises πŸš€ "; diff --git a/src/main.rs b/src/main.rs index 2233d8b069..a9f7b4a153 100644 --- a/src/main.rs +++ b/src/main.rs @@ -197,9 +197,10 @@ fn main() -> Result<()> { Ok(()) } -const OLD_METHOD_ERR: &str = "You are trying to run Rustlings using the old method before v6. +const OLD_METHOD_ERR: &str = + "You are trying to run Rustlings using the old method before version 6. The new method doesn't include cloning the Rustlings' repository. -Please follow the instructions in the README: +Please follow the instructions in `README.md`: https://github.com/rust-lang/rustlings#getting-started"; const FORMAT_VERSION_HIGHER_ERR: &str = @@ -216,5 +217,5 @@ const PRE_INIT_MSG: &str = r" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ |___/ -The `exercises` directory wasn't found in the current directory. +The `exercises/` directory couldn't be found in the current directory. If you are just starting with Rustlings, run the command `rustlings init` to initialize it."; From 9d7b973a62d3c0181be386752d72fa0722fe6135 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Jul 2024 17:03:00 +0200 Subject: [PATCH 1072/1432] Improve the comments in cow1 --- exercises/19_smart_pointers/cow1.rs | 11 ++++++----- solutions/19_smart_pointers/cow1.rs | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index 5ecf8482e8..1566500716 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -45,8 +45,9 @@ mod tests { #[test] fn owned_no_mutation() { // We can also pass `vec` without `&` so `Cow` owns it directly. In this - // case, no mutation occurs and thus also no clone. But the result is - // still owned because it was never borrowed or mutated. + // case, no mutation occurs (all numbers are already absolute) and thus + // also no clone. But the result is still owned because it was never + // borrowed or mutated. let vec = vec![0, 1, 2]; let mut input = Cow::from(vec); abs_all(&mut input); @@ -56,9 +57,9 @@ mod tests { #[test] fn owned_mutation() { - // Of course this is also the case if a mutation does occur. In this - // case, the call to `to_mut()` in the `abs_all` function returns a - // reference to the same data as before. + // Of course this is also the case if a mutation does occur (not all + // numbers are absolute). In this case, the call to `to_mut()` in the + // `abs_all` function returns a reference to the same data as before. let vec = vec![-1, 0, 1]; let mut input = Cow::from(vec); abs_all(&mut input); diff --git a/solutions/19_smart_pointers/cow1.rs b/solutions/19_smart_pointers/cow1.rs index 0a21a91b12..461143be86 100644 --- a/solutions/19_smart_pointers/cow1.rs +++ b/solutions/19_smart_pointers/cow1.rs @@ -45,8 +45,9 @@ mod tests { #[test] fn owned_no_mutation() { // We can also pass `vec` without `&` so `Cow` owns it directly. In this - // case, no mutation occurs and thus also no clone. But the result is - // still owned because it was never borrowed or mutated. + // case, no mutation occurs (all numbers are already absolute) and thus + // also no clone. But the result is still owned because it was never + // borrowed or mutated. let vec = vec![0, 1, 2]; let mut input = Cow::from(vec); abs_all(&mut input); @@ -56,9 +57,9 @@ mod tests { #[test] fn owned_mutation() { - // Of course this is also the case if a mutation does occur. In this - // case, the call to `to_mut()` in the `abs_all` function returns a - // reference to the same data as before. + // Of course this is also the case if a mutation does occur (not all + // numbers are absolute). In this case, the call to `to_mut()` in the + // `abs_all` function returns a reference to the same data as before. let vec = vec![-1, 0, 1]; let mut input = Cow::from(vec); abs_all(&mut input); From 5372caefb350dfde4e5ce018cf9dc59742558004 Mon Sep 17 00:00:00 2001 From: NitinKM <70827815+NewtonChutney@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:19:38 +0530 Subject: [PATCH 1073/1432] Update iterator sol in quiz2.rs --- solutions/quizzes/quiz2.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs index 2f5cc050ca..be3f15976e 100644 --- a/solutions/quizzes/quiz2.rs +++ b/solutions/quizzes/quiz2.rs @@ -52,12 +52,7 @@ mod my_module { .map(|(mut string, command)| match command { Command::Uppercase => string.to_uppercase(), Command::Trim => string.trim().to_string(), - Command::Append(n) => { - for _ in 0..n { - string += "bar"; - } - string - } + Command::Append(n) => string + &"bar".repeat(n), }) .collect() } From 0f4cb94cfe31745e8fee9efc7dc1d287e65e16cb Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Jul 2024 20:28:31 +0200 Subject: [PATCH 1074/1432] quiz2: Use repeat --- solutions/quizzes/quiz2.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs index be3f15976e..2f55f065b6 100644 --- a/solutions/quizzes/quiz2.rs +++ b/solutions/quizzes/quiz2.rs @@ -24,17 +24,12 @@ mod my_module { pub fn transformer(input: Vec<(String, Command)>) -> Vec { let mut output = Vec::new(); - for (mut string, command) in input { + for (string, command) in input { // Create the new string. let new_string = match command { Command::Uppercase => string.to_uppercase(), Command::Trim => string.trim().to_string(), - Command::Append(n) => { - for _ in 0..n { - string += "bar"; - } - string - } + Command::Append(n) => string + &"bar".repeat(n), }; // Push the new string to the output vector. @@ -49,7 +44,7 @@ mod my_module { pub fn transformer_iter(input: Vec<(String, Command)>) -> Vec { input .into_iter() - .map(|(mut string, command)| match command { + .map(|(string, command)| match command { Command::Uppercase => string.to_uppercase(), Command::Trim => string.trim().to_string(), Command::Append(n) => string + &"bar".repeat(n), From a7a881809f462688b26ba7b81de85753c3507c22 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 12:53:44 +0200 Subject: [PATCH 1075/1432] Check is_terminal --- src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index a9f7b4a153..3c96d1a998 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use anyhow::{bail, Context, Result}; use app_state::StateFileStatus; use clap::{Parser, Subcommand}; use std::{ - io::{self, BufRead, StdoutLock, Write}, + io::{self, BufRead, IsTerminal, StdoutLock, Write}, path::Path, process::exit, }; @@ -148,6 +148,10 @@ fn main() -> Result<()> { match args.command { None => { + if !io::stdout().is_terminal() { + bail!("Unsupported or missing terminal/TTY"); + } + let notify_exercise_names = if args.manual_run { None } else { From a4091ade5cde7602acef0601cb7f69984fcad009 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 14:40:35 +0200 Subject: [PATCH 1076/1432] iterators3: Add `IntegerOverflow` error variant --- exercises/18_iterators/iterators3.rs | 11 ++++++++++- solutions/18_iterators/iterators3.rs | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 65a0573437..6b1eca1734 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,12 +1,16 @@ #[derive(Debug, PartialEq, Eq)] enum DivisionError { + // Example: 42 / 0 DivideByZero, + // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1` + IntegerOverflow, + // Example: 5 / 2 = 2.5 NotDivisible, } // TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // Otherwise, return a suitable error. -fn divide(a: i32, b: i32) -> Result { +fn divide(a: i64, b: i64) -> Result { todo!(); } @@ -42,6 +46,11 @@ mod tests { assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); } + #[test] + fn test_integer_overflow() { + assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow)); + } + #[test] fn test_not_divisible() { assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs index d66d1ef3b5..11aa1ec8ce 100644 --- a/solutions/18_iterators/iterators3.rs +++ b/solutions/18_iterators/iterators3.rs @@ -1,6 +1,10 @@ #[derive(Debug, PartialEq, Eq)] enum DivisionError { + // Example: 42 / 0 DivideByZero, + // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1` + IntegerOverflow, + // Example: 5 / 2 = 2.5 NotDivisible, } @@ -9,6 +13,10 @@ fn divide(a: i64, b: i64) -> Result { return Err(DivisionError::DivideByZero); } + if a == i64::MIN && b == -1 { + return Err(DivisionError::IntegerOverflow); + } + if a % b != 0 { return Err(DivisionError::NotDivisible); } @@ -51,6 +59,11 @@ mod tests { assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero)); } + #[test] + fn test_integer_overflow() { + assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow)); + } + #[test] fn test_not_divisible() { assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible)); From 2d5d70693a879f07fcd941c21da3a3760cb81db6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 15:05:58 +0200 Subject: [PATCH 1077/1432] errors3: Add a comment to prevent changing the wrong line --- exercises/13_error_handling/errors3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index 33a7b877ba..8e8c38a2a1 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -19,6 +19,7 @@ fn main() { let mut tokens = 100; let pretend_user_input = "8"; + // Don't change this line. let cost = total_cost(pretend_user_input)?; if cost > tokens { From bf698659b07bab2d6d4997e2637f1d304a293c5b Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 15:20:23 +0200 Subject: [PATCH 1078/1432] Sync comment from exercise --- solutions/quizzes/quiz2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs index 2f55f065b6..58cbe4e2a6 100644 --- a/solutions/quizzes/quiz2.rs +++ b/solutions/quizzes/quiz2.rs @@ -6,7 +6,7 @@ // - Append "bar" to the string a specified amount of times // // The exact form of this will be: -// - The input is going to be a vector of a 2-length tuple, +// - The input is going to be a vector of 2-length tuples, // the first element is the string, the second one is the command. // - The output element is going to be a vector of strings. From 08c408aae042509526e8cc1f175e7a010836f53d Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 15:20:56 +0200 Subject: [PATCH 1079/1432] Add hint about string concatination with + --- rustlings-macros/info.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 715778ddb5..c9fc65aa6f 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -585,7 +585,7 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on- [[exercises]] name = "quiz2" dir = "quizzes" -hint = "No hints this time ;)" +hint = "The `+` operator can concatinate a `String` with a `&str`." # OPTIONS @@ -748,7 +748,9 @@ name = "traits1" dir = "15_traits" hint = """ More about traits in The Book: -https://doc.rust-lang.org/book/ch10-02-traits.html""" +https://doc.rust-lang.org/book/ch10-02-traits.html + +The `+` operator can concatinate a `String` with a `&str`.""" [[exercises]] name = "traits2" From 69021e1497f62d52fd2b41d5e5b71ca46b76d7c0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 16:00:12 +0200 Subject: [PATCH 1080/1432] Remove stable from book links --- exercises/04_primitive_types/README.md | 4 ++-- exercises/05_vecs/README.md | 2 +- exercises/12_options/README.md | 2 +- exercises/14_generics/README.md | 2 +- rustlings-macros/info.toml | 18 +++++++++--------- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/exercises/04_primitive_types/README.md b/exercises/04_primitive_types/README.md index cea69b02af..d67668a335 100644 --- a/exercises/04_primitive_types/README.md +++ b/exercises/04_primitive_types/README.md @@ -5,5 +5,5 @@ compiler. In this section, we'll go through the most important ones. ## Further information -- [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html) -- [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html) +- [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html) +- [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html) diff --git a/exercises/05_vecs/README.md b/exercises/05_vecs/README.md index 8ff9b85f52..e1b6128523 100644 --- a/exercises/05_vecs/README.md +++ b/exercises/05_vecs/README.md @@ -12,6 +12,6 @@ the other useful data structure, hash maps, later. ## Further information -- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) +- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html) - [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut) - [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) diff --git a/exercises/12_options/README.md b/exercises/12_options/README.md index bdd33749af..624572a3bc 100644 --- a/exercises/12_options/README.md +++ b/exercises/12_options/README.md @@ -14,7 +14,7 @@ Option types are very common in Rust code, as they have a number of uses: ## Further Information -- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions) +- [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions) - [Option Module Documentation](https://doc.rust-lang.org/std/option/) - [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html) - [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html) diff --git a/exercises/14_generics/README.md b/exercises/14_generics/README.md index 72cff3f30e..0c8c8cb1aa 100644 --- a/exercises/14_generics/README.md +++ b/exercises/14_generics/README.md @@ -7,5 +7,5 @@ The simplest and most common use of generics is for type parameters. ## Further information -- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html) +- [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html) - [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index c9fc65aa6f..846b78682c 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -309,7 +309,7 @@ In Rust, there are two ways to define a Vector. inside the square brackets. This way is simpler when you exactly know the initial values. -Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html +Check this chapter: https://doc.rust-lang.org/book/ch08-01-vectors.html of the Rust book to learn more.""" [[exercises]] @@ -378,7 +378,7 @@ dir = "06_move_semantics" test = false hint = """ To find the answer, you can consult the book section "References and Borrowing": -https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html The first problem is that `get_char` is taking ownership of the string. So `data` is moved and can't be used for `string_uppercase`. `data` is moved to @@ -416,7 +416,7 @@ to its fields. There are however some shortcuts that can be taken when instantiating structs. Have a look in The Book to find out more: -https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" +https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" [[exercises]] name = "structs3" @@ -487,7 +487,7 @@ to add one character to the `if` statement, though, that will coerce the Side note: If you're interested in learning about how this kind of reference conversion works, you can jump ahead in the book and read this part in the smart pointers chapter: -https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" +https://doc.rust-lang.org/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods""" [[exercises]] name = "strings3" @@ -561,7 +561,7 @@ hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. Learn more in The Book: -https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value""" +https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value""" [[exercises]] name = "hashmaps3" @@ -572,7 +572,7 @@ Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of exist in the table yet. Learn more in The Book: -https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value. @@ -739,7 +739,7 @@ name = "generics2" dir = "14_generics" hint = """ Related section in The Book: -https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions""" +https://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions""" # TRAITS @@ -871,7 +871,7 @@ We expect the method `Rectangle::new` to panic for negative values. To handle that, you need to add a special attribute to the test function. You can refer to the docs: -https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" +https://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic""" # STANDARD LIBRARY TYPES @@ -1007,7 +1007,7 @@ thread-local copy of the numbers. This is a simple exercise if you understand the underlying concepts, but if this is too much of a struggle, consider reading through all of Chapter 16 in The Book: -https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html""" +https://doc.rust-lang.org/book/ch16-00-concurrency.html""" [[exercises]] name = "cow1" From 01343f187b0541ab2e210f831c56ef9103ce8c60 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 16:29:43 +0200 Subject: [PATCH 1081/1432] Explain what a factorial is and link to wikipedia for more details --- exercises/18_iterators/iterators4.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 8381dbb185..c296f0e426 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,5 +1,8 @@ fn factorial(num: u64) -> u64 { - // TODO: Complete this function to return the factorial of `num`. + // TODO: Complete this function to return the factorial of `num` which is + // defined as `1 * 2 * 3 * … * num`. + // https://en.wikipedia.org/wiki/Factorial + // // Do not use: // - early returns (using the `return` keyword explicitly) // Try not to use: From c793416495b3e015cbd4fb5ea23070aad8611614 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 8 Jul 2024 16:50:35 +0200 Subject: [PATCH 1082/1432] Fix typo --- rustlings-macros/info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 846b78682c..a3007c9ce8 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -585,7 +585,7 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on- [[exercises]] name = "quiz2" dir = "quizzes" -hint = "The `+` operator can concatinate a `String` with a `&str`." +hint = "The `+` operator can concatenate a `String` with a `&str`." # OPTIONS @@ -750,7 +750,7 @@ hint = """ More about traits in The Book: https://doc.rust-lang.org/book/ch10-02-traits.html -The `+` operator can concatinate a `String` with a `&str`.""" +The `+` operator can concatenate a `String` with a `&str`.""" [[exercises]] name = "traits2" From b12b652a57d1e207bef4538e890ddd0ff7ed0a78 Mon Sep 17 00:00:00 2001 From: Jan Arends Date: Wed, 10 Jul 2024 11:16:35 +0200 Subject: [PATCH 1083/1432] updated variable name in hint --- rustlings-macros/info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index a3007c9ce8..67d50e6364 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -890,9 +890,9 @@ hint = """ `capitalize_first`: The variable `first` is a `char`. It needs to be capitalized and added to the -remaining characters in `c` in order to return the correct `String`. +remaining characters in `chars` in order to return the correct `String`. -The remaining characters in `c` can be viewed as a string slice using the +The remaining characters in `chars` can be viewed as a string slice using the `as_str` method. The documentation for `char` contains many useful methods. From e512928df57d84bd09a1d630fdafa68a1eef928d Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Jul 2024 13:27:32 +0200 Subject: [PATCH 1084/1432] Update deps --- Cargo.lock | 46 +++++++++++++++++++--------------------------- Cargo.toml | 6 +++--- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5da969ccf7..9705c65557 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -354,15 +354,6 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -594,7 +585,7 @@ dependencies = [ "cassowary", "compact_str", "crossterm", - "itertools 0.13.0", + "itertools", "lru", "paste", "stability", @@ -709,18 +700,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -785,9 +776,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "stability" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", "syn", @@ -829,9 +820,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" dependencies = [ "proc-macro2", "quote", @@ -855,9 +846,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap", "serde", @@ -880,11 +871,12 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-truncate" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools 0.12.1", + "itertools", + "unicode-segmentation", "unicode-width", ] diff --git a/Cargo.toml b/Cargo.toml index 22141144e0..c25f085ac7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ license = "MIT" edition = "2021" [workspace.dependencies] -serde = { version = "1.0.203", features = ["derive"] } -toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] } +serde = { version = "1.0.204", features = ["derive"] } +toml_edit = { version = "0.22.15", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -47,7 +47,7 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.8", features = ["derive"] } +clap = { version = "4.5.9", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } From 59d6b852afd81385020f2b59ab95c85af6229763 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Jul 2024 13:47:33 +0200 Subject: [PATCH 1085/1432] move_semantics5: Move `main` to the end --- exercises/06_move_semantics/move_semantics5.rs | 16 ++++++++-------- solutions/06_move_semantics/move_semantics5.rs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index fc59338701..cd0dafd08d 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -3,14 +3,6 @@ // TODO: Fix the compiler errors without changing anything except adding or // removing references (the character `&`). -fn main() { - let data = "Rust is great!".to_string(); - - get_char(data); - - string_uppercase(&data); -} - // Shouldn't take ownership fn get_char(data: String) -> char { data.chars().last().unwrap() @@ -22,3 +14,11 @@ fn string_uppercase(mut data: &String) { println!("{data}"); } + +fn main() { + let data = "Rust is great!".to_string(); + + get_char(data); + + string_uppercase(&data); +} diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs index 678ec97b6d..1410e913fa 100644 --- a/solutions/06_move_semantics/move_semantics5.rs +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -1,13 +1,5 @@ #![allow(clippy::ptr_arg)] -fn main() { - let data = "Rust is great!".to_string(); - - get_char(&data); - - string_uppercase(data); -} - // Borrows instead of taking ownership. // It is recommended to use `&str` instead of `&String` here. But this is // enough for now because we didn't handle strings yet. @@ -21,3 +13,11 @@ fn string_uppercase(mut data: String) { println!("{data}"); } + +fn main() { + let data = "Rust is great!".to_string(); + + get_char(&data); + + string_uppercase(data); +} From d7024d80ce0a640271d006dbfda440f60652af6d Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Jul 2024 13:50:39 +0200 Subject: [PATCH 1086/1432] move_semantics4: Avoid using the dereference operator --- exercises/06_move_semantics/move_semantics4.rs | 8 ++++---- solutions/06_move_semantics/move_semantics4.rs | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index 83a03440c0..56da988cd4 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -8,11 +8,11 @@ mod tests { // Don't add, change or remove any line. #[test] fn move_semantics4() { - let mut x = 100; + let mut x = Vec::new(); let y = &mut x; let z = &mut x; - *y += 100; - *z += 1000; - assert_eq!(x, 1200); + y.push(42); + z.push(13); + assert_eq!(x, [42, 13]); } } diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs index b7919ac04d..64fdd9dbde 100644 --- a/solutions/06_move_semantics/move_semantics4.rs +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -7,15 +7,15 @@ mod tests { // TODO: Fix the compiler errors only by reordering the lines in the test. // Don't add, change or remove any line. #[test] - fn move_semantics5() { - let mut x = 100; + fn move_semantics4() { + let mut x = Vec::new(); let y = &mut x; // `y` used here. - *y += 100; + y.push(42); // The mutable reference `y` is not used anymore, // therefore a new reference can be created. let z = &mut x; - *z += 1000; - assert_eq!(x, 1200); + z.push(13); + assert_eq!(x, [42, 13]); } } From 6263cb6456d18f142c76ccf69ee0d72ae4db3a8b Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Jul 2024 15:16:49 +0200 Subject: [PATCH 1087/1432] Add note about iterating over Option --- rustlings-macros/info.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 67d50e6364..d7bb1ca108 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1145,7 +1145,11 @@ test = false strict_clippy = true hint = """ `for` loops over `Option` values are more clearly expressed as an `if-let` -statement.""" +statement. + +Not required to solve this exercise, but if you are interested in when iterating +over `Option` can be useful, read the following section in the documentation: +https://doc.rust-lang.org/std/option/#iterating-over-option""" [[exercises]] name = "clippy3" From 47ba4502e0a60bf720e4c70ff27150bba2d18f8c Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Jul 2024 15:55:18 +0200 Subject: [PATCH 1088/1432] move_semantics2: Mention cloning in the hint --- rustlings-macros/info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index d7bb1ca108..e69834acf0 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -351,7 +351,7 @@ We call this "moving" a variable. When we pass `vec0` into `fill_vec`, it's being "moved" into `vec1`, meaning we can't access `vec0` anymore. You could make another, separate version of the data that's in `vec0` and -pass it to `fill_vec` instead.""" +pass it to `fill_vec` instead. This is called cloning in Rust.""" [[exercises]] name = "move_semantics3" From 6d2ea8dae3976076d88e783a01e64d08a68cc04a Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Jul 2024 16:49:36 +0200 Subject: [PATCH 1089/1432] Update CHANGELOG --- CHANGELOG.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9025d4510a..80e000a189 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ + + +## 6.1.0 (2024-07-10) + +#### Added + +- `dev check`: Check that all exercises (including third-party ones) include at least one `TODO` comment. +- `dev check`: Check that all exercises actually fail to run (not already solved). + +#### Changed + +- Make enum variants more consistent between enum exercises. +- `iterators3`: Teach about the possible case of integer overflow during division. + +#### Fixed + +- Exit with a helpful error message on missing/unsupported terminal/TTY. +- Mark the last exercise as done. + ## 6.0.1 (2024-07-04) @@ -64,7 +83,7 @@ This should avoid issues related to the language server or to running exercises, Clippy lints are now shown on all exercises, not only the Clippy exercises πŸ“Ž Make Clippy your friend from early on πŸ₯° -### Third party exercises +### Third-party exercises Rustlings now supports third-party exercises! From 0847b3a4bfeebd0d18578de8f45b7e4571fc6e77 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 10 Jul 2024 16:51:34 +0200 Subject: [PATCH 1090/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9705c65557..3bd74507f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -645,7 +645,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustlings" -version = "6.0.1" +version = "6.1.0" dependencies = [ "anyhow", "assert_cmd", @@ -664,7 +664,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.1" +version = "6.1.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index c25f085ac7..9198f23855 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.1" +version = "6.1.0" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.1" } +rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" } serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true From d176ddd27ec88b127c615ae5b662aed7de25a231 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Jul 2024 16:29:41 +0200 Subject: [PATCH 1091/1432] Improve TODO comment --- exercises/quizzes/quiz2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/quizzes/quiz2.rs b/exercises/quizzes/quiz2.rs index 8ef1342a1d..2cddba907e 100644 --- a/exercises/quizzes/quiz2.rs +++ b/exercises/quizzes/quiz2.rs @@ -26,7 +26,7 @@ enum Command { mod my_module { use super::Command; - // TODO: Complete the function. + // TODO: Complete the function as described above. // pub fn transformer(input: ???) -> ??? { ??? } } From 99fb11cc72ce57966cb23a3f5cae2bd34ee7ab8e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 13 Jul 2024 11:53:59 +0200 Subject: [PATCH 1092/1432] Update syn --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bd74507f6..f98f402dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -820,9 +820,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.70" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", From 3e09e509d649cfb97f42e4d8547b4a077caaa520 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 13 Jul 2024 12:00:22 +0200 Subject: [PATCH 1093/1432] Add section about rustlings not found --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 8c1a101cdd..064451ae01 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,17 @@ After installing Rustlings, run the following command to initialize the `rustlin rustlings init ``` +
+If the command `rustlings` can't be found… (click to expand) + +Cargo installs binaries to the directory `~/.cargo/bin`. +You are probably using Linux and installed Rust using your package manager. +Sadly, these package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. +The solution is to either add it manually or to uninstall Rust from the package manager and isntall it using the official way with `rustup`: +https://www.rust-lang.org/tools/install + +
+ Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises: ```bash From 12d1971b0d458045f318927f1ce18bb6808a19c0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 13 Jul 2024 12:02:39 +0200 Subject: [PATCH 1094/1432] Update section about command not found --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 064451ae01..35463ef10d 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,12 @@ rustlings init ```
-If the command `rustlings` can't be found… (click to expand) +If the command rustlings can't be found… (click to expand) -Cargo installs binaries to the directory `~/.cargo/bin`. You are probably using Linux and installed Rust using your package manager. -Sadly, these package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. + +Cargo installs binaries to the directory `~/.cargo/bin`. +Sadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. The solution is to either add it manually or to uninstall Rust from the package manager and isntall it using the official way with `rustup`: https://www.rust-lang.org/tools/install From 516fcf9168391555aacc7e99f51a99d6a87536c6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 13 Jul 2024 12:07:52 +0200 Subject: [PATCH 1095/1432] Update section --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 35463ef10d..a7f81c12db 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,11 @@ You are probably using Linux and installed Rust using your package manager. Cargo installs binaries to the directory `~/.cargo/bin`. Sadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. -The solution is to either add it manually or to uninstall Rust from the package manager and isntall it using the official way with `rustup`: -https://www.rust-lang.org/tools/install + +The solution is to … + +- either add `~/.cargo/bin` manually to `PATH` +- or to uninstall Rust from the package manager and install it using the official way with `rustup`: https://www.rust-lang.org/tools/install
From 2854dc9ab347ae500e2a2fe4b6ec6c785fb5796f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 13 Jul 2024 12:32:23 +0200 Subject: [PATCH 1096/1432] Update CI and release hook --- .github/workflows/rust.yml | 6 ++++-- release-hook.sh | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b68cb1b30e..5c423e2467 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -23,7 +23,9 @@ jobs: with: globs: "exercises/**/*.md" - name: Run cargo fmt - run: cargo fmt --all -- --check + run: cargo fmt --all --check + - name: Run rustfmt on solutions + run: rustfmt --check --edition 2021 --color always solutions/**/*.rs test: runs-on: ${{ matrix.os }} strategy: @@ -33,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - uses: swatinem/rust-cache@v2 - name: Run cargo test - run: cargo test + run: cargo test --workspace dev-check: runs-on: ubuntu-latest steps: diff --git a/release-hook.sh b/release-hook.sh index 052832f245..f021f193ee 100755 --- a/release-hook.sh +++ b/release-hook.sh @@ -3,7 +3,12 @@ # Error out if any command fails set -e -cargo run -- dev check typos -cargo outdated -w --exit-code 1 +cargo upgrades + +# Similar to CI +cargo clippy -- --deny warnings +cargo fmt --all --check +rustfmt --check --edition 2021 solutions/**/*.rs cargo test --workspace --all-targets +cargo run -- dev check --require-solutions From 3f06d767b5a11789703e7e00e7922bdcd77ce08a Mon Sep 17 00:00:00 2001 From: Mikkel ALMONTE--RINGAUD Date: Tue, 16 Jul 2024 17:20:26 +0200 Subject: [PATCH 1097/1432] fix: Lyche becomes Lychee Small typo. --- exercises/11_hashmaps/hashmaps2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index 376970d730..e9f53fea3c 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -6,7 +6,7 @@ // must add fruit to the basket so that there is at least one of each kind and // more than 11 in total - we have a lot of mouths to feed. You are not allowed // to insert any more of the fruits that are already in the basket (Apple, -// Mango, and Lyche). +// Mango, and Lychee). use std::collections::HashMap; From 362473dde012e2d703a95297c0aa846805627180 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Jul 2024 18:21:07 +0200 Subject: [PATCH 1098/1432] Sync exercise and solution --- solutions/11_hashmaps/hashmaps2.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs index a5e6ef9b24..75e6ec2cfc 100644 --- a/solutions/11_hashmaps/hashmaps2.rs +++ b/solutions/11_hashmaps/hashmaps2.rs @@ -5,7 +5,8 @@ // Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You // must add fruit to the basket so that there is at least one of each kind and // more than 11 in total - we have a lot of mouths to feed. You are not allowed -// to insert any more of these fruits! +// to insert any more of the fruits that are already in the basket (Apple, +// Mango, and Lychee). use std::collections::HashMap; From 2c79e29483b485e6dffd386e8c7e8f7131e33c37 Mon Sep 17 00:00:00 2001 From: yapjiahong Date: Wed, 17 Jul 2024 00:43:42 +0800 Subject: [PATCH 1099/1432] doc: enchane string3 exercise hint --- rustlings-macros/info.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index e69834acf0..9f6616b7f1 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -498,7 +498,11 @@ some of them: https://doc.rust-lang.org/std/string/struct.String.html#method.trim For the `compose_me` method: You can either use the `format!` macro, or convert -the string slice into an owned string, which you can then freely extend.""" +the string slice into an owned string, which you can then freely extend. + +For the `replace_me` method: You can reference: +https://doc.rust-lang.org/std/string/struct.String.html#method.replace +""" [[exercises]] name = "strings4" From 447ac3c40beed9b5447433c0404ed04a1a38afd8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Jul 2024 14:32:45 +0200 Subject: [PATCH 1100/1432] strings3: Improve hint --- rustlings-macros/info.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 9f6616b7f1..2ecb226424 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -500,9 +500,8 @@ https://doc.rust-lang.org/std/string/struct.String.html#method.trim For the `compose_me` method: You can either use the `format!` macro, or convert the string slice into an owned string, which you can then freely extend. -For the `replace_me` method: You can reference: -https://doc.rust-lang.org/std/string/struct.String.html#method.replace -""" +For the `replace_me` method, you can check out the `replace` method: +https://doc.rust-lang.org/std/string/struct.String.html#method.replace""" [[exercises]] name = "strings4" From 183ed3f88f416816312595fde2573b587fd8d999 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Jul 2024 14:33:29 +0200 Subject: [PATCH 1101/1432] Update dep --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f98f402dcb..4e05a10cca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,7 +516,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.2", + "redox_syscall 0.5.3", "smallvec", "windows-targets 0.52.6", ] @@ -607,9 +607,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags 2.6.0", ] From 82409c060fb8c5a7763c0e7480d8814e1f994ac8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 22 Jul 2024 12:01:41 +0200 Subject: [PATCH 1102/1432] Update deps --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e05a10cca..f3824f1612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -820,9 +820,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -846,9 +846,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.15" +version = "0.22.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" dependencies = [ "indexmap", "serde", @@ -1095,9 +1095,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 9198f23855..f5fe7380a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ edition = "2021" [workspace.dependencies] serde = { version = "1.0.204", features = ["derive"] } -toml_edit = { version = "0.22.15", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.16", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" From 5116a812fbcdf8662934ef35e5d6d0d0794a5d47 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 22 Jul 2024 12:02:59 +0200 Subject: [PATCH 1103/1432] tests3: Fix panic message --- exercises/17_tests/tests3.rs | 2 +- solutions/17_tests/tests3.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index ec994792fb..822184ef75 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -9,7 +9,7 @@ impl Rectangle { if width <= 0 || height <= 0 { // Returning a `Result` would be better here. But we want to learn // how to test functions that can panic. - panic!("Rectangle width and height can't be negative"); + panic!("Rectangle width and height must be positive"); } Rectangle { width, height } diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs index 503f9bc0f9..487fdc66ea 100644 --- a/solutions/17_tests/tests3.rs +++ b/solutions/17_tests/tests3.rs @@ -9,7 +9,7 @@ impl Rectangle { if width <= 0 || height <= 0 { // Returning a `Result` would be better here. But we want to learn // how to test functions that can panic. - panic!("Rectangle width and height can't be negative"); + panic!("Rectangle width and height must be positive"); } Rectangle { width, height } From e2492f65a061ab8978d40d32a03cf981544fdf03 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Jul 2024 12:51:44 +0200 Subject: [PATCH 1104/1432] Update deps --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3824f1612..4ae4ad04d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.9" +version = "4.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "8f6b81fb3c84f5563d509c59b5a48d935f689e993afa90fe39047f05adef9142" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "5ca6706fd5224857d9ac5eb9355f6683563cc0541c7cd9d014043b57cbec78ac" dependencies = [ "anstream", "anstyle", @@ -1095,9 +1095,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" +checksum = "557404e450152cd6795bb558bca69e43c585055f4606e3bcae5894fc6dac9ba0" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index f5fe7380a1..17989238cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.9", features = ["derive"] } +clap = { version = "4.5.10", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } From 3f49decce947ff7f1d30b53cf22fc2a0bce820f6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Jul 2024 14:34:43 +0200 Subject: [PATCH 1105/1432] Remove assert_cmd and predicates --- Cargo.lock | 147 -------------------------- Cargo.toml | 4 - tests/integration_tests.rs | 207 +++++++++++++++++++++++-------------- 3 files changed, 131 insertions(+), 227 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ae4ad04d3..5932578ac3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,15 +14,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "allocator-api2" version = "0.2.18" @@ -84,21 +75,6 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" -[[package]] -name = "assert_cmd" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" -dependencies = [ - "anstyle", - "bstr", - "doc-comment", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - [[package]] name = "autocfg" version = "1.3.0" @@ -117,17 +93,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "bstr" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - [[package]] name = "cassowary" version = "0.3.0" @@ -248,18 +213,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "either" version = "1.13.0" @@ -284,15 +237,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -438,12 +382,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - [[package]] name = "notify" version = "6.1.1" @@ -473,15 +411,6 @@ dependencies = [ "notify", ] -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -527,36 +456,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "predicates" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" -dependencies = [ - "anstyle", - "difflib", - "float-cmp", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" -dependencies = [ - "predicates-core", - "termtree", -] - [[package]] name = "proc-macro2" version = "1.0.86" @@ -614,47 +513,16 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "regex" -version = "1.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - [[package]] name = "rustlings" version = "6.1.0" dependencies = [ "anyhow", - "assert_cmd", "clap", "crossterm", "hashbrown", "notify-debouncer-mini", "os_pipe", - "predicates", "ratatui", "rustlings-macros", "serde", @@ -829,12 +697,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "toml_datetime" version = "0.6.6" @@ -898,15 +760,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index 17989238cd..ddf3c70332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,10 +58,6 @@ serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true -[dev-dependencies] -assert_cmd = "2.0.14" -predicates = "3.1.0" - [profile.release] panic = "abort" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 7d30467b1f..d5afd2ca6a 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,134 +1,189 @@ -use assert_cmd::prelude::*; -use std::process::Command; +use std::{ + env::{self, consts::EXE_SUFFIX}, + process::{Command, Stdio}, + str::from_utf8, +}; + +#[derive(Default)] +struct Cmd<'a> { + current_dir: Option<&'a str>, + args: &'a [&'a str], + stdout: Option<&'a str>, + full_stdout: bool, +} + +impl<'a> Cmd<'a> { + #[inline] + fn current_dir(&mut self, current_dir: &'a str) -> &mut Self { + self.current_dir = Some(current_dir); + self + } + + #[inline] + fn args(&mut self, args: &'a [&'a str]) -> &mut Self { + self.args = args; + self + } + + #[inline] + fn stdout(&mut self, stdout: &'a str) -> &mut Self { + self.stdout = Some(stdout); + self + } + + #[inline] + fn full_stdout(&mut self) -> &mut Self { + self.full_stdout = true; + self + } + + fn assert(&self, success: bool) { + let rustlings_bin = { + let mut path = env::current_exe().unwrap(); + // Pop test binary name + path.pop(); + // Pop `/deps` + path.pop(); + + path.push("rustlings"); + let mut path = path.into_os_string(); + path.push(EXE_SUFFIX); + path + }; + + let mut cmd = Command::new(rustlings_bin); + + if let Some(current_dir) = self.current_dir { + cmd.current_dir(current_dir); + } + + cmd.args(self.args) + .stdin(Stdio::null()) + .stderr(Stdio::null()); + + let status = if let Some(expected_stdout) = self.stdout { + let output = cmd.output().unwrap(); + let stdout = from_utf8(&output.stdout).unwrap(); + + if self.full_stdout { + assert_eq!(stdout, expected_stdout); + } else { + assert!(stdout.contains(expected_stdout)); + } + + output.status + } else { + cmd.stdout(Stdio::null()).status().unwrap() + }; + + assert_eq!(status.success(), success); + } + + #[inline] + fn success(&self) { + self.assert(true); + } + + #[inline] + fn fail(&self) { + self.assert(false); + } +} #[test] fn fails_when_in_wrong_dir() { - Command::cargo_bin("rustlings") - .unwrap() - .current_dir("tests/") - .assert() - .code(1); + Cmd::default().current_dir("tests").fail(); } #[test] fn run_single_compile_success() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "compSuccess"]) - .current_dir("tests/fixture/success/") - .assert() + Cmd::default() + .current_dir("tests/fixture/success") + .args(&["run", "compSuccess"]) .success(); } #[test] fn run_single_compile_failure() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "compFailure"]) - .current_dir("tests/fixture/failure/") - .assert() - .code(1); + Cmd::default() + .current_dir("tests/fixture/failure") + .args(&["run", "compFailure"]) + .fail(); } #[test] fn run_single_test_success() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testSuccess"]) - .current_dir("tests/fixture/success/") - .assert() + Cmd::default() + .current_dir("tests/fixture/success") + .args(&["run", "testSuccess"]) .success(); } #[test] fn run_single_test_failure() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testFailure"]) - .current_dir("tests/fixture/failure/") - .assert() - .code(1); + Cmd::default() + .current_dir("tests/fixture/failure") + .args(&["run", "testFailure"]) + .fail(); } #[test] fn run_single_test_not_passed() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testNotPassed.rs"]) - .current_dir("tests/fixture/failure/") - .assert() - .code(1); + Cmd::default() + .current_dir("tests/fixture/failure") + .args(&["run", "testNotPassed.rs"]) + .fail(); } #[test] fn run_single_test_no_exercise() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "compNoExercise.rs"]) + Cmd::default() .current_dir("tests/fixture/failure") - .assert() - .code(1); + .args(&["run", "compNoExercise.rs"]) + .fail(); } #[test] fn reset_single_exercise() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["reset", "intro1"]) - .assert() - .code(0); + Cmd::default().args(&["reset", "intro1"]).success(); } #[test] fn reset_no_exercise() { - Command::cargo_bin("rustlings") - .unwrap() - .arg("reset") - .assert() - .code(2) - .stderr(predicates::str::contains( - "required arguments were not provided", - )); + Cmd::default().args(&["reset"]).fail(); } #[test] fn get_hint_for_single_test() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["hint", "testFailure"]) + Cmd::default() .current_dir("tests/fixture/failure") - .assert() - .code(0) - .stdout("Hello!\n"); + .args(&["hint", "testFailure"]) + .stdout("Hello!\n") + .full_stdout() + .success(); } #[test] fn run_compile_exercise_does_not_prompt() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "pending_exercise"]) + Cmd::default() .current_dir("tests/fixture/state") - .assert() - .code(0); + .args(&["run", "pending_exercise"]) + .success(); } #[test] fn run_test_exercise_does_not_prompt() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "pending_test_exercise"]) + Cmd::default() .current_dir("tests/fixture/state") - .assert() - .code(0); + .args(&["run", "pending_test_exercise"]) + .success(); } #[test] fn run_single_test_success_with_output() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testSuccess"]) - .current_dir("tests/fixture/success/") - .assert() - .code(0) - .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS")); + Cmd::default() + .current_dir("tests/fixture/success") + .args(&["run", "testSuccess"]) + .stdout("\nTHIS TEST TOO SHALL PASS\n") + .success(); } From 8fec5155c735efe791ba2fdbaa7a562bf3e0ddea Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Jul 2024 15:12:14 +0200 Subject: [PATCH 1106/1432] Clean up tests --- Cargo.toml | 4 +- tests/fixture/failure/Cargo.toml | 20 ----- .../fixture/failure/exercises/testFailure.rs | 6 -- .../failure/exercises/testNotPassed.rs | 6 -- tests/fixture/failure/info.toml | 10 --- tests/fixture/state/Cargo.toml | 16 ---- .../state/exercises/pending_exercise.rs | 1 - .../state/exercises/pending_test_exercise.rs | 4 - tests/fixture/state/info.toml | 15 ---- tests/fixture/success/Cargo.toml | 12 --- .../fixture/success/exercises/compSuccess.rs | 1 - .../fixture/success/exercises/testSuccess.rs | 7 -- tests/fixture/success/info.toml | 10 --- tests/integration_tests.rs | 81 +++++-------------- tests/test_exercises/Cargo.toml | 11 +++ .../exercises/compilation_failure.rs} | 0 .../exercises/compilation_success.rs} | 0 .../exercises/not_in_info.rs} | 0 .../test_exercises/exercises/test_failure.rs | 9 +++ .../test_exercises/exercises/test_success.rs | 9 +++ tests/test_exercises/info.toml | 19 +++++ 21 files changed, 71 insertions(+), 170 deletions(-) delete mode 100644 tests/fixture/failure/Cargo.toml delete mode 100644 tests/fixture/failure/exercises/testFailure.rs delete mode 100644 tests/fixture/failure/exercises/testNotPassed.rs delete mode 100644 tests/fixture/failure/info.toml delete mode 100644 tests/fixture/state/Cargo.toml delete mode 100644 tests/fixture/state/exercises/pending_exercise.rs delete mode 100644 tests/fixture/state/exercises/pending_test_exercise.rs delete mode 100644 tests/fixture/state/info.toml delete mode 100644 tests/fixture/success/Cargo.toml delete mode 100644 tests/fixture/success/exercises/compSuccess.rs delete mode 100644 tests/fixture/success/exercises/testSuccess.rs delete mode 100644 tests/fixture/success/info.toml create mode 100644 tests/test_exercises/Cargo.toml rename tests/{fixture/failure/exercises/compFailure.rs => test_exercises/exercises/compilation_failure.rs} (100%) rename tests/{fixture/failure/exercises/compNoExercise.rs => test_exercises/exercises/compilation_success.rs} (100%) rename tests/{fixture/state/exercises/finished_exercise.rs => test_exercises/exercises/not_in_info.rs} (100%) create mode 100644 tests/test_exercises/exercises/test_failure.rs create mode 100644 tests/test_exercises/exercises/test_success.rs create mode 100644 tests/test_exercises/info.toml diff --git a/Cargo.toml b/Cargo.toml index ddf3c70332..685e8ac86a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,7 @@ [workspace] resolver = "2" exclude = [ - "tests/fixture/failure", - "tests/fixture/state", - "tests/fixture/success", + "tests/test_exercises", "dev", ] diff --git a/tests/fixture/failure/Cargo.toml b/tests/fixture/failure/Cargo.toml deleted file mode 100644 index 7ee2f068eb..0000000000 --- a/tests/fixture/failure/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "failure" -edition = "2021" -publish = false - -[[bin]] -name = "compFailure" -path = "exercises/compFailure.rs" - -[[bin]] -name = "compNoExercise" -path = "exercises/compNoExercise.rs" - -[[bin]] -name = "testFailure" -path = "exercises/testFailure.rs" - -[[bin]] -name = "testNotPassed" -path = "exercises/testNotPassed.rs" diff --git a/tests/fixture/failure/exercises/testFailure.rs b/tests/fixture/failure/exercises/testFailure.rs deleted file mode 100644 index fcbcf90a4a..0000000000 --- a/tests/fixture/failure/exercises/testFailure.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() {} - -#[test] -fn passing() { - asset!(true); -} diff --git a/tests/fixture/failure/exercises/testNotPassed.rs b/tests/fixture/failure/exercises/testNotPassed.rs deleted file mode 100644 index de0d61c0b1..0000000000 --- a/tests/fixture/failure/exercises/testNotPassed.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() {} - -#[test] -fn not_passing() { - assert!(false); -} diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml deleted file mode 100644 index 554607a8f7..0000000000 --- a/tests/fixture/failure/info.toml +++ /dev/null @@ -1,10 +0,0 @@ -format_version = 1 - -[[exercises]] -name = "compFailure" -test = false -hint = "" - -[[exercises]] -name = "testFailure" -hint = "Hello!" diff --git a/tests/fixture/state/Cargo.toml b/tests/fixture/state/Cargo.toml deleted file mode 100644 index adbd8ab1c3..0000000000 --- a/tests/fixture/state/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "state" -edition = "2021" -publish = false - -[[bin]] -name = "pending_exercise" -path = "exercises/pending_exercise.rs" - -[[bin]] -name = "pending_test_exercise" -path = "exercises/pending_test_exercise.rs" - -[[bin]] -name = "finished_exercise" -path = "exercises/finished_exercise.rs" diff --git a/tests/fixture/state/exercises/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs deleted file mode 100644 index f328e4d9d0..0000000000 --- a/tests/fixture/state/exercises/pending_exercise.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} diff --git a/tests/fixture/state/exercises/pending_test_exercise.rs b/tests/fixture/state/exercises/pending_test_exercise.rs deleted file mode 100644 index 718e1dbb7d..0000000000 --- a/tests/fixture/state/exercises/pending_test_exercise.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() {} - -#[test] -fn it_works() {} diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml deleted file mode 100644 index ff0b932e4e..0000000000 --- a/tests/fixture/state/info.toml +++ /dev/null @@ -1,15 +0,0 @@ -format_version = 1 - -[[exercises]] -name = "pending_exercise" -test = false -hint = """""" - -[[exercises]] -name = "pending_test_exercise" -hint = """""" - -[[exercises]] -name = "finished_exercise" -test = false -hint = """""" diff --git a/tests/fixture/success/Cargo.toml b/tests/fixture/success/Cargo.toml deleted file mode 100644 index 028cf35a63..0000000000 --- a/tests/fixture/success/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "success" -edition = "2021" -publish = false - -[[bin]] -name = "compSuccess" -path = "exercises/compSuccess.rs" - -[[bin]] -name = "testSuccess" -path = "exercises/testSuccess.rs" diff --git a/tests/fixture/success/exercises/compSuccess.rs b/tests/fixture/success/exercises/compSuccess.rs deleted file mode 100644 index f328e4d9d0..0000000000 --- a/tests/fixture/success/exercises/compSuccess.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} diff --git a/tests/fixture/success/exercises/testSuccess.rs b/tests/fixture/success/exercises/testSuccess.rs deleted file mode 100644 index 4296cf61a9..0000000000 --- a/tests/fixture/success/exercises/testSuccess.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() {} - -#[test] -fn passing() { - println!("THIS TEST TOO SHALL PASS"); - assert!(true); -} diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml deleted file mode 100644 index d66d7d4764..0000000000 --- a/tests/fixture/success/info.toml +++ /dev/null @@ -1,10 +0,0 @@ -format_version = 1 - -[[exercises]] -name = "compSuccess" -test = false -hint = """""" - -[[exercises]] -name = "testSuccess" -hint = """""" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index d5afd2ca6a..f21ee2f6fa 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -91,99 +91,62 @@ impl<'a> Cmd<'a> { } #[test] -fn fails_when_in_wrong_dir() { +fn wrong_dir() { Cmd::default().current_dir("tests").fail(); } #[test] -fn run_single_compile_success() { +fn run_compilation_success() { Cmd::default() - .current_dir("tests/fixture/success") - .args(&["run", "compSuccess"]) + .current_dir("tests/test_exercises") + .args(&["run", "compilation_success"]) .success(); } #[test] -fn run_single_compile_failure() { +fn run_compilation_failure() { Cmd::default() - .current_dir("tests/fixture/failure") - .args(&["run", "compFailure"]) + .current_dir("tests/test_exercises") + .args(&["run", "compilation_failure"]) .fail(); } #[test] -fn run_single_test_success() { +fn run_test_success() { Cmd::default() - .current_dir("tests/fixture/success") - .args(&["run", "testSuccess"]) + .current_dir("tests/test_exercises") + .args(&["run", "test_success"]) + .stdout("\nOutput from `main` function\n") .success(); } #[test] -fn run_single_test_failure() { +fn run_test_failure() { Cmd::default() - .current_dir("tests/fixture/failure") - .args(&["run", "testFailure"]) + .current_dir("tests/test_exercises") + .args(&["run", "test_failure"]) .fail(); } #[test] -fn run_single_test_not_passed() { +fn run_exercise_not_in_info() { Cmd::default() - .current_dir("tests/fixture/failure") - .args(&["run", "testNotPassed.rs"]) + .current_dir("tests/test_exercises") + .args(&["run", "not_in_info"]) .fail(); } #[test] -fn run_single_test_no_exercise() { - Cmd::default() - .current_dir("tests/fixture/failure") - .args(&["run", "compNoExercise.rs"]) - .fail(); -} - -#[test] -fn reset_single_exercise() { - Cmd::default().args(&["reset", "intro1"]).success(); -} - -#[test] -fn reset_no_exercise() { +fn reset_without_exercise_name() { Cmd::default().args(&["reset"]).fail(); } #[test] -fn get_hint_for_single_test() { +fn hint() { Cmd::default() - .current_dir("tests/fixture/failure") - .args(&["hint", "testFailure"]) - .stdout("Hello!\n") + .current_dir("tests/test_exercises") + .args(&["hint", "test_failure"]) + .stdout("The answer to everything: 42\n") .full_stdout() .success(); } - -#[test] -fn run_compile_exercise_does_not_prompt() { - Cmd::default() - .current_dir("tests/fixture/state") - .args(&["run", "pending_exercise"]) - .success(); -} - -#[test] -fn run_test_exercise_does_not_prompt() { - Cmd::default() - .current_dir("tests/fixture/state") - .args(&["run", "pending_test_exercise"]) - .success(); -} - -#[test] -fn run_single_test_success_with_output() { - Cmd::default() - .current_dir("tests/fixture/success") - .args(&["run", "testSuccess"]) - .stdout("\nTHIS TEST TOO SHALL PASS\n") - .success(); -} diff --git a/tests/test_exercises/Cargo.toml b/tests/test_exercises/Cargo.toml new file mode 100644 index 0000000000..6b81751802 --- /dev/null +++ b/tests/test_exercises/Cargo.toml @@ -0,0 +1,11 @@ +bin = [ + { name = "compilation_success", path = "exercises/compilation_success.rs" }, + { name = "compilation_failure", path = "exercises/compilation_failure.rs" }, + { name = "test_success", path = "exercises/test_success.rs" }, + { name = "test_failure", path = "exercises/test_failure.rs" }, +] + +[package] +name = "test_exercises" +edition = "2021" +publish = false diff --git a/tests/fixture/failure/exercises/compFailure.rs b/tests/test_exercises/exercises/compilation_failure.rs similarity index 100% rename from tests/fixture/failure/exercises/compFailure.rs rename to tests/test_exercises/exercises/compilation_failure.rs diff --git a/tests/fixture/failure/exercises/compNoExercise.rs b/tests/test_exercises/exercises/compilation_success.rs similarity index 100% rename from tests/fixture/failure/exercises/compNoExercise.rs rename to tests/test_exercises/exercises/compilation_success.rs diff --git a/tests/fixture/state/exercises/finished_exercise.rs b/tests/test_exercises/exercises/not_in_info.rs similarity index 100% rename from tests/fixture/state/exercises/finished_exercise.rs rename to tests/test_exercises/exercises/not_in_info.rs diff --git a/tests/test_exercises/exercises/test_failure.rs b/tests/test_exercises/exercises/test_failure.rs new file mode 100644 index 0000000000..9dc142a405 --- /dev/null +++ b/tests/test_exercises/exercises/test_failure.rs @@ -0,0 +1,9 @@ +fn main() {} + +#[cfg(test)] +mod tests { + #[test] + fn fails() { + asset!(false); + } +} diff --git a/tests/test_exercises/exercises/test_success.rs b/tests/test_exercises/exercises/test_success.rs new file mode 100644 index 0000000000..8c8a3c61d0 --- /dev/null +++ b/tests/test_exercises/exercises/test_success.rs @@ -0,0 +1,9 @@ +fn main() { + println!("Output from `main` function"); +} + +#[cfg(test)] +mod tests { + #[test] + fn passes() {} +} diff --git a/tests/test_exercises/info.toml b/tests/test_exercises/info.toml new file mode 100644 index 0000000000..d91094c0ea --- /dev/null +++ b/tests/test_exercises/info.toml @@ -0,0 +1,19 @@ +format_version = 1 + +[[exercises]] +name = "compilation_success" +test = false +hint = "" + +[[exercises]] +name = "compilation_failure" +test = false +hint = "" + +[[exercises]] +name = "test_success" +hint = "" + +[[exercises]] +name = "test_failure" +hint = "The answer to everything: 42" From 8beb2908420b0225aabe983e3425055db761b356 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Jul 2024 16:14:38 +0200 Subject: [PATCH 1107/1432] Test initialization --- src/main.rs | 4 - tests/integration_tests.rs | 100 ++++++++++++------ .../test_exercises/exercises/test_failure.rs | 2 +- 3 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3c96d1a998..658d551d12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,10 +95,6 @@ fn main() -> Result<()> { match args.command { Some(Subcommands::Init) => { - if DEBUG_PROFILE { - bail!("Disabled in the debug build"); - } - { let mut stdout = io::stdout().lock(); stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f21ee2f6fa..3ab54f9722 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,15 +1,23 @@ use std::{ env::{self, consts::EXE_SUFFIX}, + fs, process::{Command, Stdio}, str::from_utf8, }; +enum Output<'a> { + FullStdout(&'a str), + PartialStdout(&'a str), + PartialStderr(&'a str), +} + +use Output::*; + #[derive(Default)] struct Cmd<'a> { current_dir: Option<&'a str>, args: &'a [&'a str], - stdout: Option<&'a str>, - full_stdout: bool, + output: Option>, } impl<'a> Cmd<'a> { @@ -26,14 +34,8 @@ impl<'a> Cmd<'a> { } #[inline] - fn stdout(&mut self, stdout: &'a str) -> &mut Self { - self.stdout = Some(stdout); - self - } - - #[inline] - fn full_stdout(&mut self) -> &mut Self { - self.full_stdout = true; + fn output(&mut self, output: Output<'a>) -> &mut Self { + self.output = Some(output); self } @@ -57,26 +59,32 @@ impl<'a> Cmd<'a> { cmd.current_dir(current_dir); } - cmd.args(self.args) - .stdin(Stdio::null()) - .stderr(Stdio::null()); - - let status = if let Some(expected_stdout) = self.stdout { - let output = cmd.output().unwrap(); - let stdout = from_utf8(&output.stdout).unwrap(); - - if self.full_stdout { - assert_eq!(stdout, expected_stdout); - } else { - assert!(stdout.contains(expected_stdout)); + cmd.args(self.args).stdin(Stdio::null()); + + let status = match self.output { + None => cmd + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .unwrap(), + Some(FullStdout(stdout)) => { + let output = cmd.stderr(Stdio::null()).output().unwrap(); + assert_eq!(from_utf8(&output.stdout).unwrap(), stdout); + output.status + } + Some(PartialStdout(stdout)) => { + let output = cmd.stderr(Stdio::null()).output().unwrap(); + assert!(from_utf8(&output.stdout).unwrap().contains(stdout)); + output.status + } + Some(PartialStderr(stderr)) => { + let output = cmd.stdout(Stdio::null()).output().unwrap(); + assert!(from_utf8(&output.stderr).unwrap().contains(stderr)); + output.status } - - output.status - } else { - cmd.stdout(Stdio::null()).status().unwrap() }; - assert_eq!(status.success(), success); + assert_eq!(status.success(), success, "{cmd:?}"); } #[inline] @@ -90,11 +98,6 @@ impl<'a> Cmd<'a> { } } -#[test] -fn wrong_dir() { - Cmd::default().current_dir("tests").fail(); -} - #[test] fn run_compilation_success() { Cmd::default() @@ -116,7 +119,7 @@ fn run_test_success() { Cmd::default() .current_dir("tests/test_exercises") .args(&["run", "test_success"]) - .stdout("\nOutput from `main` function\n") + .output(PartialStdout("\nOutput from `main` function\n")) .success(); } @@ -146,7 +149,34 @@ fn hint() { Cmd::default() .current_dir("tests/test_exercises") .args(&["hint", "test_failure"]) - .stdout("The answer to everything: 42\n") - .full_stdout() + .output(FullStdout("The answer to everything: 42\n")) .success(); } + +#[test] +fn init() { + let _ = fs::remove_dir_all("tests/rustlings"); + + Cmd::default().current_dir("tests").fail(); + + Cmd::default() + .current_dir("tests") + .args(&["init"]) + .success(); + + // Running `init` after a successful initialization. + Cmd::default() + .current_dir("tests") + .args(&["init"]) + .output(PartialStderr("`cd rustlings`")) + .fail(); + + // Running `init` in the initialized directory. + Cmd::default() + .current_dir("tests/rustlings") + .args(&["init"]) + .output(PartialStderr("already initialized")) + .fail(); + + fs::remove_dir_all("tests/rustlings").unwrap(); +} diff --git a/tests/test_exercises/exercises/test_failure.rs b/tests/test_exercises/exercises/test_failure.rs index 9dc142a405..8c8d59d807 100644 --- a/tests/test_exercises/exercises/test_failure.rs +++ b/tests/test_exercises/exercises/test_failure.rs @@ -4,6 +4,6 @@ fn main() {} mod tests { #[test] fn fails() { - asset!(false); + assert!(false); } } From 1937b4bf664568afcdb2c73247b593299d3fca6d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Jul 2024 16:26:48 +0200 Subject: [PATCH 1108/1432] Use the rexported crossterm from ratatui --- Cargo.lock | 45 ++++++++++++++++++------------------- Cargo.toml | 3 +-- src/app_state.rs | 2 +- src/exercise.rs | 2 +- src/init.rs | 2 +- src/list.rs | 13 ++++++----- src/progress_bar.rs | 2 +- src/run.rs | 2 +- src/watch/state.rs | 2 +- src/watch/terminal_event.rs | 2 +- 10 files changed, 38 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5932578ac3..ccd9013008 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,9 +22,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -37,33 +37,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -116,9 +116,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.10" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6b81fb3c84f5563d509c59b5a48d935f689e993afa90fe39047f05adef9142" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", "clap_derive", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.10" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca6706fd5224857d9ac5eb9355f6683563cc0541c7cd9d014043b57cbec78ac" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck", "proc-macro2", @@ -150,15 +150,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "compact_str" @@ -294,9 +294,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -519,7 +519,6 @@ version = "6.1.0" dependencies = [ "anyhow", "clap", - "crossterm", "hashbrown", "notify-debouncer-mini", "os_pipe", diff --git a/Cargo.toml b/Cargo.toml index 685e8ac86a..01e679ee97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,8 +45,7 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.10", features = ["derive"] } -crossterm = "0.27.0" +clap = { version = "4.5.11", features = ["derive"] } hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" diff --git a/src/app_state.rs b/src/app_state.rs index e08f94c667..8995e81cfc 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Context, Result}; -use crossterm::style::Stylize; +use ratatui::crossterm::style::Stylize; use serde::Deserialize; use std::{ fs::{self, File}, diff --git a/src/exercise.rs b/src/exercise.rs index b6adc141e7..960eec0e76 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use crossterm::style::{style, StyledContent, Stylize}; +use ratatui::crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, io::Write, diff --git a/src/init.rs b/src/init.rs index 4063ca75f0..bfa0ab860d 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Context, Result}; -use crossterm::style::Stylize; +use ratatui::crossterm::style::Stylize; use std::{ env::set_current_dir, fs::{self, create_dir}, diff --git a/src/list.rs b/src/list.rs index 790c02fe4e..15836a44c3 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,10 +1,13 @@ use anyhow::Result; -use crossterm::{ - event::{self, Event, KeyCode, KeyEventKind}, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, - ExecutableCommand, +use ratatui::{ + backend::CrosstermBackend, + crossterm::{ + event::{self, Event, KeyCode, KeyEventKind}, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, + }, + Terminal, }; -use ratatui::{backend::CrosstermBackend, Terminal}; use std::io; use crate::app_state::AppState; diff --git a/src/progress_bar.rs b/src/progress_bar.rs index 4a54170a91..7f07ad593f 100644 --- a/src/progress_bar.rs +++ b/src/progress_bar.rs @@ -14,7 +14,7 @@ const PROGRESS_EXCEEDS_MAX_ERR: &str = /// Terminal progress bar to be used when not using Ratataui. pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result { - use crossterm::style::Stylize; + use ratatui::crossterm::style::Stylize; if progress > total { bail!(PROGRESS_EXCEEDS_MAX_ERR); diff --git a/src/run.rs b/src/run.rs index 899d0a94db..3965a0d0f0 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use crossterm::style::{style, Stylize}; +use ratatui::crossterm::style::{style, Stylize}; use std::io::{self, Write}; use crate::{ diff --git a/src/watch/state.rs b/src/watch/state.rs index 78af30a440..26ff4118b3 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use crossterm::{ +use ratatui::crossterm::{ style::{style, Stylize}, terminal, }; diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index f54af17a7d..3a1762d300 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -1,4 +1,4 @@ -use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; +use ratatui::crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; use std::sync::mpsc::Sender; use super::WatchEvent; From 2ae9f3555b9a4065eeb14f43e756f617ca8cf0ea Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 28 Jul 2024 13:30:31 +0200 Subject: [PATCH 1109/1432] Update deps --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ccd9013008..afb98cd986 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -598,9 +598,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -698,18 +698,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.16" +version = "0.22.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" dependencies = [ "indexmap", "serde", @@ -755,9 +755,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -947,9 +947,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.15" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557404e450152cd6795bb558bca69e43c585055f4606e3bcae5894fc6dac9ba0" +checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 01e679ee97..4aad110fba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" [workspace.dependencies] serde = { version = "1.0.204", features = ["derive"] } -toml_edit = { version = "0.22.16", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.17", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" From 3a99542f7346a639115abd65ea277f32ecd3e5a1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 28 Jul 2024 17:39:46 +0200 Subject: [PATCH 1110/1432] Run the final check in parallel --- CHANGELOG.md | 7 ++++++ src/app_state.rs | 63 ++++++++++++++++++++++++++++++------------------ 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80e000a189..01a2fb2420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ + + +## 6.1.1 (UNRELEASED) + +- Run the final check of all exercises in parallel. +- Small exercise improvements. + ## 6.1.0 (2024-07-10) diff --git a/src/app_state.rs b/src/app_state.rs index 8995e81cfc..40c913089b 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,11 +1,11 @@ -use anyhow::{bail, Context, Result}; -use ratatui::crossterm::style::Stylize; +use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; use std::{ fs::{self, File}, io::{Read, StdoutLock, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, + thread, }; use crate::{ @@ -373,34 +373,50 @@ impl AppState { if let Some(ind) = self.next_pending_exercise_ind() { self.set_current_exercise_ind(ind)?; - return Ok(ExercisesProgress::NewPending); } writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; - let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - for (exercise_ind, exercise) in self.exercises().iter().enumerate() { - write!(writer, "Running {exercise} ... ")?; - writer.flush()?; - - let success = exercise.run_exercise(&mut output, &self.target_dir)?; - if !success { - writeln!(writer, "{}\n", "FAILED".red())?; - - self.current_exercise_ind = exercise_ind; - - // No check if the exercise is done before setting it to pending - // because no pending exercise was found. - self.exercises[exercise_ind].done = false; - self.n_done -= 1; - - self.write()?; - - return Ok(ExercisesProgress::NewPending); + let n_exercises = self.exercises.len(); + + let pending_exercise_ind = thread::scope(|s| { + let handles = self + .exercises + .iter_mut() + .map(|exercise| { + s.spawn(|| { + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + let success = exercise.run_exercise(&mut output, &self.target_dir)?; + exercise.done = success; + Ok::<_, Error>(success) + }) + }) + .collect::>(); + + for (exercise_ind, handle) in handles.into_iter().enumerate() { + write!(writer, "\rProgress: {exercise_ind}/{n_exercises}")?; + writer.flush()?; + + let success = handle.join().unwrap()?; + if !success { + writer.write_all(b"\n\n")?; + return Ok(Some(exercise_ind)); + } } - writeln!(writer, "{}", "ok".green())?; + Ok::<_, Error>(None) + })?; + + if let Some(pending_exercise_ind) = pending_exercise_ind { + self.current_exercise_ind = pending_exercise_ind; + self.n_done = self + .exercises + .iter() + .filter(|exercise| exercise.done) + .count() as u16; + self.write()?; + return Ok(ExercisesProgress::NewPending); } // Write that the last exercise is done. @@ -426,7 +442,6 @@ Try running `cargo --version` to diagnose the problem."; const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" 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 = "+----------------------------------------------------+ From 74fab994e2133cc40718abe923645922785c2a57 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 28 Jul 2024 20:30:23 +0200 Subject: [PATCH 1111/1432] Make the output optional --- Cargo.toml | 4 +++ src/app_state.rs | 5 ++-- src/cmd.rs | 54 ++++++++++++++++++++++--------------- src/dev/check.rs | 5 ++-- src/exercise.rs | 67 +++++++++++++++++++++++++++++----------------- src/run.rs | 2 +- src/watch/state.rs | 2 +- 7 files changed, 85 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4aad110fba..0577a6e8d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,3 +63,7 @@ panic = "abort" [package.metadata.release] pre-release-hook = ["./release-hook.sh"] + +# TODO: Remove after the following fix is released: https://github.com/rust-lang/rust-clippy/pull/13102 +[lints.clippy] +needless_option_as_deref = "allow" diff --git a/src/app_state.rs b/src/app_state.rs index 40c913089b..537732bc99 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,7 +11,7 @@ use std::{ use crate::{ clear_terminal, embedded::EMBEDDED_FILES, - exercise::{Exercise, RunnableExercise, OUTPUT_CAPACITY}, + exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, DEBUG_PROFILE, }; @@ -386,8 +386,7 @@ impl AppState { .iter_mut() .map(|exercise| { s.spawn(|| { - let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - let success = exercise.run_exercise(&mut output, &self.target_dir)?; + let success = exercise.run_exercise(None, &self.target_dir)?; exercise.done = success; Ok::<_, Error>(success) }) diff --git a/src/cmd.rs b/src/cmd.rs index 6092f531ca..efeb59882c 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,30 +1,42 @@ use anyhow::{Context, Result}; -use std::{io::Read, path::Path, process::Command}; +use std::{ + io::Read, + path::Path, + process::{Command, Stdio}, +}; /// Run a command with a description for a possible error and append the merged stdout and stderr. /// The boolean in the returned `Result` is true if the command's exit status is success. -pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec) -> Result { - let (mut reader, writer) = os_pipe::pipe() - .with_context(|| format!("Failed to create a pipe to run the command `{description}``"))?; +pub fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec>) -> Result { + let spawn = |mut cmd: Command| { + // NOTE: The closure drops `cmd` which prevents a pipe deadlock. + cmd.spawn() + .with_context(|| format!("Failed to run the command `{description}`")) + }; - let writer_clone = writer.try_clone().with_context(|| { - format!("Failed to clone the pipe writer for the command `{description}`") - })?; + let mut handle = if let Some(output) = output { + let (mut reader, writer) = os_pipe::pipe().with_context(|| { + format!("Failed to create a pipe to run the command `{description}``") + })?; - let mut handle = cmd - .stdout(writer_clone) - .stderr(writer) - .spawn() - .with_context(|| format!("Failed to run the command `{description}`"))?; + let writer_clone = writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{description}`") + })?; - // Prevent pipe deadlock. - drop(cmd); + cmd.stdout(writer_clone).stderr(writer); + let handle = spawn(cmd)?; - reader - .read_to_end(output) - .with_context(|| format!("Failed to read the output of the command `{description}`"))?; + reader + .read_to_end(output) + .with_context(|| format!("Failed to read the output of the command `{description}`"))?; - output.push(b'\n'); + output.push(b'\n'); + + handle + } else { + cmd.stdout(Stdio::null()).stderr(Stdio::null()); + spawn(cmd)? + }; handle .wait() @@ -42,14 +54,14 @@ pub struct CargoCmd<'a> { /// Added as `--target-dir` if `Self::dev` is true. pub target_dir: &'a Path, /// The output buffer to append the merged stdout and stderr. - pub output: &'a mut Vec, + pub output: Option<&'a mut Vec>, /// true while developing Rustlings. pub dev: bool, } impl<'a> CargoCmd<'a> { /// Run `cargo SUBCOMMAND --bin EXERCISE_NAME … ARGS`. - pub fn run(&mut self) -> Result { + pub fn run(self) -> Result { let mut cmd = Command::new("cargo"); cmd.arg(self.subcommand); @@ -86,7 +98,7 @@ mod tests { cmd.arg("Hello"); let mut output = Vec::with_capacity(8); - run_cmd(cmd, "echo …", &mut output).unwrap(); + run_cmd(cmd, "echo …", Some(&mut output)).unwrap(); assert_eq!(output, b"Hello\n\n"); } diff --git a/src/dev/check.rs b/src/dev/check.rs index 5c35462c40..2090dab8d7 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -184,8 +184,7 @@ fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<( error_occurred.store(true, atomic::Ordering::Relaxed); }; - let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - match exercise_info.run_exercise(&mut output, target_dir) { + match exercise_info.run_exercise(None, target_dir) { Ok(true) => error(b"Already solved!"), Ok(false) => (), Err(e) => error(e.to_string().as_bytes()), @@ -244,7 +243,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &P } let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - match exercise_info.run_solution(&mut output, target_dir) { + match exercise_info.run_solution(Some(&mut output), target_dir) { Ok(true) => { paths.lock().unwrap().insert(PathBuf::from(path)); } diff --git a/src/exercise.rs b/src/exercise.rs index 960eec0e76..5cb434bf4a 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -19,8 +19,10 @@ pub const OUTPUT_CAPACITY: usize = 1 << 14; // Run an exercise binary and append its output to the `output` buffer. // Compilation must be done before calling this method. -fn run_bin(bin_name: &str, output: &mut Vec, target_dir: &Path) -> Result { - writeln!(output, "{}", "Output".underlined())?; +fn run_bin(bin_name: &str, mut output: Option<&mut Vec>, target_dir: &Path) -> Result { + if let Some(output) = output.as_deref_mut() { + writeln!(output, "{}", "Output".underlined())?; + } // 7 = "/debug/".len() let mut bin_path = PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + bin_name.len()); @@ -28,19 +30,25 @@ fn run_bin(bin_name: &str, output: &mut Vec, target_dir: &Path) -> Result, target_dir: &Path) -> Result { - output.clear(); + fn run( + &self, + bin_name: &str, + mut output: Option<&mut Vec>, + target_dir: &Path, + ) -> Result { + if let Some(output) = output.as_deref_mut() { + output.clear(); + } // Developing the official Rustlings. let dev = DEBUG_PROFILE && in_official_repo(); @@ -90,7 +105,7 @@ pub trait RunnableExercise { description: "cargo build …", hide_warnings: false, target_dir, - output, + output: output.as_deref_mut(), dev, } .run()?; @@ -99,7 +114,9 @@ pub trait RunnableExercise { } // Discard the output of `cargo build` because it will be shown again by Clippy. - output.clear(); + if let Some(output) = output.as_deref_mut() { + output.clear(); + } // `--profile test` is required to also check code with `[cfg(test)]`. let clippy_args: &[&str] = if self.strict_clippy() { @@ -114,7 +131,7 @@ pub trait RunnableExercise { description: "cargo clippy …", hide_warnings: false, target_dir, - output, + output: output.as_deref_mut(), dev, } .run()?; @@ -123,7 +140,7 @@ pub trait RunnableExercise { } if !self.test() { - return run_bin(bin_name, output, target_dir); + return run_bin(bin_name, output.as_deref_mut(), target_dir); } let test_success = CargoCmd { @@ -134,12 +151,12 @@ pub trait RunnableExercise { // Hide warnings because they are shown by Clippy. hide_warnings: true, target_dir, - output, + output: output.as_deref_mut(), dev, } .run()?; - let run_success = run_bin(bin_name, output, target_dir)?; + let run_success = run_bin(bin_name, output.as_deref_mut(), target_dir)?; Ok(test_success && run_success) } @@ -147,13 +164,13 @@ pub trait RunnableExercise { /// Compile, check and run the exercise. /// The output is written to the `output` buffer after clearing it. #[inline] - fn run_exercise(&self, output: &mut Vec, target_dir: &Path) -> Result { + fn run_exercise(&self, output: Option<&mut Vec>, target_dir: &Path) -> Result { self.run(self.name(), output, target_dir) } /// Compile, check and run the exercise's solution. /// The output is written to the `output` buffer after clearing it. - fn run_solution(&self, output: &mut Vec, target_dir: &Path) -> Result { + fn run_solution(&self, output: Option<&mut Vec>, target_dir: &Path) -> Result { let name = self.name(); let mut bin_name = String::with_capacity(name.len()); bin_name.push_str(name); diff --git a/src/run.rs b/src/run.rs index 3965a0d0f0..606f0a4340 100644 --- a/src/run.rs +++ b/src/run.rs @@ -11,7 +11,7 @@ use crate::{ pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - let success = exercise.run_exercise(&mut output, app_state.target_dir())?; + let success = exercise.run_exercise(Some(&mut output), app_state.target_dir())?; let mut stdout = io::stdout().lock(); stdout.write_all(&output)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 26ff4118b3..8f01db7d9b 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -54,7 +54,7 @@ impl<'a> WatchState<'a> { let success = self .app_state .current_exercise() - .run_exercise(&mut self.output, self.app_state.target_dir())?; + .run_exercise(Some(&mut self.output), self.app_state.target_dir())?; if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { From c8fddd8f62302395900ae6038a45653f22c994de Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 31 Jul 2024 18:53:25 +0200 Subject: [PATCH 1112/1432] Add Github profile links for every author --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0577a6e8d1..1e0228daa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,10 @@ exclude = [ [workspace.package] version = "6.1.0" authors = [ - "Liv ", - "Mo Bitar ", + "Mo Bitar ", # https://github.com/mo8it + "Liv ", # https://github.com/shadows-withal # Alumni - "Carol (Nichols || Goulding) ", + "Carol (Nichols || Goulding) ", # https://github.com/carols10cents ] repository = "https://github.com/rust-lang/rustlings" license = "MIT" From 2ad408f2b8eb357a3f2b44b7facb9e3bfe4f35a2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 31 Jul 2024 18:54:24 +0200 Subject: [PATCH 1113/1432] Update deps --- Cargo.lock | 21 +++++++++++---------- Cargo.toml | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index afb98cd986..fab8e58435 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,9 +357,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown", ] @@ -587,11 +587,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -617,9 +618,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio", @@ -698,18 +699,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.17" +version = "0.22.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" +checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" dependencies = [ "indexmap", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1e0228daa2..ad9164d840 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" [workspace.dependencies] serde = { version = "1.0.204", features = ["derive"] } -toml_edit = { version = "0.22.17", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.18", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -51,7 +51,7 @@ notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" } -serde_json = "1.0.120" +serde_json = "1.0.121" serde.workspace = true toml_edit.workspace = true From 802b97b2edb142ad6bc4ee10ccc16ece7c6dc346 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 01:07:31 +0200 Subject: [PATCH 1114/1432] Set stdin to null when running the binary of an exercise --- src/cmd.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd.rs b/src/cmd.rs index efeb59882c..d158bfb95f 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -10,7 +10,8 @@ use std::{ pub fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec>) -> Result { let spawn = |mut cmd: Command| { // NOTE: The closure drops `cmd` which prevents a pipe deadlock. - cmd.spawn() + cmd.stdin(Stdio::null()) + .spawn() .with_context(|| format!("Failed to run the command `{description}`")) }; From 766f3c50ec20c9b3fbf95bddf9fc095ce65cef78 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 01:07:56 +0200 Subject: [PATCH 1115/1432] Add hint to run `dev check` again after `dev update` --- src/dev/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 2090dab8d7..1087138ccc 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -41,7 +41,7 @@ fn check_cargo_toml( bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it"); } - bail!("The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it"); + bail!("The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it. Then run `rustlings dev check` again"); } Ok(()) From dacdce1ea245523bf7bf380f91bff76e7f867315 Mon Sep 17 00:00:00 2001 From: Yudai Kawabuchi Date: Thu, 1 Aug 2024 09:47:50 +0900 Subject: [PATCH 1116/1432] fix: update struct name in hashmap3 --- exercises/11_hashmaps/hashmaps3.rs | 4 ++-- solutions/11_hashmaps/hashmaps3.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 9f8fdd7858..7e9584d1ad 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -10,12 +10,12 @@ use std::collections::HashMap; // A structure to store the goal details of a team. #[derive(Default)] -struct Team { +struct TeamScores { goals_scored: u8, goals_conceded: u8, } -fn build_scores_table(results: &str) -> HashMap<&str, Team> { +fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { // The name of the team is the key and its associated struct is the value. let mut scores = HashMap::new(); diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 54f480b9bc..c075b0f6cd 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -10,12 +10,12 @@ use std::collections::HashMap; // A structure to store the goal details of a team. #[derive(Default)] -struct Team { +struct TeamScores { goals_scored: u8, goals_conceded: u8, } -fn build_scores_table(results: &str) -> HashMap<&str, Team> { +fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { // The name of the team is the key and its associated struct is the value. let mut scores = HashMap::new(); @@ -28,13 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, Team> { let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); // Insert the default with zeros if a team doesn't exist yet. - let team_1 = scores.entry(team_1_name).or_insert_with(Team::default); + let team_1 = scores.entry(team_1_name).or_insert_with(TeamScores::default); // Update the values. team_1.goals_scored += team_1_score; team_1.goals_conceded += team_2_score; // Similarely for the second team. - let team_2 = scores.entry(team_2_name).or_insert_with(Team::default); + let team_2 = scores.entry(team_2_name).or_insert_with(TeamScores::default); team_2.goals_scored += team_2_score; team_2.goals_conceded += team_1_score; } From e65ae09789410c230a863ad219b90c434adf5e4f Mon Sep 17 00:00:00 2001 From: Yudai Kawabuchi Date: Thu, 1 Aug 2024 09:55:25 +0900 Subject: [PATCH 1117/1432] fix format --- solutions/11_hashmaps/hashmaps3.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index c075b0f6cd..9c58b2d38e 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -28,13 +28,17 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); // Insert the default with zeros if a team doesn't exist yet. - let team_1 = scores.entry(team_1_name).or_insert_with(TeamScores::default); + let team_1 = scores + .entry(team_1_name) + .or_insert_with(TeamScores::default); // Update the values. team_1.goals_scored += team_1_score; team_1.goals_conceded += team_2_score; // Similarely for the second team. - let team_2 = scores.entry(team_2_name).or_insert_with(TeamScores::default); + let team_2 = scores + .entry(team_2_name) + .or_insert_with(TeamScores::default); team_2.goals_scored += team_2_score; team_2.goals_conceded += team_1_score; } From 455d87caddc69d1b2589cd11638a341e136c891b Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 11:26:30 +0200 Subject: [PATCH 1118/1432] Fix capacity --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 5cb434bf4a..605d5f101b 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -172,7 +172,7 @@ pub trait RunnableExercise { /// The output is written to the `output` buffer after clearing it. fn run_solution(&self, output: Option<&mut Vec>, target_dir: &Path) -> Result { let name = self.name(); - let mut bin_name = String::with_capacity(name.len()); + let mut bin_name = String::with_capacity(name.len() + 4); bin_name.push_str(name); bin_name.push_str("_sol"); From 33a56803281ec4ec84fbe61919e9c825f1f446f7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 11:28:26 +0200 Subject: [PATCH 1119/1432] Hide `cargo build` warnings if there is no output --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 605d5f101b..8a0406132e 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -103,7 +103,7 @@ pub trait RunnableExercise { args: &[], bin_name, description: "cargo build …", - hide_warnings: false, + hide_warnings: output.is_none(), target_dir, output: output.as_deref_mut(), dev, From c7590dd752ab35d06a85f016e88921f10934e6aa Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 15:23:54 +0200 Subject: [PATCH 1120/1432] Improve the runner --- src/app_state.rs | 51 ++++-------------- src/cmd.rs | 126 +++++++++++++++++++++++++++++++++------------ src/dev.rs | 4 +- src/dev/check.rs | 34 ++++++------ src/dev/update.rs | 3 +- src/exercise.rs | 103 +++++++++++++----------------------- src/main.rs | 18 +------ src/run.rs | 2 +- src/watch/state.rs | 2 +- 9 files changed, 162 insertions(+), 181 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 537732bc99..ea99746b12 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,19 +1,18 @@ use anyhow::{bail, Context, Error, Result}; -use serde::Deserialize; use std::{ fs::{self, File}, io::{Read, StdoutLock, Write}, - path::{Path, PathBuf}, + path::Path, process::{Command, Stdio}, thread, }; use crate::{ clear_terminal, + cmd::CmdRunner, embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, - DEBUG_PROFILE, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; @@ -34,31 +33,6 @@ pub enum StateFileStatus { NotRead, } -// Parses parts of the output of `cargo metadata`. -#[derive(Deserialize)] -struct CargoMetadata { - target_directory: PathBuf, -} - -pub fn parse_target_dir() -> Result { - // Get the target directory from Cargo. - let metadata_output = Command::new("cargo") - .arg("metadata") - .arg("-q") - .arg("--format-version") - .arg("1") - .arg("--no-deps") - .stdin(Stdio::null()) - .stderr(Stdio::inherit()) - .output() - .context(CARGO_METADATA_ERR)? - .stdout; - - serde_json::de::from_slice::(&metadata_output) - .context("Failed to read the field `target_directory` from the `cargo metadata` output") - .map(|metadata| metadata.target_directory) -} - pub struct AppState { current_exercise_ind: usize, exercises: Vec, @@ -68,8 +42,7 @@ pub struct AppState { // Preallocated buffer for reading and writing the state file. file_buf: Vec, official_exercises: bool, - // Cargo's target directory. - target_dir: PathBuf, + cmd_runner: CmdRunner, } impl AppState { @@ -123,7 +96,7 @@ impl AppState { exercise_infos: Vec, final_message: String, ) -> Result<(Self, StateFileStatus)> { - let target_dir = parse_target_dir()?; + let cmd_runner = CmdRunner::build()?; let exercises = exercise_infos .into_iter() @@ -157,7 +130,7 @@ impl AppState { final_message, file_buf: Vec::with_capacity(2048), official_exercises: !Path::new("info.toml").exists(), - target_dir, + cmd_runner, }; let state_file_status = slf.update_from_file(); @@ -186,8 +159,8 @@ impl AppState { } #[inline] - pub fn target_dir(&self) -> &Path { - &self.target_dir + pub fn cmd_runner(&self) -> &CmdRunner { + &self.cmd_runner } // Write the state file. @@ -336,7 +309,7 @@ impl AppState { /// Official exercises: Dump the solution file form the binary and return its path. /// Third-party exercises: Check if a solution file exists and return its path in that case. pub fn current_solution_path(&self) -> Result> { - if DEBUG_PROFILE { + if cfg!(debug_assertions) { return Ok(None); } @@ -386,7 +359,7 @@ impl AppState { .iter_mut() .map(|exercise| { s.spawn(|| { - let success = exercise.run_exercise(None, &self.target_dir)?; + let success = exercise.run_exercise(None, &self.cmd_runner)?; exercise.done = success; Ok::<_, Error>(success) }) @@ -434,10 +407,6 @@ impl AppState { } } -const CARGO_METADATA_ERR: &str = "Failed to run the command `cargo metadata …` -Did you already install Rust? -Try running `cargo --version` to diagnose the problem."; - const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" All exercises seem to be done. Recompiling and running all exercises to make sure that all of them are actually done. @@ -490,7 +459,7 @@ mod tests { final_message: String::new(), file_buf: Vec::new(), official_exercises: true, - target_dir: PathBuf::new(), + cmd_runner: CmdRunner::build().unwrap(), }; let mut assert = |done: [bool; 3], expected: [Option; 3]| { diff --git a/src/cmd.rs b/src/cmd.rs index d158bfb95f..1891a283ad 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,13 +1,14 @@ use anyhow::{Context, Result}; +use serde::Deserialize; use std::{ io::Read, - path::Path, + path::PathBuf, process::{Command, Stdio}, }; /// Run a command with a description for a possible error and append the merged stdout and stderr. /// The boolean in the returned `Result` is true if the command's exit status is success. -pub fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec>) -> Result { +fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec>) -> Result { let spawn = |mut cmd: Command| { // NOTE: The closure drops `cmd` which prevents a pipe deadlock. cmd.stdin(Stdio::null()) @@ -45,50 +46,107 @@ pub fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec> .map(|status| status.success()) } -pub struct CargoCmd<'a> { - pub subcommand: &'a str, - pub args: &'a [&'a str], - pub bin_name: &'a str, - pub description: &'a str, - /// RUSTFLAGS="-A warnings" - pub hide_warnings: bool, - /// Added as `--target-dir` if `Self::dev` is true. - pub target_dir: &'a Path, - /// The output buffer to append the merged stdout and stderr. - pub output: Option<&'a mut Vec>, - /// true while developing Rustlings. - pub dev: bool, +// Parses parts of the output of `cargo metadata`. +#[derive(Deserialize)] +struct CargoMetadata { + target_directory: PathBuf, +} + +pub struct CmdRunner { + target_dir: PathBuf, } -impl<'a> CargoCmd<'a> { - /// Run `cargo SUBCOMMAND --bin EXERCISE_NAME … ARGS`. - pub fn run(self) -> Result { +impl CmdRunner { + pub fn build() -> Result { + // Get the target directory from Cargo. + let metadata_output = Command::new("cargo") + .arg("metadata") + .arg("-q") + .arg("--format-version") + .arg("1") + .arg("--no-deps") + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output() + .context(CARGO_METADATA_ERR)? + .stdout; + + let target_dir = serde_json::de::from_slice::(&metadata_output) + .context("Failed to read the field `target_directory` from the `cargo metadata` output") + .map(|metadata| metadata.target_directory)?; + + Ok(Self { target_dir }) + } + + pub fn cargo<'out>( + &self, + subcommand: &str, + bin_name: &str, + output: Option<&'out mut Vec>, + ) -> CargoSubcommand<'out> { let mut cmd = Command::new("cargo"); - cmd.arg(self.subcommand); + cmd.arg(subcommand).arg("-q").arg("--bin").arg(bin_name); // A hack to make `cargo run` work when developing Rustlings. - if self.dev { - cmd.arg("--manifest-path") - .arg("dev/Cargo.toml") - .arg("--target-dir") - .arg(self.target_dir); + #[cfg(debug_assertions)] + cmd.arg("--manifest-path") + .arg("dev/Cargo.toml") + .arg("--target-dir") + .arg(&self.target_dir); + + if output.is_some() { + cmd.arg("--color").arg("always"); } - cmd.arg("--color") - .arg("always") - .arg("-q") - .arg("--bin") - .arg(self.bin_name) - .args(self.args); + CargoSubcommand { cmd, output } + } - if self.hide_warnings { - cmd.env("RUSTFLAGS", "-A warnings"); - } + /// The boolean in the returned `Result` is true if the command's exit status is success. + pub fn run_debug_bin(&self, bin_name: &str, output: Option<&mut Vec>) -> Result { + // 7 = "/debug/".len() + let mut bin_path = + PathBuf::with_capacity(self.target_dir.as_os_str().len() + 7 + bin_name.len()); + bin_path.push(&self.target_dir); + bin_path.push("debug"); + bin_path.push(bin_name); + + run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output) + } +} + +pub struct CargoSubcommand<'out> { + cmd: Command, + output: Option<&'out mut Vec>, +} + +impl<'out> CargoSubcommand<'out> { + #[inline] + pub fn args<'arg, I>(&mut self, args: I) -> &mut Self + where + I: IntoIterator, + { + self.cmd.args(args); + self + } - run_cmd(cmd, self.description, self.output) + /// RUSTFLAGS="-A warnings" + #[inline] + pub fn hide_warnings(&mut self) -> &mut Self { + self.cmd.env("RUSTFLAGS", "-A warnings"); + self + } + + /// The boolean in the returned `Result` is true if the command's exit status is success. + #[inline] + pub fn run(self, description: &str) -> Result { + run_cmd(self.cmd, description, self.output) } } +const CARGO_METADATA_ERR: &str = "Failed to run the command `cargo metadata …` +Did you already install Rust? +Try running `cargo --version` to diagnose the problem."; + #[cfg(test)] mod tests { use super::*; diff --git a/src/dev.rs b/src/dev.rs index 5f7e64c8d6..8af40d6943 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -2,8 +2,6 @@ use anyhow::{bail, Context, Result}; use clap::Subcommand; use std::path::PathBuf; -use crate::DEBUG_PROFILE; - mod check; mod new; mod update; @@ -32,7 +30,7 @@ impl DevCommands { pub fn run(self) -> Result<()> { match self { Self::New { path, no_git } => { - if DEBUG_PROFILE { + if cfg!(debug_assertions) { bail!("Disabled in the debug build"); } diff --git a/src/dev/check.rs b/src/dev/check.rs index 1087138ccc..db5b21fc7c 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -12,11 +12,11 @@ use std::{ }; use crate::{ - app_state::parse_target_dir, cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY}, + cmd::CmdRunner, exercise::{RunnableExercise, OUTPUT_CAPACITY}, info_file::{ExerciseInfo, InfoFile}, - CURRENT_FORMAT_VERSION, DEBUG_PROFILE, + CURRENT_FORMAT_VERSION, }; // Find a char that isn't allowed in the exercise's `name` or `dir`. @@ -37,8 +37,8 @@ fn check_cargo_toml( append_bins(&mut new_bins, exercise_infos, exercise_path_prefix); if old_bins != new_bins { - if DEBUG_PROFILE { - bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it"); + if cfg!(debug_assertions) { + bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it. Then run `cargo run -- dev check` again"); } bail!("The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it. Then run `rustlings dev check` again"); @@ -162,7 +162,7 @@ fn check_unexpected_files( Ok(()) } -fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<()> { +fn check_exercises_unsolved(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { let error_occurred = AtomicBool::new(false); println!( @@ -184,7 +184,7 @@ fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<( error_occurred.store(true, atomic::Ordering::Relaxed); }; - match exercise_info.run_exercise(None, target_dir) { + match exercise_info.run_exercise(None, cmd_runner) { Ok(true) => error(b"Already solved!"), Ok(false) => (), Err(e) => error(e.to_string().as_bytes()), @@ -200,7 +200,7 @@ fn check_exercises_unsolved(info_file: &InfoFile, target_dir: &Path) -> Result<( Ok(()) } -fn check_exercises(info_file: &InfoFile, target_dir: &Path) -> Result<()> { +fn check_exercises(info_file: &InfoFile, cmd_runner: &CmdRunner) -> 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"), @@ -210,10 +210,14 @@ fn check_exercises(info_file: &InfoFile, target_dir: &Path) -> Result<()> { let info_file_paths = check_info_file_exercises(info_file)?; check_unexpected_files("exercises", &info_file_paths)?; - check_exercises_unsolved(info_file, target_dir) + check_exercises_unsolved(info_file, cmd_runner) } -fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &Path) -> Result<()> { +fn check_solutions( + require_solutions: bool, + info_file: &InfoFile, + cmd_runner: &CmdRunner, +) -> Result<()> { let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len())); let error_occurred = AtomicBool::new(false); @@ -243,7 +247,7 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &P } let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - match exercise_info.run_solution(Some(&mut output), target_dir) { + match exercise_info.run_solution(Some(&mut output), cmd_runner) { Ok(true) => { paths.lock().unwrap().insert(PathBuf::from(path)); } @@ -266,8 +270,8 @@ fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &P pub fn check(require_solutions: bool) -> Result<()> { let info_file = InfoFile::parse()?; - // A hack to make `cargo run -- dev check` work when developing Rustlings. - if DEBUG_PROFILE { + if cfg!(debug_assertions) { + // A hack to make `cargo run -- dev check` work when developing Rustlings. check_cargo_toml( &info_file.exercises, include_str!("../../dev-Cargo.toml"), @@ -279,9 +283,9 @@ pub fn check(require_solutions: bool) -> Result<()> { check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"")?; } - let target_dir = parse_target_dir()?; - check_exercises(&info_file, &target_dir)?; - check_solutions(require_solutions, &info_file, &target_dir)?; + let cmd_runner = CmdRunner::build()?; + check_exercises(&info_file, &cmd_runner)?; + check_solutions(require_solutions, &info_file, &cmd_runner)?; println!("\nEverything looks fine!"); diff --git a/src/dev/update.rs b/src/dev/update.rs index 66efe3d05b..680d302f89 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -4,7 +4,6 @@ use std::fs; use crate::{ cargo_toml::updated_cargo_toml, info_file::{ExerciseInfo, InfoFile}, - DEBUG_PROFILE, }; // Update the `Cargo.toml` file. @@ -27,7 +26,7 @@ pub fn update() -> Result<()> { let info_file = InfoFile::parse()?; // A hack to make `cargo run -- dev update` work when developing Rustlings. - if DEBUG_PROFILE { + if cfg!(debug_assertions) { update_cargo_toml( &info_file.exercises, include_str!("../../dev-Cargo.toml"), diff --git a/src/exercise.rs b/src/exercise.rs index 8a0406132e..48b98896bb 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -3,38 +3,25 @@ use ratatui::crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, io::Write, - path::{Path, PathBuf}, - process::Command, }; -use crate::{ - cmd::{run_cmd, CargoCmd}, - in_official_repo, - terminal_link::TerminalFileLink, - DEBUG_PROFILE, -}; +use crate::{cmd::CmdRunner, terminal_link::TerminalFileLink}; /// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; // Run an exercise binary and append its output to the `output` buffer. // Compilation must be done before calling this method. -fn run_bin(bin_name: &str, mut output: Option<&mut Vec>, target_dir: &Path) -> Result { +fn run_bin( + bin_name: &str, + mut output: Option<&mut Vec>, + cmd_runner: &CmdRunner, +) -> Result { if let Some(output) = output.as_deref_mut() { writeln!(output, "{}", "Output".underlined())?; } - // 7 = "/debug/".len() - let mut bin_path = PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + bin_name.len()); - bin_path.push(target_dir); - bin_path.push("debug"); - bin_path.push(bin_name); - - let success = run_cmd( - Command::new(&bin_path), - &bin_path.to_string_lossy(), - output.as_deref_mut(), - )?; + let success = cmd_runner.run_debug_bin(bin_name, output.as_deref_mut())?; if let Some(output) = output { if !success { @@ -89,26 +76,20 @@ pub trait RunnableExercise { &self, bin_name: &str, mut output: Option<&mut Vec>, - target_dir: &Path, + cmd_runner: &CmdRunner, ) -> Result { - if let Some(output) = output.as_deref_mut() { + let output_is_none = if let Some(output) = output.as_deref_mut() { output.clear(); - } + false + } else { + true + }; - // Developing the official Rustlings. - let dev = DEBUG_PROFILE && in_official_repo(); - - let build_success = CargoCmd { - subcommand: "build", - args: &[], - bin_name, - description: "cargo build …", - hide_warnings: output.is_none(), - target_dir, - output: output.as_deref_mut(), - dev, + let mut build_cmd = cmd_runner.cargo("build", bin_name, output.as_deref_mut()); + if output_is_none { + build_cmd.hide_warnings(); } - .run()?; + let build_success = build_cmd.run("cargo build …")?; if !build_success { return Ok(false); } @@ -118,45 +99,33 @@ pub trait RunnableExercise { output.clear(); } + let mut clippy_cmd = cmd_runner.cargo("clippy", bin_name, output.as_deref_mut()); + // `--profile test` is required to also check code with `[cfg(test)]`. - let clippy_args: &[&str] = if self.strict_clippy() { - &["--profile", "test", "--", "-D", "warnings"] + if self.strict_clippy() { + clippy_cmd.args(["--profile", "test", "--", "-D", "warnings"]); } else { - &["--profile", "test"] - }; - let clippy_success = CargoCmd { - subcommand: "clippy", - args: clippy_args, - bin_name, - description: "cargo clippy …", - hide_warnings: false, - target_dir, - output: output.as_deref_mut(), - dev, + clippy_cmd.args(["--profile", "test"]); } - .run()?; + + let clippy_success = clippy_cmd.run("cargo clippy …")?; if !clippy_success { return Ok(false); } if !self.test() { - return run_bin(bin_name, output.as_deref_mut(), target_dir); + return run_bin(bin_name, output.as_deref_mut(), cmd_runner); } - let test_success = CargoCmd { - subcommand: "test", - args: &["--", "--color", "always", "--show-output"], - bin_name, - description: "cargo test …", - // Hide warnings because they are shown by Clippy. - hide_warnings: true, - target_dir, - output: output.as_deref_mut(), - dev, + let mut test_cmd = cmd_runner.cargo("test", bin_name, output.as_deref_mut()); + if !output_is_none { + test_cmd.args(["--", "--color", "always", "--show-output"]); } - .run()?; + // Hide warnings because they are shown by Clippy. + test_cmd.hide_warnings(); + let test_success = test_cmd.run("cargo test …")?; - let run_success = run_bin(bin_name, output.as_deref_mut(), target_dir)?; + let run_success = run_bin(bin_name, output, cmd_runner)?; Ok(test_success && run_success) } @@ -164,19 +133,19 @@ pub trait RunnableExercise { /// Compile, check and run the exercise. /// The output is written to the `output` buffer after clearing it. #[inline] - fn run_exercise(&self, output: Option<&mut Vec>, target_dir: &Path) -> Result { - self.run(self.name(), output, target_dir) + fn run_exercise(&self, output: Option<&mut Vec>, cmd_runner: &CmdRunner) -> Result { + self.run(self.name(), output, cmd_runner) } /// Compile, check and run the exercise's solution. /// The output is written to the `output` buffer after clearing it. - fn run_solution(&self, output: Option<&mut Vec>, target_dir: &Path) -> Result { + fn run_solution(&self, output: Option<&mut Vec>, cmd_runner: &CmdRunner) -> Result { let name = self.name(); let mut bin_name = String::with_capacity(name.len() + 4); bin_name.push_str(name); bin_name.push_str("_sol"); - self.run(&bin_name, output, target_dir) + self.run(&bin_name, output, cmd_runner) } } diff --git a/src/main.rs b/src/main.rs index 658d551d12..1f0afdec44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,22 +24,6 @@ mod terminal_link; mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; -const DEBUG_PROFILE: bool = { - #[allow(unused_assignments, unused_mut)] - let mut debug_profile = false; - - #[cfg(debug_assertions)] - { - debug_profile = true; - } - - debug_profile -}; - -// The current directory is the official Rustligns repository. -fn in_official_repo() -> bool { - Path::new("dev/rustlings-repo.txt").exists() -} fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { stdout.write_all(b"\x1b[H\x1b[2J\x1b[3J") @@ -89,7 +73,7 @@ enum Subcommands { fn main() -> Result<()> { let args = Args::parse(); - if !DEBUG_PROFILE && in_official_repo() { + if cfg!(not(debug_assertions)) && Path::new("dev/rustlings-repo.txt").exists() { bail!("{OLD_METHOD_ERR}"); } diff --git a/src/run.rs b/src/run.rs index 606f0a4340..964e13b892 100644 --- a/src/run.rs +++ b/src/run.rs @@ -11,7 +11,7 @@ use crate::{ pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - let success = exercise.run_exercise(Some(&mut output), app_state.target_dir())?; + let success = exercise.run_exercise(Some(&mut output), app_state.cmd_runner())?; let mut stdout = io::stdout().lock(); stdout.write_all(&output)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 8f01db7d9b..46f48d9fab 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -54,7 +54,7 @@ impl<'a> WatchState<'a> { let success = self .app_state .current_exercise() - .run_exercise(Some(&mut self.output), self.app_state.target_dir())?; + .run_exercise(Some(&mut self.output), self.app_state.cmd_runner())?; if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { From e0f0944bffe607af9e6059df7a65d4b9b0b99e4f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 15:53:32 +0200 Subject: [PATCH 1121/1432] Refactor check_solutions --- src/dev/check.rs | 99 ++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index db5b21fc7c..0d1e58781e 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,13 +1,10 @@ -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Error, Result}; use std::{ cmp::Ordering, fs::{self, read_dir, OpenOptions}, io::{self, Read, Write}, path::{Path, PathBuf}, - sync::{ - atomic::{self, AtomicBool}, - Mutex, - }, + sync::atomic::{self, AtomicBool}, thread, }; @@ -213,56 +210,76 @@ fn check_exercises(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { check_exercises_unsolved(info_file, cmd_runner) } +enum SolutionCheck { + Success { sol_path: String }, + MissingRequired, + MissingOptional, + RunFailure { output: Vec }, + Err(Error), +} + fn check_solutions( require_solutions: bool, info_file: &InfoFile, cmd_runner: &CmdRunner, ) -> Result<()> { - let paths = Mutex::new(hashbrown::HashSet::with_capacity(info_file.exercises.len())); - let error_occurred = AtomicBool::new(false); - println!("Running all solutions. This may take a while…\n"); - thread::scope(|s| { - for exercise_info in &info_file.exercises { - s.spawn(|| { - let error = |e| { - let mut stderr = io::stderr().lock(); - stderr.write_all(e).unwrap(); - stderr - .write_all(b"\nFailed to run the solution of the exercise ") - .unwrap(); - stderr.write_all(exercise_info.name.as_bytes()).unwrap(); - stderr.write_all(SEPARATOR).unwrap(); - error_occurred.store(true, atomic::Ordering::Relaxed); - }; + let sol_paths = thread::scope(|s| { + let handles = info_file + .exercises + .iter() + .map(|exercise_info| { + s.spawn(|| { + let sol_path = exercise_info.sol_path(); + if !Path::new(&sol_path).exists() { + if require_solutions { + return SolutionCheck::MissingRequired; + } + + return SolutionCheck::MissingOptional; + } - let path = exercise_info.sol_path(); - if !Path::new(&path).exists() { - if require_solutions { - error(b"Solution missing"); + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + match exercise_info.run_solution(Some(&mut output), cmd_runner) { + Ok(true) => SolutionCheck::Success { sol_path }, + Ok(false) => SolutionCheck::RunFailure { output }, + Err(e) => SolutionCheck::Err(e), } + }) + }) + .collect::>(); - // No solution to check. - return; - } + let mut sol_paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); - let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - match exercise_info.run_solution(Some(&mut output), cmd_runner) { - Ok(true) => { - paths.lock().unwrap().insert(PathBuf::from(path)); - } - Ok(false) => error(&output), - Err(e) => error(e.to_string().as_bytes()), + for (exercise_name, handle) in info_file + .exercises + .iter() + .map(|exercise_info| &exercise_info.name) + .zip(handles) + { + match handle.join() { + Ok(SolutionCheck::Success { sol_path }) => { + sol_paths.insert(PathBuf::from(sol_path)); } - }); + Ok(SolutionCheck::MissingRequired) => { + bail!("The solution of the exercise {exercise_name} is missing"); + } + Ok(SolutionCheck::MissingOptional) => (), + Ok(SolutionCheck::RunFailure { output }) => { + io::stderr().lock().write_all(&output)?; + bail!("Running the solution of the exercise {exercise_name} failed with the error above"); + } + Ok(SolutionCheck::Err(e)) => return Err(e), + Err(_) => { + bail!("Panic while trying to run the solution of the exericse {exercise_name}"); + } + } } - }); - if error_occurred.load(atomic::Ordering::Relaxed) { - bail!("At least one solution failed. See the output above."); - } + Ok(sol_paths) + })?; - check_unexpected_files("solutions", &paths.into_inner().unwrap())?; + check_unexpected_files("solutions", &sol_paths)?; Ok(()) } From 65a8f6bb4b542a24bf80265cf5080b5c8f51fb7e Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 19:14:09 +0200 Subject: [PATCH 1122/1432] Run rustfmt on solutions in `dev check` --- .github/workflows/rust.yml | 2 -- CHANGELOG.md | 1 + Cargo.toml | 2 +- src/dev/check.rs | 26 +++++++++++++++++++++----- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5c423e2467..80f052d611 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,8 +24,6 @@ jobs: globs: "exercises/**/*.md" - name: Run cargo fmt run: cargo fmt --all --check - - name: Run rustfmt on solutions - run: rustfmt --check --edition 2021 --color always solutions/**/*.rs test: runs-on: ${{ matrix.os }} strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 01a2fb2420..846c67f06b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Run the final check of all exercises in parallel. - Small exercise improvements. +- `dev check`: Check that all solutions are formatted with `rustfmt`. diff --git a/Cargo.toml b/Cargo.toml index ad9164d840..d4466ce26d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ authors = [ ] repository = "https://github.com/rust-lang/rustlings" license = "MIT" -edition = "2021" +edition = "2021" # On Update: Update the edition of the `rustfmt` command that checks the solutions. [workspace.dependencies] serde = { version = "1.0.204", features = ["derive"] } diff --git a/src/dev/check.rs b/src/dev/check.rs index 0d1e58781e..cf1d976095 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -4,6 +4,7 @@ use std::{ fs::{self, read_dir, OpenOptions}, io::{self, Read, Write}, path::{Path, PathBuf}, + process::{Command, Stdio}, sync::atomic::{self, AtomicBool}, thread, }; @@ -224,7 +225,7 @@ fn check_solutions( cmd_runner: &CmdRunner, ) -> Result<()> { println!("Running all solutions. This may take a while…\n"); - let sol_paths = thread::scope(|s| { + thread::scope(|s| { let handles = info_file .exercises .iter() @@ -250,6 +251,14 @@ fn check_solutions( .collect::>(); let mut sol_paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); + let mut fmt_cmd = Command::new("rustfmt"); + fmt_cmd + .arg("--check") + .arg("--edition") + .arg("2021") + .arg("--color") + .arg("--always") + .stdin(Stdio::null()); for (exercise_name, handle) in info_file .exercises @@ -259,6 +268,7 @@ fn check_solutions( { match handle.join() { Ok(SolutionCheck::Success { sol_path }) => { + fmt_cmd.arg(&sol_path); sol_paths.insert(PathBuf::from(sol_path)); } Ok(SolutionCheck::MissingRequired) => { @@ -276,12 +286,18 @@ fn check_solutions( } } - Ok(sol_paths) - })?; + let handle = s.spawn(move || check_unexpected_files("solutions", &sol_paths)); - check_unexpected_files("solutions", &sol_paths)?; + if !fmt_cmd + .status() + .context("Failed to run `rustfmt` on all solution files")? + .success() + { + bail!("Some solutions aren't formatted. Run `rustfmt` on them"); + } - Ok(()) + handle.join().unwrap() + }) } pub fn check(require_solutions: bool) -> Result<()> { From 3fc462f90fec10cfacd3d81e944b11bb776a2941 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 19:17:40 +0200 Subject: [PATCH 1123/1432] Fix tests --- src/dev/check.rs | 17 +++++++---------- src/dev/update.rs | 23 +++++++++-------------- tests/test_exercises/Cargo.toml | 11 ----------- tests/test_exercises/dev/Cargo.toml | 11 +++++++++++ 4 files changed, 27 insertions(+), 35 deletions(-) delete mode 100644 tests/test_exercises/Cargo.toml create mode 100644 tests/test_exercises/dev/Cargo.toml diff --git a/src/dev/check.rs b/src/dev/check.rs index cf1d976095..0b243b2f44 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -25,10 +25,13 @@ fn forbidden_char(input: &str) -> Option { // Check that the Cargo.toml file is up-to-date. fn check_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 current_cargo_toml = fs::read_to_string(cargo_toml_path) + .with_context(|| format!("Failed to read the file `{cargo_toml_path}`"))?; + + let (bins_start_ind, bins_end_ind) = bins_start_end_ind(¤t_cargo_toml)?; let old_bins = ¤t_cargo_toml.as_bytes()[bins_start_ind..bins_end_ind]; let mut new_bins = Vec::with_capacity(BINS_BUFFER_CAPACITY); @@ -305,15 +308,9 @@ pub fn check(require_solutions: bool) -> Result<()> { if cfg!(debug_assertions) { // A hack to make `cargo run -- dev check` work when developing Rustlings. - check_cargo_toml( - &info_file.exercises, - include_str!("../../dev-Cargo.toml"), - b"../", - )?; + check_cargo_toml(&info_file.exercises, "dev/Cargo.toml", b"../")?; } 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, ¤t_cargo_toml, b"")?; + check_cargo_toml(&info_file.exercises, "Cargo.toml", b"")?; } let cmd_runner = CmdRunner::build()?; diff --git a/src/dev/update.rs b/src/dev/update.rs index 680d302f89..6de3c8f79b 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -9,12 +9,14 @@ use crate::{ // Update the `Cargo.toml` file. fn update_cargo_toml( exercise_infos: &[ExerciseInfo], - current_cargo_toml: &str, - exercise_path_prefix: &[u8], cargo_toml_path: &str, + exercise_path_prefix: &[u8], ) -> Result<()> { + let current_cargo_toml = fs::read_to_string(cargo_toml_path) + .with_context(|| format!("Failed to read the file `{cargo_toml_path}`"))?; + let updated_cargo_toml = - updated_cargo_toml(exercise_infos, current_cargo_toml, exercise_path_prefix)?; + updated_cargo_toml(exercise_infos, ¤t_cargo_toml, exercise_path_prefix)?; fs::write(cargo_toml_path, updated_cargo_toml) .context("Failed to write the `Cargo.toml` file")?; @@ -25,21 +27,14 @@ fn update_cargo_toml( pub fn update() -> Result<()> { let info_file = InfoFile::parse()?; - // A hack to make `cargo run -- dev update` work when developing Rustlings. if cfg!(debug_assertions) { - update_cargo_toml( - &info_file.exercises, - include_str!("../../dev-Cargo.toml"), - b"../", - "dev/Cargo.toml", - ) - .context("Failed to update the file `dev/Cargo.toml`")?; + // A hack to make `cargo run -- dev update` work when developing Rustlings. + update_cargo_toml(&info_file.exercises, "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, ¤t_cargo_toml, b"", "Cargo.toml") + update_cargo_toml(&info_file.exercises, "Cargo.toml", &[]) .context("Failed to update the file `Cargo.toml`")?; println!("Updated `Cargo.toml`"); diff --git a/tests/test_exercises/Cargo.toml b/tests/test_exercises/Cargo.toml deleted file mode 100644 index 6b81751802..0000000000 --- a/tests/test_exercises/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -bin = [ - { name = "compilation_success", path = "exercises/compilation_success.rs" }, - { name = "compilation_failure", path = "exercises/compilation_failure.rs" }, - { name = "test_success", path = "exercises/test_success.rs" }, - { name = "test_failure", path = "exercises/test_failure.rs" }, -] - -[package] -name = "test_exercises" -edition = "2021" -publish = false diff --git a/tests/test_exercises/dev/Cargo.toml b/tests/test_exercises/dev/Cargo.toml new file mode 100644 index 0000000000..01fe7c10d0 --- /dev/null +++ b/tests/test_exercises/dev/Cargo.toml @@ -0,0 +1,11 @@ +bin = [ + { name = "compilation_success", path = "../exercises/compilation_success.rs" }, + { name = "compilation_failure", path = "../exercises/compilation_failure.rs" }, + { name = "test_success", path = "../exercises/test_success.rs" }, + { name = "test_failure", path = "../exercises/test_failure.rs" }, +] + +[package] +name = "test_exercises" +edition = "2021" +publish = false From 700a065abd4d9536ca8f12fa18975025fc2bc1ac Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 19:19:14 +0200 Subject: [PATCH 1124/1432] Fix rustfmt option --- src/dev/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 0b243b2f44..c89eb35d79 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -260,7 +260,7 @@ fn check_solutions( .arg("--edition") .arg("2021") .arg("--color") - .arg("--always") + .arg("always") .stdin(Stdio::null()); for (exercise_name, handle) in info_file From d1ff4b5cf069226d0852a2d999f71653897fd0e1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 1 Aug 2024 19:19:25 +0200 Subject: [PATCH 1125/1432] Remove newline --- src/dev/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index c89eb35d79..3f6b44073f 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -317,7 +317,7 @@ pub fn check(require_solutions: bool) -> Result<()> { check_exercises(&info_file, &cmd_runner)?; check_solutions(require_solutions, &info_file, &cmd_runner)?; - println!("\nEverything looks fine!"); + println!("Everything looks fine!"); Ok(()) } From 14682060522371a358c2054fd2cc5cfdd1786078 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 2 Aug 2024 15:54:14 +0200 Subject: [PATCH 1126/1432] Stop on first exercise solved --- src/dev/check.rs | 110 ++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 3f6b44073f..f01374d37d 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -5,7 +5,6 @@ use std::{ io::{self, Read, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, - sync::atomic::{self, AtomicBool}, thread, }; @@ -22,7 +21,7 @@ fn forbidden_char(input: &str) -> Option { input.chars().find(|c| !c.is_alphanumeric() && *c != '_') } -// Check that the Cargo.toml file is up-to-date. +// Check that the `Cargo.toml` file is up-to-date. fn check_cargo_toml( exercise_infos: &[ExerciseInfo], cargo_toml_path: &str, @@ -164,41 +163,42 @@ fn check_unexpected_files( } fn check_exercises_unsolved(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { - let error_occurred = AtomicBool::new(false); - println!( "Running all exercises to check that they aren't already solved. This may take a while…\n", ); thread::scope(|s| { - for exercise_info in &info_file.exercises { - if exercise_info.skip_check_unsolved { - continue; - } - - s.spawn(|| { - let error = |e| { - let mut stderr = io::stderr().lock(); - stderr.write_all(e).unwrap(); - stderr.write_all(b"\nProblem with the exercise ").unwrap(); - stderr.write_all(exercise_info.name.as_bytes()).unwrap(); - stderr.write_all(SEPARATOR).unwrap(); - error_occurred.store(true, atomic::Ordering::Relaxed); - }; - - match exercise_info.run_exercise(None, cmd_runner) { - Ok(true) => error(b"Already solved!"), - Ok(false) => (), - Err(e) => error(e.to_string().as_bytes()), + let handles = info_file + .exercises + .iter() + .filter_map(|exercise_info| { + if exercise_info.skip_check_unsolved { + return None; } - }); - } - }); - if error_occurred.load(atomic::Ordering::Relaxed) { - bail!(CHECK_EXERCISES_UNSOLVED_ERR); - } + Some(s.spawn(|| exercise_info.run_exercise(None, cmd_runner))) + }) + .collect::>(); - Ok(()) + for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { + let Ok(result) = handle.join() else { + bail!( + "Panic while trying to run the exericse {}", + exercise_info.name, + ); + }; + + match result { + Ok(true) => bail!( + "The exercise {} is already solved.\n{SKIP_CHECK_UNSOLVED_HINT}", + exercise_info.name, + ), + Ok(false) => (), + Err(e) => return Err(e), + } + } + + Ok(()) + }) } fn check_exercises(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { @@ -209,9 +209,10 @@ fn check_exercises(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { } let info_file_paths = check_info_file_exercises(info_file)?; - check_unexpected_files("exercises", &info_file_paths)?; + let handle = thread::spawn(move || check_unexpected_files("exercises", &info_file_paths)); - check_exercises_unsolved(info_file, cmd_runner) + check_exercises_unsolved(info_file, cmd_runner)?; + handle.join().unwrap() } enum SolutionCheck { @@ -263,29 +264,34 @@ fn check_solutions( .arg("always") .stdin(Stdio::null()); - for (exercise_name, handle) in info_file - .exercises - .iter() - .map(|exercise_info| &exercise_info.name) - .zip(handles) - { - match handle.join() { - Ok(SolutionCheck::Success { sol_path }) => { + for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { + let Ok(check_result) = handle.join() else { + bail!( + "Panic while trying to run the solution of the exericse {}", + exercise_info.name, + ); + }; + + match check_result { + SolutionCheck::Success { sol_path } => { fmt_cmd.arg(&sol_path); sol_paths.insert(PathBuf::from(sol_path)); } - Ok(SolutionCheck::MissingRequired) => { - bail!("The solution of the exercise {exercise_name} is missing"); + SolutionCheck::MissingRequired => { + bail!( + "The solution of the exercise {} is missing", + exercise_info.name, + ); } - Ok(SolutionCheck::MissingOptional) => (), - Ok(SolutionCheck::RunFailure { output }) => { + SolutionCheck::MissingOptional => (), + SolutionCheck::RunFailure { output } => { io::stderr().lock().write_all(&output)?; - bail!("Running the solution of the exercise {exercise_name} failed with the error above"); - } - Ok(SolutionCheck::Err(e)) => return Err(e), - Err(_) => { - bail!("Panic while trying to run the solution of the exericse {exercise_name}"); + bail!( + "Running the solution of the exercise {} failed with the error above", + exercise_info.name, + ); } + SolutionCheck::Err(e) => return Err(e), } } @@ -322,8 +328,4 @@ pub fn check(require_solutions: bool) -> Result<()> { Ok(()) } -const SEPARATOR: &[u8] = - b"\n========================================================================================\n"; - -const CHECK_EXERCISES_UNSOLVED_ERR: &str = "At least one exercise is already solved or failed to run. See the output above. -If this is an intro exercise that is intended to be already solved, add `skip_check_unsolved = true` to the exercise's metadata in the `info.toml` file."; +const SKIP_CHECK_UNSOLVED_HINT: &str = "If this is an introduction exercise that is intended to be already solved, add `skip_check_unsolved = true` to the exercise's metadata in the `info.toml` file"; From 5016c7cf7c846cc4d271fa06d8d7debc7604ae5c Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 2 Aug 2024 16:28:05 +0200 Subject: [PATCH 1127/1432] Use `trim_ascii` instead of `trim` --- src/app_state.rs | 7 +++---- src/dev/check.rs | 2 +- src/exercise.rs | 2 +- src/main.rs | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index ea99746b12..8e43c57c9b 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -107,8 +107,7 @@ impl AppState { let path = exercise_info.path().leak(); let name = exercise_info.name.leak(); let dir = exercise_info.dir.map(|dir| &*dir.leak()); - - let hint = exercise_info.hint.trim().to_owned(); + let hint = exercise_info.hint.leak().trim_ascii(); Exercise { dir, @@ -397,7 +396,7 @@ impl AppState { clear_terminal(writer)?; writer.write_all(FENISH_LINE.as_bytes())?; - let final_message = self.final_message.trim(); + let final_message = self.final_message.trim_ascii(); if !final_message.is_empty() { writer.write_all(final_message.as_bytes())?; writer.write_all(b"\n")?; @@ -445,7 +444,7 @@ mod tests { path: "exercises/0.rs", test: false, strict_clippy: false, - hint: String::new(), + hint: "", done: false, } } diff --git a/src/dev/check.rs b/src/dev/check.rs index f01374d37d..e1e716c158 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -71,7 +71,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result Result<()> { let mut stdout = io::stdout().lock(); clear_terminal(&mut stdout)?; - let welcome_message = welcome_message.trim(); + let welcome_message = welcome_message.trim_ascii(); write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; stdout.flush()?; press_enter_prompt()?; From 175294fa5dda30ed313050a4837631575dc8a232 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 2 Aug 2024 16:40:06 +0200 Subject: [PATCH 1128/1432] Add `rust-version` --- CHANGELOG.md | 1 + Cargo.toml | 2 ++ README.md | 2 +- rustlings-macros/Cargo.toml | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 846c67f06b..cf036eebfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 6.1.1 (UNRELEASED) +- Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports. - Run the final check of all exercises in parallel. - Small exercise improvements. - `dev check`: Check that all solutions are formatted with `rustfmt`. diff --git a/Cargo.toml b/Cargo.toml index d4466ce26d..5895579152 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ authors = [ repository = "https://github.com/rust-lang/rustlings" license = "MIT" edition = "2021" # On Update: Update the edition of the `rustfmt` command that checks the solutions. +rust-version = "1.80" [workspace.dependencies] serde = { version = "1.0.204", features = ["derive"] } @@ -29,6 +30,7 @@ authors.workspace = true repository.workspace = true license.workspace = true edition.workspace = true +rust-version.workspace = true keywords = [ "exercise", "learning", diff --git a/README.md b/README.md index a7f81c12db..bfffad8143 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ It contains code examples and exercises similar to Rustlings, but online. ### Installing Rust -Before installing Rustlings, you need to have _Rust installed_. +Before installing Rustlings, you need to have the **latest version of Rust** installed. Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. This will also install _Cargo_, Rust's package/project manager. diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 20d6776e20..f5ecfcc266 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -6,6 +6,7 @@ authors.workspace = true repository.workspace = true license.workspace = true edition.workspace = true +rust-version.workspace = true include = [ "/src/", "/info.toml", From 2128be8b2855f2640137491cb1ed0c9d89721603 Mon Sep 17 00:00:00 2001 From: Matt Nield <64328730+matthewjnield@users.noreply.github.com> Date: Sun, 4 Aug 2024 02:36:45 -0400 Subject: [PATCH 1129/1432] chore: Fix snakecase convention in errors6.rs Exercise errors6.rs prompts the user to add a method named `from_parseint`. This commit changes the method name to the corrected snakecase format, `from_parse_int`. --- exercises/13_error_handling/errors6.rs | 2 +- solutions/13_error_handling/errors6.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index b656c61706..b1995e036a 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -25,7 +25,7 @@ impl ParsePosNonzeroError { } // TODO: Add another error conversion function here. - // fn from_parseint(???) -> Self { ??? } + // fn from_parse_int(???) -> Self { ??? } } #[derive(PartialEq, Debug)] diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs index 429d3ea319..86793619f2 100644 --- a/solutions/13_error_handling/errors6.rs +++ b/solutions/13_error_handling/errors6.rs @@ -24,7 +24,7 @@ impl ParsePosNonzeroError { Self::Creation(err) } - fn from_parseint(err: ParseIntError) -> Self { + fn from_parse_int(err: ParseIntError) -> Self { Self::ParseInt(err) } } @@ -44,7 +44,7 @@ impl PositiveNonzeroInteger { fn parse(s: &str) -> Result { // Return an appropriate error instead of panicking when `parse()` // returns an error. - let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?; + let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Self::new(x).map_err(ParsePosNonzeroError::from_creation) } From 13124aafe3fd0fcd5efad12419ea5cc5a3b8ceef Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 5 Aug 2024 03:15:43 +0200 Subject: [PATCH 1130/1432] Update deps --- Cargo.lock | 49 +++++++++++++++++++++++++++++-------------------- Cargo.toml | 8 ++++---- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fab8e58435..ac915aacee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,9 +116,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", "clap_derive", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstream", "anstyle", @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", @@ -264,9 +264,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -419,12 +419,12 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "os_pipe" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -587,9 +587,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", "memchr", @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.18" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", @@ -794,11 +794,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -825,6 +825,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -948,9 +957,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.16" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 5895579152..e76077d98c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ rust-version = "1.80" [workspace.dependencies] serde = { version = "1.0.204", features = ["derive"] } -toml_edit = { version = "0.22.18", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -47,13 +47,13 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.11", features = ["derive"] } +clap = { version = "4.5.13", features = ["derive"] } hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } -os_pipe = "1.2.0" +os_pipe = "1.2.1" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" } -serde_json = "1.0.121" +serde_json = "1.0.122" serde.workspace = true toml_edit.workspace = true From bdf4960b6a9626c83281ae2fb9cbccda676dffcf Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 7 Aug 2024 23:25:22 +0200 Subject: [PATCH 1131/1432] Fix exercise name shift in exercise check --- src/dev/check.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index e1e716c158..202e629286 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -175,22 +175,21 @@ fn check_exercises_unsolved(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Res return None; } - Some(s.spawn(|| exercise_info.run_exercise(None, cmd_runner))) + Some(( + exercise_info.name.as_str(), + s.spawn(|| exercise_info.run_exercise(None, cmd_runner)), + )) }) .collect::>(); - for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { + for (exercise_name, handle) in handles { let Ok(result) = handle.join() else { - bail!( - "Panic while trying to run the exericse {}", - exercise_info.name, - ); + bail!("Panic while trying to run the exericse {exercise_name}"); }; match result { Ok(true) => bail!( - "The exercise {} is already solved.\n{SKIP_CHECK_UNSOLVED_HINT}", - exercise_info.name, + "The exercise {exercise_name} is already solved.\n{SKIP_CHECK_UNSOLVED_HINT}", ), Ok(false) => (), Err(e) => return Err(e), From 286a455fa94bc638e6418d75634e28a78275b033 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 7 Aug 2024 23:35:50 +0200 Subject: [PATCH 1132/1432] Avoid using `RUSTFLAGS` to not trigger rebuilding, especially in rust-analyzer --- src/cmd.rs | 7 ------- src/exercise.rs | 53 +++++++++++++++++++++++-------------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/src/cmd.rs b/src/cmd.rs index 1891a283ad..ba6ec89481 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -129,13 +129,6 @@ impl<'out> CargoSubcommand<'out> { self } - /// RUSTFLAGS="-A warnings" - #[inline] - pub fn hide_warnings(&mut self) -> &mut Self { - self.cmd.env("RUSTFLAGS", "-A warnings"); - self - } - /// The boolean in the returned `Result` is true if the command's exit status is success. #[inline] pub fn run(self, description: &str) -> Result { diff --git a/src/exercise.rs b/src/exercise.rs index 7a383bb605..500d119479 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -78,27 +78,40 @@ pub trait RunnableExercise { mut output: Option<&mut Vec>, cmd_runner: &CmdRunner, ) -> Result { - let output_is_none = if let Some(output) = output.as_deref_mut() { + if let Some(output) = output.as_deref_mut() { output.clear(); - false - } else { - true - }; - - let mut build_cmd = cmd_runner.cargo("build", bin_name, output.as_deref_mut()); - if output_is_none { - build_cmd.hide_warnings(); } - let build_success = build_cmd.run("cargo build …")?; + + let build_success = cmd_runner + .cargo("build", bin_name, output.as_deref_mut()) + .run("cargo build …")?; if !build_success { return Ok(false); } - // Discard the output of `cargo build` because it will be shown again by Clippy. + // Discard the compiler output because it will be shown again by `cargo test` or Clippy. if let Some(output) = output.as_deref_mut() { output.clear(); } + if self.test() { + let output_is_some = output.is_some(); + let mut test_cmd = cmd_runner.cargo("test", bin_name, output.as_deref_mut()); + if output_is_some { + test_cmd.args(["--", "--color", "always", "--show-output"]); + } + let test_success = test_cmd.run("cargo test …")?; + if !test_success { + run_bin(bin_name, output, cmd_runner)?; + return Ok(false); + } + + // Discard the compiler output because it will be shown again by Clippy. + if let Some(output) = output.as_deref_mut() { + output.clear(); + } + } + let mut clippy_cmd = cmd_runner.cargo("clippy", bin_name, output.as_deref_mut()); // `--profile test` is required to also check code with `[cfg(test)]`. @@ -109,25 +122,9 @@ pub trait RunnableExercise { } let clippy_success = clippy_cmd.run("cargo clippy …")?; - if !clippy_success { - return Ok(false); - } - - if !self.test() { - return run_bin(bin_name, output.as_deref_mut(), cmd_runner); - } - - let mut test_cmd = cmd_runner.cargo("test", bin_name, output.as_deref_mut()); - if !output_is_none { - test_cmd.args(["--", "--color", "always", "--show-output"]); - } - // Hide warnings because they are shown by Clippy. - test_cmd.hide_warnings(); - let test_success = test_cmd.run("cargo test …")?; - let run_success = run_bin(bin_name, output, cmd_runner)?; - Ok(test_success && run_success) + Ok(clippy_success && run_success) } /// Compile, check and run the exercise. From 24aed1b14e5d2ea40029cc0ae469bcb32332c88a Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 7 Aug 2024 23:45:58 +0200 Subject: [PATCH 1133/1432] Update CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf036eebfe..155ad84333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,16 @@ -## 6.1.1 (UNRELEASED) +## 6.1.1 (2024-08-08) +It is recommended to update to this version to fix issues with the language server `rust-analyzer`. +You can update using the following two commands: + +```bash +rustup update +cargo install rustlings +``` + +- Fix `rust-analyzer` rebuilding all exercises after changing one file. - Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports. - Run the final check of all exercises in parallel. - Small exercise improvements. From 81bf0a64300a2dd1f05b71e2674cc927385df410 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 7 Aug 2024 23:46:11 +0200 Subject: [PATCH 1134/1432] Remove redundant rustfmt check for solutions --- release-hook.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/release-hook.sh b/release-hook.sh index f021f193ee..d5954ca81f 100755 --- a/release-hook.sh +++ b/release-hook.sh @@ -9,6 +9,5 @@ cargo upgrades # Similar to CI cargo clippy -- --deny warnings cargo fmt --all --check -rustfmt --check --edition 2021 solutions/**/*.rs cargo test --workspace --all-targets cargo run -- dev check --require-solutions From 4933ace50b5e3fc6512f5129280425c5ba297b72 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 7 Aug 2024 23:54:02 +0200 Subject: [PATCH 1135/1432] Add `panic = "abort"` for exercises --- dev/Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 7f3acb5194..d814ba2bd1 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -195,3 +195,9 @@ name = "exercises" edition = "2021" # Don't publish the exercises on crates.io! publish = false + +[profile.release] +panic = "abort" + +[profile.dev] +panic = "abort" From 97719fe8da82a91a1919e55e1950d0997acca574 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 00:20:04 +0200 Subject: [PATCH 1136/1432] Remove state file and solutions dir from .gitignore --- CHANGELOG.md | 5 +++-- src/dev/new.rs | 4 ++-- src/init.rs | 8 +++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 155ad84333..c014306625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ - + -## 6.1.1 (2024-08-08) +## 6.2.0 (2024-08-08) It is recommended to update to this version to fix issues with the language server `rust-analyzer`. You can update using the following two commands: @@ -12,6 +12,7 @@ cargo install rustlings - Fix `rust-analyzer` rebuilding all exercises after changing one file. - Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports. +- Remove the state file and the solutions directory from the generated `.gitignore` file. - Run the final check of all exercises in parallel. - Small exercise improvements. - `dev check`: Check that all solutions are formatted with `rustfmt`. diff --git a/src/dev/new.rs b/src/dev/new.rs index 55d5f14107..c7650465f1 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -76,8 +76,8 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { pub const GITIGNORE: &[u8] = b".rustlings-state.txt Cargo.lock -target -.vscode +target/ +.vscode/ !.vscode/extensions.json "; diff --git a/src/init.rs b/src/init.rs index bfa0ab860d..9c7d10f40b 100644 --- a/src/init.rs +++ b/src/init.rs @@ -92,11 +92,9 @@ const INIT_SOLUTION_FILE: &[u8] = b"fn main() { } "; -const GITIGNORE: &[u8] = b".rustlings-state.txt -solutions -Cargo.lock -target -.vscode +const GITIGNORE: &[u8] = b"Cargo.lock +target/ +.vscode/ "; pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; From 693bb708b2af786b942e172d3aed104c0abd252e Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 00:20:20 +0200 Subject: [PATCH 1137/1432] Add README to the solutions dir --- CHANGELOG.md | 1 + solutions/README.md | 6 ++++++ src/init.rs | 5 +++++ 3 files changed, 12 insertions(+) create mode 100644 solutions/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c014306625..4b753d284a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ cargo install rustlings - Fix `rust-analyzer` rebuilding all exercises after changing one file. - Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports. - Remove the state file and the solutions directory from the generated `.gitignore` file. +- Add a `README.md` file to the `solutions/` directory. - Run the final check of all exercises in parallel. - Small exercise improvements. - `dev check`: Check that all solutions are formatted with `rustfmt`. diff --git a/solutions/README.md b/solutions/README.md new file mode 100644 index 0000000000..6a217fa4d9 --- /dev/null +++ b/solutions/README.md @@ -0,0 +1,6 @@ +# Official Rustlings solutions + +Before you finish an exercise, its solution file will only contain an empty `main` function. +The content of this file will be automatically replaced by the actual solution once you finish the exercise. + +Note that these solution are often only _one possibility_ to solve an exercise. diff --git a/src/init.rs b/src/init.rs index 9c7d10f40b..3970bb2f16 100644 --- a/src/init.rs +++ b/src/init.rs @@ -35,6 +35,11 @@ pub fn init() -> Result<()> { .context("Failed to initialize the `rustlings/exercises` directory")?; create_dir("solutions").context("Failed to create the `solutions/` directory")?; + fs::write( + "solutions/README.md", + include_bytes!("../solutions/README.md"), + ) + .context("Failed to create the file rustlings/solutions/README.md")?; for dir in EMBEDDED_FILES.exercise_dirs { let mut dir_path = String::with_capacity(10 + dir.name.len()); dir_path.push_str("solutions/"); From 11fc3f1e56b1b248465039db4c736bb7186e4f47 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 00:35:29 +0200 Subject: [PATCH 1138/1432] Fix errors not being shown after the welcome message --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index 2d1d5455ef..12786d0118 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,6 +121,8 @@ fn main() -> Result<()> { stdout.flush()?; press_enter_prompt()?; clear_terminal(&mut stdout)?; + // Flush to be able to show errors occuring before printing a newline to stdout. + stdout.flush()?; } StateFileStatus::Read => (), } From fd97470f3551e5c068dc796f25320ebb5b93a08c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 00:42:26 +0200 Subject: [PATCH 1139/1432] Adapt type name in hint --- rustlings-macros/info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 2ecb226424..504bfd94ff 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -571,7 +571,7 @@ name = "hashmaps3" dir = "11_hashmaps" hint = """ Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of - `HashMap` to insert the default value of `Team` if a team doesn't + `HashMap` to insert the default value of `TeamScores` if a team doesn't exist in the table yet. Learn more in The Book: From 06a0f278e5a5d9235c0bf97d2334bd33c432fd02 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 01:35:47 +0200 Subject: [PATCH 1140/1432] Don't recommend the builtin VS-Code terminal because it can't clear scrollback --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index bfffad8143..91ca564cc6 100644 --- a/README.md +++ b/README.md @@ -88,8 +88,6 @@ While working with Rustlings, please use a modern terminal for the best user exp The default terminal on Linux and Mac should be sufficient. On Windows, we recommend the [Windows Terminal](https://aka.ms/terminal). -If you use VS Code, the builtin terminal should also be fine. - ## Doing exercises The exercises are sorted by topic and can be found in the subdirectory `exercises/`. From 39580381fa9cd52cf8026ad0360e077ee25d1aa0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 01:48:57 +0200 Subject: [PATCH 1141/1432] rust-analyzer problem isn't fixed :( --- CHANGELOG.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b753d284a..19c9a51649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,6 @@ ## 6.2.0 (2024-08-08) -It is recommended to update to this version to fix issues with the language server `rust-analyzer`. -You can update using the following two commands: - -```bash -rustup update -cargo install rustlings -``` - -- Fix `rust-analyzer` rebuilding all exercises after changing one file. - Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports. - Remove the state file and the solutions directory from the generated `.gitignore` file. - Add a `README.md` file to the `solutions/` directory. From 8df66f79918168617da9709c0edcfeb3ca0e53c8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 02:45:18 +0200 Subject: [PATCH 1142/1432] Allow initialization in a workspace --- src/init.rs | 71 ++++++++++++++++++++++++++++++++++++----------------- src/main.rs | 24 +++--------------- src/term.rs | 12 +++++++++ 3 files changed, 65 insertions(+), 42 deletions(-) create mode 100644 src/term.rs diff --git a/src/init.rs b/src/init.rs index 3970bb2f16..dc23cbbfc3 100644 --- a/src/init.rs +++ b/src/init.rs @@ -3,30 +3,40 @@ use ratatui::crossterm::style::Stylize; use std::{ env::set_current_dir, fs::{self, create_dir}, - io::ErrorKind, + io::{self, Write}, path::Path, process::{Command, Stdio}, }; -use crate::{cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile}; +use crate::{ + cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile, + term::press_enter_prompt, +}; pub fn init() -> Result<()> { - // Prevent initialization in a directory that contains the file `Cargo.toml`. - // This can mean that Rustlings was already initialized in this directory. - // Otherwise, this can cause problems with Cargo workspaces. - if Path::new("Cargo.toml").exists() { - bail!(CARGO_TOML_EXISTS_ERR); + let rustlings_dir = Path::new("rustlings"); + if rustlings_dir.exists() { + bail!(RUSTLINGS_DIR_ALREADY_EXISTS_ERR); } - let rustlings_path = Path::new("rustlings"); - if let Err(e) = create_dir(rustlings_path) { - if e.kind() == ErrorKind::AlreadyExists { - bail!(RUSTLINGS_DIR_ALREADY_EXISTS_ERR); + let mut stdout = io::stdout().lock(); + let mut init_git = true; + + if Path::new("Cargo.toml").exists() { + if Path::new("exercises").exists() && Path::new("solutions").exists() { + bail!(IN_INITIALIZED_DIR_ERR); } - return Err(e.into()); + + stdout.write_all(CARGO_TOML_EXISTS_PROMPT_MSG)?; + press_enter_prompt(&mut stdout)?; + init_git = false; } - set_current_dir("rustlings") + stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; + press_enter_prompt(&mut stdout)?; + + create_dir(rustlings_dir).context("Failed to create the `rustlings/` directory")?; + set_current_dir(rustlings_dir) .context("Failed to change the current directory to `rustlings/`")?; let info_file = InfoFile::parse()?; @@ -75,18 +85,21 @@ pub fn init() -> Result<()> { fs::write(".vscode/extensions.json", VS_CODE_EXTENSIONS_JSON) .context("Failed to create the file `rustlings/.vscode/extensions.json`")?; - // Ignore any Git error because Git initialization is not required. - let _ = Command::new("git") - .arg("init") - .stdin(Stdio::null()) - .stderr(Stdio::null()) - .status(); + if init_git { + // Ignore any Git error because Git initialization is not required. + let _ = Command::new("git") + .arg("init") + .stdin(Stdio::null()) + .stderr(Stdio::null()) + .status(); + } - println!( + writeln!( + stdout, "\n{}\n\n{}", "Initialization done βœ“".green(), POST_INIT_MSG.bold(), - ); + )?; Ok(()) } @@ -104,7 +117,7 @@ target/ pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; -const CARGO_TOML_EXISTS_ERR: &str = "The current directory contains the file `Cargo.toml`. +const IN_INITIALIZED_DIR_ERR: &str = "It looks like Rustlings is already initialized in this directory. If you already initialized Rustlings, run the command `rustlings` for instructions on getting started with the exercises. Otherwise, please run `rustlings init` again in another directory."; @@ -115,5 +128,19 @@ You probably already initialized Rustlings. Run `cd rustlings` Then run `rustlings` again"; +const CARGO_TOML_EXISTS_PROMPT_MSG: &[u8] = br#"You are about to initialize Rustlings in a directory that already contains a `Cargo.toml` file! + + => It is recommended to abort with CTRL+C and initialize Rustlings in another directory <= + +If you know what you are doing and want to initialize Rustlings in a Cargo workspace, +then you need to add its directory to `members` in the `workspace` section of the `Cargo.toml` file: + +```toml +[workspace] +members = ["rustlings"] +``` + +Press ENTER if you are sure that you want to continue after reading the warning above "#; + const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory. Then run `rustlings` to get started."; diff --git a/src/main.rs b/src/main.rs index 12786d0118..edb3e1466e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,11 @@ use anyhow::{bail, Context, Result}; use app_state::StateFileStatus; use clap::{Parser, Subcommand}; use std::{ - io::{self, BufRead, IsTerminal, StdoutLock, Write}, + io::{self, IsTerminal, Write}, path::Path, process::exit, }; +use term::{clear_terminal, press_enter_prompt}; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; @@ -20,20 +21,12 @@ mod init; mod list; mod progress_bar; mod run; +mod term; mod terminal_link; mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; -fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { - stdout.write_all(b"\x1b[H\x1b[2J\x1b[3J") -} - -fn press_enter_prompt() -> io::Result<()> { - io::stdin().lock().read_until(b'\n', &mut Vec::new())?; - Ok(()) -} - /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -79,14 +72,6 @@ fn main() -> Result<()> { match args.command { Some(Subcommands::Init) => { - { - let mut stdout = io::stdout().lock(); - stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; - stdout.flush()?; - press_enter_prompt()?; - stdout.write_all(b"\n")?; - } - return init::init().context("Initialization failed"); } Some(Subcommands::Dev(dev_command)) => return dev_command.run(), @@ -118,8 +103,7 @@ fn main() -> Result<()> { let welcome_message = welcome_message.trim_ascii(); write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; - stdout.flush()?; - press_enter_prompt()?; + press_enter_prompt(&mut stdout)?; clear_terminal(&mut stdout)?; // Flush to be able to show errors occuring before printing a newline to stdout. stdout.flush()?; diff --git a/src/term.rs b/src/term.rs new file mode 100644 index 0000000000..e1ac3da95b --- /dev/null +++ b/src/term.rs @@ -0,0 +1,12 @@ +use std::io::{self, BufRead, StdoutLock, Write}; + +pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { + stdout.write_all(b"\x1b[H\x1b[2J\x1b[3J") +} + +pub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> { + stdout.flush()?; + io::stdin().lock().read_until(b'\n', &mut Vec::new())?; + stdout.write_all(b"\n")?; + Ok(()) +} From dc0ffbe16eb5ecc591422fe225ebb58f17b0e231 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Thu, 8 Aug 2024 01:23:58 +0200 Subject: [PATCH 1143/1432] Replace hashbrown with ahash hashbrown is already used in the standard library, but we want the improved performance of the different hash algorithm. Using ahash directly conveys this intent more clearly. --- Cargo.lock | 14 +++++++++++++- Cargo.toml | 2 +- src/app_state.rs | 3 ++- src/dev/check.rs | 14 ++++++-------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac915aacee..22aa252828 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -246,6 +247,17 @@ dependencies = [ "libc", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -517,9 +529,9 @@ dependencies = [ name = "rustlings" version = "6.1.0" dependencies = [ + "ahash", "anyhow", "clap", - "hashbrown", "notify-debouncer-mini", "os_pipe", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index e76077d98c..47e153019a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,9 +46,9 @@ include = [ ] [dependencies] +ahash = "0.8.11" anyhow = "1.0.86" clap = { version = "4.5.13", features = ["derive"] } -hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } diff --git a/src/app_state.rs b/src/app_state.rs index 8e43c57c9b..ac45bfc6f6 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,3 +1,4 @@ +use ahash::{HashSet, HashSetExt}; use anyhow::{bail, Context, Error, Result}; use std::{ fs::{self, File}, @@ -69,7 +70,7 @@ impl AppState { return StateFileStatus::NotRead; } - let mut done_exercises = hashbrown::HashSet::with_capacity(self.exercises.len()); + let mut done_exercises = HashSet::with_capacity(self.exercises.len()); for done_exerise_name in lines { if done_exerise_name.is_empty() { diff --git a/src/dev/check.rs b/src/dev/check.rs index 202e629286..7b17274946 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,3 +1,4 @@ +use ahash::{HashSet, HashSetExt}; use anyhow::{anyhow, bail, Context, Error, Result}; use std::{ cmp::Ordering, @@ -48,9 +49,9 @@ fn check_cargo_toml( } // Check the info of all exercises and return their paths in a set. -fn check_info_file_exercises(info_file: &InfoFile) -> Result> { - let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len()); - let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); +fn check_info_file_exercises(info_file: &InfoFile) -> Result> { + let mut names = HashSet::with_capacity(info_file.exercises.len()); + let mut paths = HashSet::with_capacity(info_file.exercises.len()); let mut file_buf = String::with_capacity(1 << 14); for exercise_info in &info_file.exercises { @@ -111,10 +112,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result, -) -> Result<()> { +fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet) -> Result<()> { let unexpected_file = |path: &Path| { anyhow!("Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory", path.display()) }; @@ -253,7 +251,7 @@ fn check_solutions( }) .collect::>(); - let mut sol_paths = hashbrown::HashSet::with_capacity(info_file.exercises.len()); + let mut sol_paths = HashSet::with_capacity(info_file.exercises.len()); let mut fmt_cmd = Command::new("rustfmt"); fmt_cmd .arg("--check") From dc086c6bf1e678a1886e0a2bb78627fac076402d Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Thu, 8 Aug 2024 12:51:27 +0200 Subject: [PATCH 1144/1432] Improve initialization in workspace - Detect if we are in a cargo project more reliably. (e.g. if `rustlings init` is run in the `src/` directory) - Refuse to initialize rustlings in a non-workspace cargo project. - Automatically populate the `workspace.members` field if `rustlings init` is run in a workspace. This may be considered risky, as there is no guarantee that's what the user wanted to do. However, it is consistent with the behavior of `cargo new`. Also, newcomers to Rust are unlikely to accidentally be in a cargo workspace, as they won't know how to create one in the first place. The use case for initialization in a workspace is when a workshop organizer wants to use rustlings alongside other exerices and provide a single repository with everything in one place. --- src/init.rs | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/init.rs b/src/init.rs index dc23cbbfc3..94551c4c69 100644 --- a/src/init.rs +++ b/src/init.rs @@ -4,7 +4,7 @@ use std::{ env::set_current_dir, fs::{self, create_dir}, io::{self, Write}, - path::Path, + path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -22,14 +22,27 @@ pub fn init() -> Result<()> { let mut stdout = io::stdout().lock(); let mut init_git = true; - if Path::new("Cargo.toml").exists() { + let manifest_path = Command::new("cargo") + .args(["locate-project", "--message-format=plain"]) + .output()?; + if manifest_path.status.success() { + let manifest_path: PathBuf = String::from_utf8_lossy(&manifest_path.stdout).trim().into(); + if Path::new("exercises").exists() && Path::new("solutions").exists() { bail!(IN_INITIALIZED_DIR_ERR); } - - stdout.write_all(CARGO_TOML_EXISTS_PROMPT_MSG)?; - press_enter_prompt(&mut stdout)?; - init_git = false; + if fs::read_to_string(manifest_path)?.contains("[workspace]") { + // make sure "rustlings" is added to `workspace.members` by making + // cargo initialize a new project + let output = Command::new("cargo").args(["new", "rustlings"]).output()?; + if !output.status.success() { + bail!("Failed to initilize new workspace member"); + } + fs::remove_dir_all("rustlings")?; + init_git = false; + } else { + bail!(IN_NON_WORKSPACE_CARGO_PROJECT_ERR); + } } stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; @@ -128,19 +141,9 @@ You probably already initialized Rustlings. Run `cd rustlings` Then run `rustlings` again"; -const CARGO_TOML_EXISTS_PROMPT_MSG: &[u8] = br#"You are about to initialize Rustlings in a directory that already contains a `Cargo.toml` file! - - => It is recommended to abort with CTRL+C and initialize Rustlings in another directory <= - -If you know what you are doing and want to initialize Rustlings in a Cargo workspace, -then you need to add its directory to `members` in the `workspace` section of the `Cargo.toml` file: - -```toml -[workspace] -members = ["rustlings"] -``` - -Press ENTER if you are sure that you want to continue after reading the warning above "#; +const IN_NON_WORKSPACE_CARGO_PROJECT_ERR: &str = "\ +The current directory is already part of a cargo project. +Please initialize rustlings in a different directory."; const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory. Then run `rustlings` to get started."; From 8b43d7925761edcd6ca8bacf382e82a05aa5c0e7 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Thu, 8 Aug 2024 14:04:43 +0200 Subject: [PATCH 1145/1432] Fix integration tests --- Cargo.lock | 49 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +++ tests/integration_tests.rs | 14 +++++------ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac915aacee..e61702567f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,6 +225,22 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "filetime" version = "0.2.23" @@ -339,6 +355,12 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -513,6 +535,19 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustlings" version = "6.1.0" @@ -526,6 +561,7 @@ dependencies = [ "rustlings-macros", "serde", "serde_json", + "tempfile", "toml_edit", ] @@ -697,6 +733,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "toml_datetime" version = "0.6.8" diff --git a/Cargo.toml b/Cargo.toml index e76077d98c..316879efe8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,9 @@ serde_json = "1.0.122" serde.workspace = true toml_edit.workspace = true +[dev-dependencies] +tempfile = "3.12.0" + [profile.release] panic = "abort" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 3ab54f9722..d821e20a0e 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -155,28 +155,28 @@ fn hint() { #[test] fn init() { - let _ = fs::remove_dir_all("tests/rustlings"); + let test_dir = tempfile::TempDir::new().unwrap(); + let initialized_dir = test_dir.path().join("rustlings"); + let test_dir = test_dir.path().to_str().unwrap(); - Cmd::default().current_dir("tests").fail(); + Cmd::default().current_dir(test_dir).fail(); Cmd::default() - .current_dir("tests") + .current_dir(test_dir) .args(&["init"]) .success(); // Running `init` after a successful initialization. Cmd::default() - .current_dir("tests") + .current_dir(test_dir) .args(&["init"]) .output(PartialStderr("`cd rustlings`")) .fail(); // Running `init` in the initialized directory. Cmd::default() - .current_dir("tests/rustlings") + .current_dir(initialized_dir.to_str().unwrap()) .args(&["init"]) .output(PartialStderr("already initialized")) .fail(); - - fs::remove_dir_all("tests/rustlings").unwrap(); } From 34f02cf83d155fc5efee9970994d3a83ab58c284 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 22:37:56 +0200 Subject: [PATCH 1146/1432] Attach error message as context --- src/watch.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 88a1230155..c669030494 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -102,8 +102,7 @@ pub fn watch( watch_state.render()?; } WatchEvent::NotifyErr(e) => { - watch_state.into_writer().write_all(NOTIFY_ERR.as_bytes())?; - return Err(Error::from(e)); + return Err(Error::from(e).context(NOTIFY_ERR)); } WatchEvent::TerminalEventErr(e) => { return Err(Error::from(e).context("Terminal event listener failed")); From 0785b2419277bc1cbc7f55c123f8e248759f4766 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 22:41:41 +0200 Subject: [PATCH 1147/1432] Show a message before running the exercise --- src/watch/state.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/watch/state.rs b/src/watch/state.rs index 46f48d9fab..45fbd9e0c7 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -51,6 +51,8 @@ impl<'a> WatchState<'a> { pub fn run_current_exercise(&mut self) -> Result<()> { self.show_hint = false; + self.writer + .write_all(b"\nChecking the exercise, please wait...")?; let success = self .app_state .current_exercise() From 4ce8667b9d878dc48fafb665699a5fc71c190972 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 22:48:53 +0200 Subject: [PATCH 1148/1432] Show the exercise name in the waiting message --- src/watch/state.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index 45fbd9e0c7..abfff7ac21 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -51,8 +51,11 @@ impl<'a> WatchState<'a> { pub fn run_current_exercise(&mut self) -> Result<()> { self.show_hint = false; - self.writer - .write_all(b"\nChecking the exercise, please wait...")?; + writeln!( + self.writer, + "\nChecking the exercise `{}`. Please wait…", + self.app_state.current_exercise().name, + )?; let success = self .app_state .current_exercise() From 1b9faa4d61665074fe450277644974dd0167e6e9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 23:13:49 +0200 Subject: [PATCH 1149/1432] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19c9a51649..1529ba005a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 6.2.0 (2024-08-08) +- Show a message before checking and running an exercise. This gives the user instant feedback and avoids confusion if the checks take too long. - Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports. - Remove the state file and the solutions directory from the generated `.gitignore` file. - Add a `README.md` file to the `solutions/` directory. From e41c3a7c925387ca2c2441b4f41c963b95bc828d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 8 Aug 2024 23:46:21 +0200 Subject: [PATCH 1150/1432] Use fixed seeds with ahash --- Cargo.lock | 12 ------------ Cargo.toml | 2 +- src/app_state.rs | 4 ++-- src/collections.rs | 10 ++++++++++ src/dev/check.rs | 8 ++++---- src/main.rs | 1 + 6 files changed, 18 insertions(+), 19 deletions(-) create mode 100644 src/collections.rs diff --git a/Cargo.lock b/Cargo.lock index 86d35c5d9a..a61ba59761 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", "once_cell", "version_check", "zerocopy", @@ -263,17 +262,6 @@ dependencies = [ "libc", ] -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hashbrown" version = "0.14.5" diff --git a/Cargo.toml b/Cargo.toml index 456f738d3e..2cd2ebade0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ include = [ ] [dependencies] -ahash = "0.8.11" +ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.86" clap = { version = "4.5.13", features = ["derive"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } diff --git a/src/app_state.rs b/src/app_state.rs index ac45bfc6f6..b72469c4df 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,4 +1,3 @@ -use ahash::{HashSet, HashSetExt}; use anyhow::{bail, Context, Error, Result}; use std::{ fs::{self, File}, @@ -11,6 +10,7 @@ use std::{ use crate::{ clear_terminal, cmd::CmdRunner, + collections::hash_set_with_capacity, embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, @@ -70,7 +70,7 @@ impl AppState { return StateFileStatus::NotRead; } - let mut done_exercises = HashSet::with_capacity(self.exercises.len()); + let mut done_exercises = hash_set_with_capacity(self.exercises.len()); for done_exerise_name in lines { if done_exerise_name.is_empty() { diff --git a/src/collections.rs b/src/collections.rs new file mode 100644 index 0000000000..fa9e3fa7c9 --- /dev/null +++ b/src/collections.rs @@ -0,0 +1,10 @@ +use ahash::AHasher; +use std::hash::BuildHasherDefault; + +/// DOS attacks aren't a concern for Rustlings. Therefore, we use `ahash` with fixed seeds. +pub type HashSet = std::collections::HashSet>; + +#[inline] +pub fn hash_set_with_capacity(capacity: usize) -> HashSet { + HashSet::with_capacity_and_hasher(capacity, BuildHasherDefault::::default()) +} diff --git a/src/dev/check.rs b/src/dev/check.rs index 7b17274946..ca1b30c9b3 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,4 +1,3 @@ -use ahash::{HashSet, HashSetExt}; use anyhow::{anyhow, bail, Context, Error, Result}; use std::{ cmp::Ordering, @@ -12,6 +11,7 @@ use std::{ use crate::{ cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY}, cmd::CmdRunner, + collections::{hash_set_with_capacity, HashSet}, exercise::{RunnableExercise, OUTPUT_CAPACITY}, info_file::{ExerciseInfo, InfoFile}, CURRENT_FORMAT_VERSION, @@ -50,8 +50,8 @@ fn check_cargo_toml( // Check the info of all exercises and return their paths in a set. fn check_info_file_exercises(info_file: &InfoFile) -> Result> { - let mut names = HashSet::with_capacity(info_file.exercises.len()); - let mut paths = HashSet::with_capacity(info_file.exercises.len()); + let mut names = hash_set_with_capacity(info_file.exercises.len()); + let mut paths = hash_set_with_capacity(info_file.exercises.len()); let mut file_buf = String::with_capacity(1 << 14); for exercise_info in &info_file.exercises { @@ -251,7 +251,7 @@ fn check_solutions( }) .collect::>(); - let mut sol_paths = HashSet::with_capacity(info_file.exercises.len()); + let mut sol_paths = hash_set_with_capacity(info_file.exercises.len()); let mut fmt_cmd = Command::new("rustfmt"); fmt_cmd .arg("--check") diff --git a/src/main.rs b/src/main.rs index edb3e1466e..58cd8ff6fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::Wa mod app_state; mod cargo_toml; mod cmd; +mod collections; mod dev; mod embedded; mod exercise; From 337460d299e59552620d4a9cc3de3a8cf067a4f8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 00:12:49 +0200 Subject: [PATCH 1151/1432] Check the status of the `cargo metadata` command --- src/cmd.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/cmd.rs b/src/cmd.rs index ba6ec89481..a10a7eaa28 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use serde::Deserialize; use std::{ io::Read, @@ -68,12 +68,16 @@ impl CmdRunner { .stdin(Stdio::null()) .stderr(Stdio::inherit()) .output() - .context(CARGO_METADATA_ERR)? - .stdout; + .context(CARGO_METADATA_ERR)?; - let target_dir = serde_json::de::from_slice::(&metadata_output) - .context("Failed to read the field `target_directory` from the `cargo metadata` output") - .map(|metadata| metadata.target_directory)?; + if !metadata_output.status.success() { + bail!("The command `cargo metadata …` failed. Are you in the `rustlings/` directory?"); + } + + let target_dir = serde_json::de::from_slice::(&metadata_output.stdout) + .context( + "Failed to read the field `target_directory` from the output of the command `cargo metadata …`", + )?.target_directory; Ok(Self { target_dir }) } From 140c4e4812eff982cc3e0c9df6fa5076d7b56633 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 00:49:30 +0200 Subject: [PATCH 1152/1432] Improve initialization in a Cargo workspace --- src/init.rs | 77 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/src/init.rs b/src/init.rs index 94551c4c69..3a0e11d2b9 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,5 +1,6 @@ use anyhow::{bail, Context, Result}; use ratatui::crossterm::style::Stylize; +use serde::Deserialize; use std::{ env::set_current_dir, fs::{self, create_dir}, @@ -13,36 +14,68 @@ use crate::{ term::press_enter_prompt, }; +#[derive(Deserialize)] +struct CargoLocateProject { + root: PathBuf, +} + pub fn init() -> Result<()> { let rustlings_dir = Path::new("rustlings"); if rustlings_dir.exists() { bail!(RUSTLINGS_DIR_ALREADY_EXISTS_ERR); } + let locate_project_output = Command::new("cargo") + .arg("locate-project") + .arg("-q") + .arg("--workspace") + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output() + .context(CARGO_LOCATE_PROJECT_ERR)?; + let mut stdout = io::stdout().lock(); let mut init_git = true; - let manifest_path = Command::new("cargo") - .args(["locate-project", "--message-format=plain"]) - .output()?; - if manifest_path.status.success() { - let manifest_path: PathBuf = String::from_utf8_lossy(&manifest_path.stdout).trim().into(); - + if locate_project_output.status.success() { if Path::new("exercises").exists() && Path::new("solutions").exists() { bail!(IN_INITIALIZED_DIR_ERR); } - if fs::read_to_string(manifest_path)?.contains("[workspace]") { - // make sure "rustlings" is added to `workspace.members` by making - // cargo initialize a new project - let output = Command::new("cargo").args(["new", "rustlings"]).output()?; - if !output.status.success() { - bail!("Failed to initilize new workspace member"); - } - fs::remove_dir_all("rustlings")?; - init_git = false; - } else { - bail!(IN_NON_WORKSPACE_CARGO_PROJECT_ERR); + + let workspace_manifest = + serde_json::de::from_slice::(&locate_project_output.stdout) + .context( + "Failed to read the field `root` from the output of `cargo locate-project …`", + )? + .root; + + let workspace_manifest_content = fs::read_to_string(&workspace_manifest) + .with_context(|| format!("Failed to read the file {}", workspace_manifest.display()))?; + if !workspace_manifest_content.contains("[workspace]\n") + && !workspace_manifest_content.contains("workspace.") + { + bail!("The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory"); } + + // Make sure "rustlings" is added to `workspace.members` by making + // Cargo initialize a new project. + let status = Command::new("cargo") + .arg("new") + .arg("-q") + .arg("--vcs") + .arg("none") + .arg("rustlings") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .status()?; + if !status.success() { + bail!("Failed to initilize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory"); + } + + stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the Cargo.toml file of this Cargo workspace.\n")?; + fs::remove_dir_all("rustlings") + .context("Failed to remove the temporary directory `rustlings/`")?; + init_git = false; } stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; @@ -117,6 +150,10 @@ pub fn init() -> Result<()> { Ok(()) } +const CARGO_LOCATE_PROJECT_ERR: &str = "Failed to run the command `cargo locate-project …` +Did you already install Rust? +Try running `cargo --version` to diagnose the problem."; + const INIT_SOLUTION_FILE: &[u8] = b"fn main() { // DON'T EDIT THIS SOLUTION FILE! // It will be automatically filled after you finish the exercise. @@ -133,7 +170,7 @@ pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.ru const IN_INITIALIZED_DIR_ERR: &str = "It looks like Rustlings is already initialized in this directory. If you already initialized Rustlings, run the command `rustlings` for instructions on getting started with the exercises. -Otherwise, please run `rustlings init` again in another directory."; +Otherwise, please run `rustlings init` again in a different directory."; const RUSTLINGS_DIR_ALREADY_EXISTS_ERR: &str = "A directory with the name `rustlings` already exists in the current directory. @@ -141,9 +178,5 @@ You probably already initialized Rustlings. Run `cd rustlings` Then run `rustlings` again"; -const IN_NON_WORKSPACE_CARGO_PROJECT_ERR: &str = "\ -The current directory is already part of a cargo project. -Please initialize rustlings in a different directory."; - const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory. Then run `rustlings` to get started."; From 479f45da9b18372d46c3f3ba7243c68f2bab09ae Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 01:05:44 +0200 Subject: [PATCH 1153/1432] test_dir is a str anyway --- tests/integration_tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index d821e20a0e..bb3a084b22 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,6 +1,5 @@ use std::{ env::{self, consts::EXE_SUFFIX}, - fs, process::{Command, Stdio}, str::from_utf8, }; @@ -156,7 +155,6 @@ fn hint() { #[test] fn init() { let test_dir = tempfile::TempDir::new().unwrap(); - let initialized_dir = test_dir.path().join("rustlings"); let test_dir = test_dir.path().to_str().unwrap(); Cmd::default().current_dir(test_dir).fail(); @@ -173,9 +171,11 @@ fn init() { .output(PartialStderr("`cd rustlings`")) .fail(); + let initialized_dir = format!("{test_dir}/rustlings"); + // Running `init` in the initialized directory. Cmd::default() - .current_dir(initialized_dir.to_str().unwrap()) + .current_dir(&initialized_dir) .args(&["init"]) .output(PartialStderr("already initialized")) .fail(); From 55e68d2c632b30733ec3d16b4039a9cd9b39823f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 01:06:27 +0200 Subject: [PATCH 1154/1432] Update deps --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a61ba59761..d3db242eab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,9 +116,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.13" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" dependencies = [ "clap_builder", "clap_derive", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" dependencies = [ "anstream", "anstyle", @@ -603,18 +603,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 2cd2ebade0..56b6f967d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.204", features = ["derive"] } +serde = { version = "1.0.205", features = ["derive"] } toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] } [package] @@ -48,7 +48,7 @@ include = [ [dependencies] ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.86" -clap = { version = "4.5.13", features = ["derive"] } +clap = { version = "4.5.14", features = ["derive"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } From f5737b5a49f6dfafbefda74df05ca1a93cdec94a Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 01:08:52 +0200 Subject: [PATCH 1155/1432] Fix typos --- src/init.rs | 2 +- src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/init.rs b/src/init.rs index 3a0e11d2b9..62a670db08 100644 --- a/src/init.rs +++ b/src/init.rs @@ -69,7 +69,7 @@ pub fn init() -> Result<()> { .stdout(Stdio::null()) .status()?; if !status.success() { - bail!("Failed to initilize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory"); + bail!("Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory"); } stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the Cargo.toml file of this Cargo workspace.\n")?; diff --git a/src/main.rs b/src/main.rs index 58cd8ff6fd..0855d4352b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,7 +106,7 @@ fn main() -> Result<()> { write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; press_enter_prompt(&mut stdout)?; clear_terminal(&mut stdout)?; - // Flush to be able to show errors occuring before printing a newline to stdout. + // Flush to be able to show errors occurring before printing a newline to stdout. stdout.flush()?; } StateFileStatus::Read => (), From 82ebd29ff603b1f62c4e17f0f0c85cfcf05e70a0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 01:14:08 +0200 Subject: [PATCH 1156/1432] Add a special confirmation for initialization in a workspace --- src/init.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/init.rs b/src/init.rs index 62a670db08..5e876d62e6 100644 --- a/src/init.rs +++ b/src/init.rs @@ -57,6 +57,9 @@ pub fn init() -> Result<()> { bail!("The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory"); } + stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\nPress ENTER to continue ")?; + press_enter_prompt(&mut stdout)?; + // Make sure "rustlings" is added to `workspace.members` by making // Cargo initialize a new project. let status = Command::new("cargo") @@ -76,11 +79,11 @@ pub fn init() -> Result<()> { fs::remove_dir_all("rustlings") .context("Failed to remove the temporary directory `rustlings/`")?; init_git = false; + } else { + stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; + press_enter_prompt(&mut stdout)?; } - stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; - press_enter_prompt(&mut stdout)?; - create_dir(rustlings_dir).context("Failed to create the `rustlings/` directory")?; set_current_dir(rustlings_dir) .context("Failed to change the current directory to `rustlings/`")?; From fc141b8dfc8326c35ad51f77aad4aef41cd62ee9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 01:16:45 +0200 Subject: [PATCH 1157/1432] Put Cargo.toml in `` --- src/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index 5e876d62e6..26fe09c5b7 100644 --- a/src/init.rs +++ b/src/init.rs @@ -75,7 +75,7 @@ pub fn init() -> Result<()> { bail!("Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory"); } - stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the Cargo.toml file of this Cargo workspace.\n")?; + stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the `Cargo.toml` file of this Cargo workspace.\n")?; fs::remove_dir_all("rustlings") .context("Failed to remove the temporary directory `rustlings/`")?; init_git = false; From 16af9817721ac1855c66f2dd67627c820b91be5f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 01:27:31 +0200 Subject: [PATCH 1158/1432] Hide stderr of `cargo locate-project` --- src/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index 26fe09c5b7..3a7ccf4d32 100644 --- a/src/init.rs +++ b/src/init.rs @@ -30,7 +30,7 @@ pub fn init() -> Result<()> { .arg("-q") .arg("--workspace") .stdin(Stdio::null()) - .stderr(Stdio::inherit()) + .stderr(Stdio::null()) .output() .context(CARGO_LOCATE_PROJECT_ERR)?; From 52a231ce2f6c3853fc11a34a3935366f27e299f4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 02:17:01 +0200 Subject: [PATCH 1159/1432] Update Ratatui --- Cargo.lock | 62 +++++++++++++++++++++++++++++++---------------- Cargo.toml | 2 +- src/list.rs | 2 +- src/list/state.rs | 2 +- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3db242eab..504113f5d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -162,13 +162,14 @@ checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "compact_str" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" dependencies = [ "castaway", "cfg-if", "itoa", + "rustversion", "ryu", "static_assertions", ] @@ -190,15 +191,15 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" -version = "0.27.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.6.0", "crossterm_winapi", - "libc", - "mio", + "mio 1.0.1", "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -278,6 +279,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "indexmap" version = "2.3.0" @@ -308,6 +315,16 @@ dependencies = [ "libc", ] +[[package]] +name = "instability" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -404,6 +421,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mio" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "notify" version = "6.1.1" @@ -418,7 +448,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio", + "mio 0.8.11", "walkdir", "windows-sys 0.48.0", ] @@ -498,18 +528,18 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" +checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303" dependencies = [ "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", + "instability", "itertools", "lru", "paste", - "stability", "strum", "strum_macros", "unicode-segmentation", @@ -659,7 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio", + "mio 1.0.1", "signal-hook", ] @@ -678,16 +708,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "stability" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "static_assertions" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 56b6f967d8..1dcc55ac4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ anyhow = "1.0.86" clap = { version = "4.5.14", features = ["derive"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" -ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" } serde_json = "1.0.122" serde.workspace = true diff --git a/src/list.rs b/src/list.rs index 15836a44c3..a246ebc098 100644 --- a/src/list.rs +++ b/src/list.rs @@ -27,7 +27,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> { let mut ui_state = UiState::new(app_state); 'outer: loop { - terminal.draw(|frame| ui_state.draw(frame).unwrap())?; + terminal.try_draw(|frame| ui_state.draw(frame).map_err(io::Error::other))?; let key = loop { match event::read()? { diff --git a/src/list/state.rs b/src/list/state.rs index d6df6344b3..7bb95ffb48 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -161,7 +161,7 @@ impl<'a> UiState<'a> { } pub fn draw(&mut self, frame: &mut Frame) -> Result<()> { - let area = frame.size(); + let area = frame.area(); frame.render_stateful_widget( &self.table, From a1d5702ba099263c42c73201d4eb44b4ad5785d5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 11:51:56 +0200 Subject: [PATCH 1160/1432] Ready for publish --- CHANGELOG.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1529ba005a..391d9c6ccb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,20 @@ -## 6.2.0 (2024-08-08) +## 6.2.0 (2024-08-09) + +### Added - Show a message before checking and running an exercise. This gives the user instant feedback and avoids confusion if the checks take too long. - Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports. -- Remove the state file and the solutions directory from the generated `.gitignore` file. - Add a `README.md` file to the `solutions/` directory. +- Allow initializing Rustlings in a Cargo workspace. +- `dev check`: Check that all solutions are formatted with `rustfmt`. + +### Changed + +- Remove the state file and the solutions directory from the generated `.gitignore` file. - Run the final check of all exercises in parallel. - Small exercise improvements. -- `dev check`: Check that all solutions are formatted with `rustfmt`. From 4472d50eba291a90017fa4c1974682c4392bf8b8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 11:52:31 +0200 Subject: [PATCH 1161/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 504113f5d7..dfda6124b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,7 +580,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.1.0" +version = "6.2.0" dependencies = [ "ahash", "anyhow", @@ -597,7 +597,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.1.0" +version = "6.2.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1dcc55ac4c..4ce639b604 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ exclude = [ ] [workspace.package] -version = "6.1.0" +version = "6.2.0" authors = [ "Mo Bitar ", # https://github.com/mo8it "Liv ", # https://github.com/shadows-withal @@ -52,7 +52,7 @@ clap = { version = "4.5.14", features = ["derive"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" } serde_json = "1.0.122" serde.workspace = true toml_edit.workspace = true From ce3dcc98560a7555386556ba13d5e901bb27e2ed Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 12:47:32 +0200 Subject: [PATCH 1162/1432] Fix typo --- solutions/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/README.md b/solutions/README.md index 6a217fa4d9..5b5176b636 100644 --- a/solutions/README.md +++ b/solutions/README.md @@ -3,4 +3,4 @@ Before you finish an exercise, its solution file will only contain an empty `main` function. The content of this file will be automatically replaced by the actual solution once you finish the exercise. -Note that these solution are often only _one possibility_ to solve an exercise. +Note that these solutions are often only _one possibility_ to solve an exercise. From ed9740b72cbea165e030507ad5212e91d834d466 Mon Sep 17 00:00:00 2001 From: Chad Dougherty Date: Thu, 15 Aug 2024 14:21:27 -0400 Subject: [PATCH 1163/1432] fix typo Similarely -> Similarly in comment --- solutions/11_hashmaps/hashmaps3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 9c58b2d38e..8a5d30b669 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -35,7 +35,7 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { team_1.goals_scored += team_1_score; team_1.goals_conceded += team_2_score; - // Similarely for the second team. + // Similarly for the second team. let team_2 = scores .entry(team_2_name) .or_insert_with(TeamScores::default); From c903db5c533b4c047bb47740deb85ebfd467bdcc Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 Aug 2024 00:15:33 +0200 Subject: [PATCH 1164/1432] Add project lints --- Cargo.toml | 14 +++++++++++++- rustlings-macros/Cargo.toml | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4ce639b604..7e353d6edf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,18 @@ panic = "abort" [package.metadata.release] pre-release-hook = ["./release-hook.sh"] +[workspace.lints.rust] +unsafe_code = "forbid" +unstable_features = "forbid" + +[workspace.lints.clippy] +empty_loop = "forbid" +infinite_loop = "deny" +mem_forget = "deny" +dbg_macro = "warn" +todo = "warn" # TODO: Remove after the following fix is released: https://github.com/rust-lang/rust-clippy/pull/13102 -[lints.clippy] needless_option_as_deref = "allow" + +[lints] +workspace = true diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index f5ecfcc266..8a85201856 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -19,3 +19,6 @@ proc-macro = true quote = "1.0.36" serde.workspace = true toml_edit.workspace = true + +[lints] +workspace = true From 0b3ad9141bc6a04d5216f8dec0163f92bcee4804 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 Aug 2024 00:24:38 +0200 Subject: [PATCH 1165/1432] Add exercise lints --- dev/Cargo.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index d814ba2bd1..7bde359c79 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -201,3 +201,19 @@ panic = "abort" [profile.dev] panic = "abort" + +[lints.rust] +# You shouldn't write unsafe code in Rustlings +unsafe_code = "forbid" +# You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust +unstable_features = "forbid" + +[lints.clippy] +# You forgot a `todo!()` +todo = "forbid" +# This can only happen by mistake in Rustlings +empty_loop = "forbid" +# No infinite loops are needed in Rustlings +infinite_loop = "deny" +# You shouldn't leak memory while still learning Rust +mem_forget = "deny" From 6ce31defb6386541e015c7905add3a6c138b35c6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 14:40:09 +0200 Subject: [PATCH 1166/1432] Ignore stdout of git init --- src/init.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index 3a7ccf4d32..95b04e9fd2 100644 --- a/src/init.rs +++ b/src/init.rs @@ -139,13 +139,14 @@ pub fn init() -> Result<()> { let _ = Command::new("git") .arg("init") .stdin(Stdio::null()) + .stdout(Stdio::null()) .stderr(Stdio::null()) .status(); } writeln!( stdout, - "\n{}\n\n{}", + "{}\n\n{}", "Initialization done βœ“".green(), POST_INIT_MSG.bold(), )?; From 8ef2ff12576ca3c3db443ad99475db3e9d656ab1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 14:54:13 +0200 Subject: [PATCH 1167/1432] Remove "Hello and" --- exercises/00_intro/intro1.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 7b8baa22e9..8bcaf683e7 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -6,8 +6,7 @@ // Try adding a new `println!` and check the updated output in the terminal. fn main() { - println!("Hello and"); - println!(r#" welcome to... "#); + println!(r#" Welcome to... "#); println!(r#" _ _ _ "#); println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#); println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#); From 8016f5ca2db2d790c1046b2c57a1add3bcf9cf64 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 14:55:58 +0200 Subject: [PATCH 1168/1432] Remove unneeded comma --- exercises/00_intro/intro1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 8bcaf683e7..172a6ab1f2 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,4 +1,4 @@ -// TODO: We sometimes encourage you to keep trying things on a given exercise, +// TODO: We sometimes encourage you to keep trying things on a given exercise // even after you already figured it out. If you got everything working and feel // ready for the next exercise, enter `n` in the terminal. // From 36f315c344a1c7cd8577d68d899a99f93d245bbd Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 14:56:52 +0200 Subject: [PATCH 1169/1432] Add "the" --- exercises/01_variables/variables1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs index 0a9e55488f..f83b44d415 100644 --- a/exercises/01_variables/variables1.rs +++ b/exercises/01_variables/variables1.rs @@ -1,5 +1,5 @@ fn main() { - // TODO: Add missing keyword. + // TODO: Add the missing keyword. x = 5; println!("x has the value {x}"); From 69b4fd49fcfad1f71df4627c6fa7f1aa505d0778 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 14:59:00 +0200 Subject: [PATCH 1170/1432] Only take a u8 to avoid huge output --- exercises/02_functions/functions3.rs | 2 +- solutions/02_functions/functions3.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs index 5d5122afa3..8d65477219 100644 --- a/exercises/02_functions/functions3.rs +++ b/exercises/02_functions/functions3.rs @@ -1,4 +1,4 @@ -fn call_me(num: u32) { +fn call_me(num: u8) { for i in 0..num { println!("Ring! Call number {}", i + 1); } diff --git a/solutions/02_functions/functions3.rs b/solutions/02_functions/functions3.rs index c581c425f7..ce5fe8eb1b 100644 --- a/solutions/02_functions/functions3.rs +++ b/solutions/02_functions/functions3.rs @@ -1,4 +1,4 @@ -fn call_me(num: u32) { +fn call_me(num: u8) { for i in 0..num { println!("Ring! Call number {}", i + 1); } From ca5d5f0a4909fbf53ed08e759a5f480648c88bb6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 15:45:02 +0200 Subject: [PATCH 1171/1432] Remove dot for copy-pasta --- exercises/13_error_handling/errors1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index ec7cb3cb66..e07fddc3cd 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -6,7 +6,7 @@ // of `Option`. fn generate_nametag_text(name: String) -> Option { if name.is_empty() { - // Empty names aren't allowed. + // Empty names aren't allowed None } else { Some(format!("Hi! My name is {name}")) From e760f0776753a9a48f9c67634aa0814ec4f64cbb Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 15:53:24 +0200 Subject: [PATCH 1172/1432] Make it clear that reset only resets one exercise --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index 7bb95ffb48..e4b28317e0 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -193,7 +193,7 @@ impl<'a> UiState<'a> { // Help footer. let mut spans = Vec::with_capacity(4); spans.push(Span::raw( - "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset β”‚ filter ", + "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚ filter ", )); match self.filter { Filter::Done => { From 2baa140615f1cca592ab6f7b9b2af192571c36ec Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 15:53:34 +0200 Subject: [PATCH 1173/1432] q only quits the list --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index e4b28317e0..b73b54e2ab 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -206,7 +206,7 @@ impl<'a> UiState<'a> { } Filter::None => spans.push(Span::raw("one/

ending")), } - spans.push(Span::raw(" β”‚ uit")); + spans.push(Span::raw(" β”‚ uit list")); Line::from(spans) } else { Line::from(self.message.as_str().light_blue()) From b678bd8ed2659d699bc2b73aee23162e9941c2d8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 16:34:43 +0200 Subject: [PATCH 1174/1432] Disable mouse in the list --- src/list.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/list.rs b/src/list.rs index a246ebc098..74ae0a26e3 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,14 +1,14 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use ratatui::{ backend::CrosstermBackend, crossterm::{ - event::{self, Event, KeyCode, KeyEventKind}, + event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, - ExecutableCommand, + QueueableCommand, }, Terminal, }; -use std::io; +use std::io::{self, Write}; use crate::app_state::AppState; @@ -18,7 +18,10 @@ mod state; pub fn list(app_state: &mut AppState) -> Result<()> { let mut stdout = io::stdout().lock(); - stdout.execute(EnterAlternateScreen)?; + stdout + .queue(EnterAlternateScreen)? + .queue(EnableMouseCapture)? + .flush()?; enable_raw_mode()?; let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; @@ -30,7 +33,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> { terminal.try_draw(|frame| ui_state.draw(frame).map_err(io::Error::other))?; let key = loop { - match event::read()? { + match event::read().context("Failed to read terminal event")? { Event::Key(key) => match key.kind { KeyEventKind::Press | KeyEventKind::Repeat => break key, KeyEventKind::Release => (), @@ -86,7 +89,10 @@ pub fn list(app_state: &mut AppState) -> Result<()> { } drop(terminal); - stdout.execute(LeaveAlternateScreen)?; + stdout + .queue(LeaveAlternateScreen)? + .queue(DisableMouseCapture)? + .flush()?; disable_raw_mode()?; Ok(()) From 3eaccbb61a730e0735ac151266e15fae79353f9d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 16:49:07 +0200 Subject: [PATCH 1175/1432] Restore the terminal after an error in the list --- src/list.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/list.rs b/src/list.rs index 74ae0a26e3..f28230e4e3 100644 --- a/src/list.rs +++ b/src/list.rs @@ -8,7 +8,7 @@ use ratatui::{ }, Terminal, }; -use std::io::{self, Write}; +use std::io::{self, StdoutLock, Write}; use crate::app_state::AppState; @@ -16,15 +16,8 @@ use self::state::{Filter, UiState}; mod state; -pub fn list(app_state: &mut AppState) -> Result<()> { - let mut stdout = io::stdout().lock(); - stdout - .queue(EnterAlternateScreen)? - .queue(EnableMouseCapture)? - .flush()?; - enable_raw_mode()?; - - let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; +fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> { + let mut terminal = Terminal::new(CrosstermBackend::new(stdout))?; terminal.clear()?; let mut ui_state = UiState::new(app_state); @@ -88,12 +81,25 @@ pub fn list(app_state: &mut AppState) -> Result<()> { } } - drop(terminal); + Ok(()) +} + +pub fn list(app_state: &mut AppState) -> Result<()> { + let mut stdout = io::stdout().lock(); + stdout + .queue(EnterAlternateScreen)? + .queue(EnableMouseCapture)? + .flush()?; + enable_raw_mode()?; + + let res = handle_list(app_state, &mut stdout); + + // Restore the terminal even if we got an error. stdout .queue(LeaveAlternateScreen)? .queue(DisableMouseCapture)? .flush()?; disable_raw_mode()?; - Ok(()) + res } From 72e557b3a9c2a802d81a56b6c08e69cda450f2c0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 16:54:44 +0200 Subject: [PATCH 1176/1432] Break help footer on narrow terminals --- src/list/state.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index b73b54e2ab..48a404f36e 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -2,11 +2,11 @@ use anyhow::{Context, Result}; use ratatui::{ layout::{Constraint, Rect}, style::{Style, Stylize}, - text::{Line, Span}, + text::{Span, Text}, widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, Frame, }; -use std::fmt::Write; +use std::{fmt::Write, mem}; use crate::{app_state::AppState, progress_bar::progress_bar_ratatui}; @@ -162,6 +162,9 @@ impl<'a> UiState<'a> { pub fn draw(&mut self, frame: &mut Frame) -> Result<()> { let area = frame.area(); + let narrow = area.width < 95; + let narrow_u16 = u16::from(narrow); + let table_height = area.height - 3 - narrow_u16; frame.render_stateful_widget( &self.table, @@ -169,7 +172,7 @@ impl<'a> UiState<'a> { x: 0, y: 0, width: area.width, - height: area.height - 3, + height: table_height, }, &mut self.table_state, ); @@ -183,7 +186,7 @@ impl<'a> UiState<'a> { .block(Block::default().borders(Borders::BOTTOM)), Rect { x: 0, - y: area.height - 3, + y: table_height, width: area.width, height: 2, }, @@ -191,10 +194,19 @@ impl<'a> UiState<'a> { let message = if self.message.is_empty() { // Help footer. + let mut text = Text::default(); let mut spans = Vec::with_capacity(4); spans.push(Span::raw( - "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚ filter ", + "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚", )); + + if narrow { + text.push_line(mem::take(&mut spans)); + spans.push(Span::raw("filter ")); + } else { + spans.push(Span::raw(" filter ")); + } + match self.filter { Filter::Done => { spans.push("one".underlined().magenta()); @@ -206,18 +218,20 @@ impl<'a> UiState<'a> { } Filter::None => spans.push(Span::raw("one/

ending")), } + spans.push(Span::raw(" β”‚ uit list")); - Line::from(spans) + text.push_line(spans); + text } else { - Line::from(self.message.as_str().light_blue()) + Text::from(self.message.as_str().light_blue()) }; frame.render_widget( message, Rect { x: 0, - y: area.height - 1, + y: table_height + 2, width: area.width, - height: 1, + height: 1 + narrow_u16, }, ); From 71f31d74bce5c657ab8a583c0a12a205e50e32cb Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 Aug 2024 16:57:58 +0200 Subject: [PATCH 1177/1432] Update deps --- Cargo.lock | 70 ++++++++++++++++++++++++++++-------------------------- Cargo.toml | 6 ++--- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dfda6124b0..8a38321f49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,9 +116,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.14" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.14" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -197,7 +197,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.6.0", "crossterm_winapi", - "mio 1.0.1", + "mio 1.0.2", "parking_lot", "rustix", "signal-hook", @@ -244,14 +244,14 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -287,9 +287,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "indexmap" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown", @@ -368,9 +368,20 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" + +[[package]] +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] [[package]] name = "linux-raw-sys" @@ -423,9 +434,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", @@ -497,7 +508,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -547,15 +558,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.3" @@ -633,18 +635,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.205" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.205" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", @@ -653,9 +655,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", "memchr", @@ -689,7 +691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio 1.0.1", + "mio 1.0.2", "signal-hook", ] @@ -744,9 +746,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7e353d6edf..a8b81eb2a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.205", features = ["derive"] } +serde = { version = "1.0.208", features = ["derive"] } toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] } [package] @@ -48,12 +48,12 @@ include = [ [dependencies] ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.86" -clap = { version = "4.5.14", features = ["derive"] } +clap = { version = "4.5.16", features = ["derive"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" } -serde_json = "1.0.122" +serde_json = "1.0.125" serde.workspace = true toml_edit.workspace = true From b70c1abd7c4dd4e7d4c95fd992a60dfcf9ce42fe Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 19 Aug 2024 23:28:53 +0200 Subject: [PATCH 1178/1432] Update deps --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a38321f49..a66f4ba312 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -368,9 +368,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.156" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libredox" @@ -746,9 +746,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", From 78a8553f1cfa7b9ae63d5d92702d68035f937041 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 19 Aug 2024 23:29:17 +0200 Subject: [PATCH 1179/1432] "Continue at" quits the list --- src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index f28230e4e3..6ff69596b8 100644 --- a/src/list.rs +++ b/src/list.rs @@ -75,7 +75,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } KeyCode::Char('c') => { ui_state.selected_to_current_exercise()?; - ui_state = ui_state.with_updated_rows(); + break; } _ => (), } From b01fddef8b3bbf7805a5f767ac30b7c84fc8630f Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 19 Aug 2024 23:52:22 +0200 Subject: [PATCH 1180/1432] Show progress of `dev check` --- src/dev/check.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index ca1b30c9b3..626a9c59d6 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -161,9 +161,9 @@ fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet) -> R } fn check_exercises_unsolved(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { - println!( - "Running all exercises to check that they aren't already solved. This may take a while…\n", - ); + let mut stdout = io::stdout().lock(); + stdout.write_all(b"Running all exercises to check that they aren't already solved...\n")?; + thread::scope(|s| { let handles = info_file .exercises @@ -180,6 +180,11 @@ fn check_exercises_unsolved(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Res }) .collect::>(); + let n_handles = handles.len(); + write!(stdout, "Progress: 0/{n_handles}")?; + stdout.flush()?; + let mut handle_num = 1; + for (exercise_name, handle) in handles { let Ok(result) = handle.join() else { bail!("Panic while trying to run the exericse {exercise_name}"); @@ -192,7 +197,12 @@ fn check_exercises_unsolved(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Res Ok(false) => (), Err(e) => return Err(e), } + + write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; + stdout.flush()?; + handle_num += 1; } + stdout.write_all(b"\n")?; Ok(()) }) @@ -225,7 +235,9 @@ fn check_solutions( info_file: &InfoFile, cmd_runner: &CmdRunner, ) -> Result<()> { - println!("Running all solutions. This may take a while…\n"); + let mut stdout = io::stdout().lock(); + stdout.write_all(b"Running all solutions...\n")?; + thread::scope(|s| { let handles = info_file .exercises @@ -261,6 +273,11 @@ fn check_solutions( .arg("always") .stdin(Stdio::null()); + let n_handles = handles.len(); + write!(stdout, "Progress: 0/{n_handles}")?; + stdout.flush()?; + let mut handle_num = 1; + for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { let Ok(check_result) = handle.join() else { bail!( @@ -282,7 +299,8 @@ fn check_solutions( } SolutionCheck::MissingOptional => (), SolutionCheck::RunFailure { output } => { - io::stderr().lock().write_all(&output)?; + stdout.write_all(b"\n\n")?; + stdout.write_all(&output)?; bail!( "Running the solution of the exercise {} failed with the error above", exercise_info.name, @@ -290,7 +308,12 @@ fn check_solutions( } SolutionCheck::Err(e) => return Err(e), } + + write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; + stdout.flush()?; + handle_num += 1; } + stdout.write_all(b"\n")?; let handle = s.spawn(move || check_unexpected_files("solutions", &sol_paths)); From 631f44331ea02abaac6f053fa9dfccb96fb8646f Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 13:08:15 +0200 Subject: [PATCH 1181/1432] Remove `--show-output` for tests and use `--format pretty` --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 500d119479..cdac2e2dc0 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -98,7 +98,7 @@ pub trait RunnableExercise { let output_is_some = output.is_some(); let mut test_cmd = cmd_runner.cargo("test", bin_name, output.as_deref_mut()); if output_is_some { - test_cmd.args(["--", "--color", "always", "--show-output"]); + test_cmd.args(["--", "--color", "always", "--format", "pretty"]); } let test_success = test_cmd.run("cargo test …")?; if !test_success { From d141a73493ede10cb5d97cdd16b08560fa44de25 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 13:35:07 +0200 Subject: [PATCH 1182/1432] threads3: Improve the test --- exercises/20_threads/threads3.rs | 14 +++++--------- solutions/20_threads/threads3.rs | 14 +++++--------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 8aa7291fd6..6d16bd9fec 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -1,7 +1,6 @@ use std::{sync::mpsc, thread, time::Duration}; struct Queue { - length: u32, first_half: Vec, second_half: Vec, } @@ -9,7 +8,6 @@ struct Queue { impl Queue { fn new() -> Self { Self { - length: 10, first_half: vec![1, 2, 3, 4, 5], second_half: vec![6, 7, 8, 9, 10], } @@ -48,17 +46,15 @@ mod tests { fn threads3() { let (tx, rx) = mpsc::channel(); let queue = Queue::new(); - let queue_length = queue.length; send_tx(queue, tx); - let mut total_received: u32 = 0; - for received in rx { - println!("Got: {received}"); - total_received += 1; + let mut received = Vec::with_capacity(10); + for value in rx { + received.push(value); } - println!("Number of received values: {total_received}"); - assert_eq!(total_received, queue_length); + received.sort(); + assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); } } diff --git a/solutions/20_threads/threads3.rs b/solutions/20_threads/threads3.rs index cd2dfbe39b..7ceefea016 100644 --- a/solutions/20_threads/threads3.rs +++ b/solutions/20_threads/threads3.rs @@ -1,7 +1,6 @@ use std::{sync::mpsc, thread, time::Duration}; struct Queue { - length: u32, first_half: Vec, second_half: Vec, } @@ -9,7 +8,6 @@ struct Queue { impl Queue { fn new() -> Self { Self { - length: 10, first_half: vec![1, 2, 3, 4, 5], second_half: vec![6, 7, 8, 9, 10], } @@ -50,17 +48,15 @@ mod tests { fn threads3() { let (tx, rx) = mpsc::channel(); let queue = Queue::new(); - let queue_length = queue.length; send_tx(queue, tx); - let mut total_received: u32 = 0; - for received in rx { - println!("Got: {received}"); - total_received += 1; + let mut received = Vec::with_capacity(10); + for value in rx { + received.push(value); } - println!("Number of received values: {total_received}"); - assert_eq!(total_received, queue_length); + received.sort(); + assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); } } From e74f2a4274e22faf050a1229b4526ceb53b55479 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 13:39:14 +0200 Subject: [PATCH 1183/1432] Check for `#[test]` with newline at the end --- src/dev/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 626a9c59d6..6cc24856b9 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -97,7 +97,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { bail!("Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user."); } - if !exercise_info.test && file_buf.contains("#[test]") { + if !exercise_info.test && file_buf.contains("#[test]\n") { bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file"); } From 27999f2d2678607538d887032e38774a35d438b8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 13:49:48 +0200 Subject: [PATCH 1184/1432] Check if exercise doesn't contain tests --- src/dev/check.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 6cc24856b9..6f16e0bfc4 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -97,7 +97,12 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { bail!("Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user."); } - if !exercise_info.test && file_buf.contains("#[test]\n") { + let contains_tests = file_buf.contains("#[test]\n"); + if exercise_info.test { + if !contains_tests { + bail!("The file `{path}` doesn't contain any tests. If you don't want to add tests to this exercise, set `test = false` for this exercise in the `info.toml` file"); + } + } else if contains_tests { bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file"); } From 5b7368c46d9369a58075000b03be7f171f230f5c Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 13:54:20 +0200 Subject: [PATCH 1185/1432] Improve error message if no exercise exists --- src/info_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info_file.rs b/src/info_file.rs index f27d018593..d4e46110bd 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -135,4 +135,4 @@ impl InfoFile { } const NO_EXERCISES_ERR: &str = "There are no exercises yet! -If you are developing third-party exercises, add at least one exercise before testing."; +Add at least one exercise before testing."; From 13cc3acdfdcff91c059f4153c694464750a67b82 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 13:56:52 +0200 Subject: [PATCH 1186/1432] Improve readability --- src/cmd.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cmd.rs b/src/cmd.rs index a10a7eaa28..4a93312adb 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -74,12 +74,14 @@ impl CmdRunner { bail!("The command `cargo metadata …` failed. Are you in the `rustlings/` directory?"); } - let target_dir = serde_json::de::from_slice::(&metadata_output.stdout) + let metadata: CargoMetadata = serde_json::de::from_slice(&metadata_output.stdout) .context( "Failed to read the field `target_directory` from the output of the command `cargo metadata …`", - )?.target_directory; + )?; - Ok(Self { target_dir }) + Ok(Self { + target_dir: metadata.target_directory, + }) } pub fn cargo<'out>( From 8854f0a5ed2a0a3cd26ba1959ee8d0ec41798143 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 14:32:47 +0200 Subject: [PATCH 1187/1432] Use anyhow! --- src/dev/check.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 6f16e0bfc4..6ad1981501 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -229,7 +229,6 @@ fn check_exercises(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { enum SolutionCheck { Success { sol_path: String }, - MissingRequired, MissingOptional, RunFailure { output: Vec }, Err(Error), @@ -252,7 +251,10 @@ fn check_solutions( let sol_path = exercise_info.sol_path(); if !Path::new(&sol_path).exists() { if require_solutions { - return SolutionCheck::MissingRequired; + return SolutionCheck::Err(anyhow!( + "The solution of the exercise {} is missing", + exercise_info.name, + )); } return SolutionCheck::MissingOptional; @@ -296,12 +298,6 @@ fn check_solutions( fmt_cmd.arg(&sol_path); sol_paths.insert(PathBuf::from(sol_path)); } - SolutionCheck::MissingRequired => { - bail!( - "The solution of the exercise {} is missing", - exercise_info.name, - ); - } SolutionCheck::MissingOptional => (), SolutionCheck::RunFailure { output } => { stdout.write_all(b"\n\n")?; From 50f6e5232e342931beec67a7edae191feadf4d75 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 14:47:08 +0200 Subject: [PATCH 1188/1432] Leak info_file and cmd_runner in `dev check` --- src/dev/check.rs | 247 ++++++++++++++++++++++++----------------------- 1 file changed, 125 insertions(+), 122 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 6ad1981501..e00d4cc1cb 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -165,65 +165,67 @@ fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet) -> R Ok(()) } -fn check_exercises_unsolved(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { +fn check_exercises_unsolved( + info_file: &'static InfoFile, + cmd_runner: &'static CmdRunner, +) -> Result<()> { let mut stdout = io::stdout().lock(); stdout.write_all(b"Running all exercises to check that they aren't already solved...\n")?; - thread::scope(|s| { - let handles = info_file - .exercises - .iter() - .filter_map(|exercise_info| { - if exercise_info.skip_check_unsolved { - return None; - } - - Some(( - exercise_info.name.as_str(), - s.spawn(|| exercise_info.run_exercise(None, cmd_runner)), - )) - }) - .collect::>(); - - let n_handles = handles.len(); - write!(stdout, "Progress: 0/{n_handles}")?; - stdout.flush()?; - let mut handle_num = 1; - - for (exercise_name, handle) in handles { - let Ok(result) = handle.join() else { - bail!("Panic while trying to run the exericse {exercise_name}"); - }; - - match result { - Ok(true) => bail!( - "The exercise {exercise_name} is already solved.\n{SKIP_CHECK_UNSOLVED_HINT}", - ), - Ok(false) => (), - Err(e) => return Err(e), + let handles = info_file + .exercises + .iter() + .filter_map(|exercise_info| { + if exercise_info.skip_check_unsolved { + return None; } - write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; - stdout.flush()?; - handle_num += 1; + Some(( + exercise_info.name.as_str(), + thread::spawn(|| exercise_info.run_exercise(None, cmd_runner)), + )) + }) + .collect::>(); + + let n_handles = handles.len(); + write!(stdout, "Progress: 0/{n_handles}")?; + stdout.flush()?; + let mut handle_num = 1; + + for (exercise_name, handle) in handles { + let Ok(result) = handle.join() else { + bail!("Panic while trying to run the exericse {exercise_name}"); + }; + + match result { + Ok(true) => { + bail!("The exercise {exercise_name} is already solved.\n{SKIP_CHECK_UNSOLVED_HINT}",) + } + Ok(false) => (), + Err(e) => return Err(e), } - stdout.write_all(b"\n")?; - Ok(()) - }) + write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; + stdout.flush()?; + handle_num += 1; + } + stdout.write_all(b"\n")?; + + Ok(()) } -fn check_exercises(info_file: &InfoFile, cmd_runner: &CmdRunner) -> Result<()> { +fn check_exercises(info_file: &'static InfoFile, cmd_runner: &'static CmdRunner) -> 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 handle = thread::spawn(move || check_exercises_unsolved(info_file, cmd_runner)); + let info_file_paths = check_info_file_exercises(info_file)?; - let handle = thread::spawn(move || check_unexpected_files("exercises", &info_file_paths)); + check_unexpected_files("exercises", &info_file_paths)?; - check_exercises_unsolved(info_file, cmd_runner)?; handle.join().unwrap() } @@ -236,98 +238,96 @@ enum SolutionCheck { fn check_solutions( require_solutions: bool, - info_file: &InfoFile, - cmd_runner: &CmdRunner, + info_file: &'static InfoFile, + cmd_runner: &'static CmdRunner, ) -> Result<()> { let mut stdout = io::stdout().lock(); stdout.write_all(b"Running all solutions...\n")?; - thread::scope(|s| { - let handles = info_file - .exercises - .iter() - .map(|exercise_info| { - s.spawn(|| { - let sol_path = exercise_info.sol_path(); - if !Path::new(&sol_path).exists() { - if require_solutions { - return SolutionCheck::Err(anyhow!( - "The solution of the exercise {} is missing", - exercise_info.name, - )); - } - - return SolutionCheck::MissingOptional; + let handles = info_file + .exercises + .iter() + .map(|exercise_info| { + thread::spawn(move || { + let sol_path = exercise_info.sol_path(); + if !Path::new(&sol_path).exists() { + if require_solutions { + return SolutionCheck::Err(anyhow!( + "The solution of the exercise {} is missing", + exercise_info.name, + )); } - let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - match exercise_info.run_solution(Some(&mut output), cmd_runner) { - Ok(true) => SolutionCheck::Success { sol_path }, - Ok(false) => SolutionCheck::RunFailure { output }, - Err(e) => SolutionCheck::Err(e), - } - }) - }) - .collect::>(); - - let mut sol_paths = hash_set_with_capacity(info_file.exercises.len()); - let mut fmt_cmd = Command::new("rustfmt"); - fmt_cmd - .arg("--check") - .arg("--edition") - .arg("2021") - .arg("--color") - .arg("always") - .stdin(Stdio::null()); - - let n_handles = handles.len(); - write!(stdout, "Progress: 0/{n_handles}")?; - stdout.flush()?; - let mut handle_num = 1; + return SolutionCheck::MissingOptional; + } - for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { - let Ok(check_result) = handle.join() else { + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + match exercise_info.run_solution(Some(&mut output), cmd_runner) { + Ok(true) => SolutionCheck::Success { sol_path }, + Ok(false) => SolutionCheck::RunFailure { output }, + Err(e) => SolutionCheck::Err(e), + } + }) + }) + .collect::>(); + + let mut sol_paths = hash_set_with_capacity(info_file.exercises.len()); + let mut fmt_cmd = Command::new("rustfmt"); + fmt_cmd + .arg("--check") + .arg("--edition") + .arg("2021") + .arg("--color") + .arg("always") + .stdin(Stdio::null()); + + let n_handles = handles.len(); + write!(stdout, "Progress: 0/{n_handles}")?; + stdout.flush()?; + let mut handle_num = 1; + + for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { + let Ok(check_result) = handle.join() else { + bail!( + "Panic while trying to run the solution of the exericse {}", + exercise_info.name, + ); + }; + + match check_result { + SolutionCheck::Success { sol_path } => { + fmt_cmd.arg(&sol_path); + sol_paths.insert(PathBuf::from(sol_path)); + } + SolutionCheck::MissingOptional => (), + SolutionCheck::RunFailure { output } => { + stdout.write_all(b"\n\n")?; + stdout.write_all(&output)?; bail!( - "Panic while trying to run the solution of the exericse {}", + "Running the solution of the exercise {} failed with the error above", exercise_info.name, ); - }; - - match check_result { - SolutionCheck::Success { sol_path } => { - fmt_cmd.arg(&sol_path); - sol_paths.insert(PathBuf::from(sol_path)); - } - SolutionCheck::MissingOptional => (), - SolutionCheck::RunFailure { output } => { - stdout.write_all(b"\n\n")?; - stdout.write_all(&output)?; - bail!( - "Running the solution of the exercise {} failed with the error above", - exercise_info.name, - ); - } - SolutionCheck::Err(e) => return Err(e), } - - write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; - stdout.flush()?; - handle_num += 1; + SolutionCheck::Err(e) => return Err(e), } - stdout.write_all(b"\n")?; - let handle = s.spawn(move || check_unexpected_files("solutions", &sol_paths)); + write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; + stdout.flush()?; + handle_num += 1; + } + stdout.write_all(b"\n")?; - if !fmt_cmd - .status() - .context("Failed to run `rustfmt` on all solution files")? - .success() - { - bail!("Some solutions aren't formatted. Run `rustfmt` on them"); - } + let handle = thread::spawn(move || check_unexpected_files("solutions", &sol_paths)); - handle.join().unwrap() - }) + if !fmt_cmd + .status() + .context("Failed to run `rustfmt` on all solution files")? + .success() + { + bail!("Some solutions aren't formatted. Run `rustfmt` on them"); + } + + handle.join().unwrap() } pub fn check(require_solutions: bool) -> Result<()> { @@ -340,9 +340,12 @@ pub fn check(require_solutions: bool) -> Result<()> { check_cargo_toml(&info_file.exercises, "Cargo.toml", b"")?; } - let cmd_runner = CmdRunner::build()?; - check_exercises(&info_file, &cmd_runner)?; - check_solutions(require_solutions, &info_file, &cmd_runner)?; + // Leaking is fine since they are used until the end of the program. + let cmd_runner = Box::leak(Box::new(CmdRunner::build()?)); + let info_file = Box::leak(Box::new(info_file)); + + check_exercises(info_file, cmd_runner)?; + check_solutions(require_solutions, info_file, cmd_runner)?; println!("Everything looks fine!"); From e7ba88f90594962795bd028a8537efd6b1eedf6e Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 16:04:29 +0200 Subject: [PATCH 1189/1432] Highlight the solution file --- src/run.rs | 15 +++++++++------ src/watch/state.rs | 23 +++++++++++------------ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/run.rs b/src/run.rs index 964e13b892..0bc965cd41 100644 --- a/src/run.rs +++ b/src/run.rs @@ -33,18 +33,21 @@ pub fn run(app_state: &mut AppState) -> Result<()> { )?; if let Some(solution_path) = app_state.current_solution_path()? { - println!( - "\nA solution file can be found at {}\n", - style(TerminalFileLink(&solution_path)).underlined().green(), - ); + writeln!( + stdout, + "\n{} for comparison: {}\n", + "Solution".bold(), + style(TerminalFileLink(&solution_path)).underlined().cyan(), + )?; } match app_state.done_current_exercise(&mut stdout)? { ExercisesProgress::AllDone => (), - ExercisesProgress::CurrentPending | ExercisesProgress::NewPending => println!( + ExercisesProgress::CurrentPending | ExercisesProgress::NewPending => writeln!( + stdout, "Next exercise: {}", app_state.current_exercise().terminal_link(), - ), + )?, } Ok(()) diff --git a/src/watch/state.rs b/src/watch/state.rs index abfff7ac21..fe972faf70 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -137,21 +137,20 @@ impl<'a> WatchState<'a> { } if self.done_status != DoneStatus::Pending { - writeln!( - self.writer, - "{}\n", - "Exercise done βœ“ -When you are done experimenting, enter `n` to move on to the next exercise πŸ¦€" - .bold() - .green(), - )?; - } + writeln!(self.writer, "{}", "Exercise done βœ“".bold().green())?; + + if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { + writeln!( + self.writer, + "{} for comparison: {}", + "Solution".bold(), + style(TerminalFileLink(solution_path)).underlined().cyan(), + )?; + } - if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { writeln!( self.writer, - "A solution file can be found at {}\n", - style(TerminalFileLink(solution_path)).underlined().green(), + "When done experimenting, enter `n` to move on to the next exercise πŸ¦€\n", )?; } From a2d1cb3b2246f50ea36408f2be1962f5dc69d09b Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 20 Aug 2024 16:05:52 +0200 Subject: [PATCH 1190/1432] Push newline after running an exercise instead on each rendering --- src/watch/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index fe972faf70..6bf8e69289 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -56,10 +56,12 @@ impl<'a> WatchState<'a> { "\nChecking the exercise `{}`. Please wait…", self.app_state.current_exercise().name, )?; + let success = self .app_state .current_exercise() .run_exercise(Some(&mut self.output), self.app_state.cmd_runner())?; + self.output.push(b'\n'); if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { @@ -121,11 +123,9 @@ impl<'a> WatchState<'a> { pub fn render(&mut self) -> Result<()> { // Prevent having the first line shifted if clearing wasn't successful. self.writer.write_all(b"\n")?; - clear_terminal(&mut self.writer)?; self.writer.write_all(&self.output)?; - self.writer.write_all(b"\n")?; if self.show_hint { writeln!( From bedf0789f2129f333cc1af14775c40d7312297f5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 22 Aug 2024 14:25:14 +0200 Subject: [PATCH 1191/1432] Always use strict Clippy when checking solutions --- src/exercise.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index cdac2e2dc0..5318b9a598 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -72,7 +72,7 @@ pub trait RunnableExercise { // Compile, check and run the exercise or its solution (depending on `bin_nameΒ΄). // The output is written to the `output` buffer after clearing it. - fn run( + fn run( &self, bin_name: &str, mut output: Option<&mut Vec>, @@ -115,7 +115,7 @@ pub trait RunnableExercise { let mut clippy_cmd = cmd_runner.cargo("clippy", bin_name, output.as_deref_mut()); // `--profile test` is required to also check code with `[cfg(test)]`. - if self.strict_clippy() { + if FORCE_STRICT_CLIPPY || self.strict_clippy() { clippy_cmd.args(["--profile", "test", "--", "-D", "warnings"]); } else { clippy_cmd.args(["--profile", "test"]); @@ -131,7 +131,7 @@ pub trait RunnableExercise { /// The output is written to the `output` buffer after clearing it. #[inline] fn run_exercise(&self, output: Option<&mut Vec>, cmd_runner: &CmdRunner) -> Result { - self.run(self.name(), output, cmd_runner) + self.run::(self.name(), output, cmd_runner) } /// Compile, check and run the exercise's solution. @@ -142,7 +142,7 @@ pub trait RunnableExercise { bin_name.push_str(name); bin_name.push_str("_sol"); - self.run(&bin_name, output, cmd_runner) + self.run::(&bin_name, output, cmd_runner) } } From 423b50b068f7cb489e4c5f241b696491419620c1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 22 Aug 2024 14:37:47 +0200 Subject: [PATCH 1192/1432] Use match instead of comparison chain --- exercises/13_error_handling/errors4.rs | 2 -- solutions/13_error_handling/errors4.rs | 12 ++++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index e41d594576..ba01e54bf5 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -1,5 +1,3 @@ -#![allow(clippy::comparison_chain)] - #[derive(PartialEq, Debug)] enum CreationError { Negative, diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs index f4d39bf9c4..7f176cfc41 100644 --- a/solutions/13_error_handling/errors4.rs +++ b/solutions/13_error_handling/errors4.rs @@ -1,5 +1,3 @@ -#![allow(clippy::comparison_chain)] - #[derive(PartialEq, Debug)] enum CreationError { Negative, @@ -11,12 +9,10 @@ struct PositiveNonzeroInteger(u64); impl PositiveNonzeroInteger { fn new(value: i64) -> Result { - if value == 0 { - Err(CreationError::Zero) - } else if value < 0 { - Err(CreationError::Negative) - } else { - Ok(Self(value as u64)) + match value.cmp(&0) { + Ordering::Less => Err(CreationError::Negative), + Ordering::Equal => Err(CreationError::Zero), + Ordering::Greater => Ok(Self(value as u64)), } } } From f1abd8577c824eac4eb152a4b0789ce23642ba62 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 22 Aug 2024 14:41:25 +0200 Subject: [PATCH 1193/1432] Add missing Clippy allows to solutions --- solutions/09_strings/strings4.rs | 7 +++---- solutions/18_iterators/iterators4.rs | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs index fe4733e3e9..3c69b976e7 100644 --- a/solutions/09_strings/strings4.rs +++ b/solutions/09_strings/strings4.rs @@ -18,12 +18,11 @@ fn main() { // Here, both answers work. // `.into()` converts a type into an expected type. // If it is called where `String` is expected, it will convert `&str` to `String`. - // But if is called where `&str` is expected, then `&str` is kept `&str` since no - // conversion is needed. string("nice weather".into()); + // But if it is called where `&str` is expected, then `&str` is kept `&str` since no conversion is needed. + // If you remove the `#[allow(…)]` line, then Clippy will tell you to remove `.into()` below since it is a useless conversion. + #[allow(clippy::useless_conversion)] string_slice("nice weather".into()); - // ^^^^^^^ the compiler recommends removing the `.into()` - // call because it is a useless conversion. string(format!("Interpolation {}", "Station")); diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs index 4c3c49d962..4168835afc 100644 --- a/solutions/18_iterators/iterators4.rs +++ b/solutions/18_iterators/iterators4.rs @@ -25,6 +25,7 @@ fn factorial_fold(num: u64) -> u64 { // -> 1 * 2 is calculated, then the result 2 is multiplied by // the second element 3 so the result 6 is returned. // And so on… + #[allow(clippy::unnecessary_fold)] (2..=num).fold(1, |acc, x| acc * x) } From 47976caa69e24ea9ee5d38918a0abea89ff10983 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 22 Aug 2024 14:42:17 +0200 Subject: [PATCH 1194/1432] Import Ordering --- solutions/13_error_handling/errors4.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs index 7f176cfc41..70c5f1caa1 100644 --- a/solutions/13_error_handling/errors4.rs +++ b/solutions/13_error_handling/errors4.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + #[derive(PartialEq, Debug)] enum CreationError { Negative, From 570bc9f32d7ef0bf741fab44d15f7cd54a1f3fc1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 24 Aug 2024 00:14:12 +0200 Subject: [PATCH 1195/1432] Start list without Ratatui --- .typos.toml | 3 - Cargo.lock | 163 +--------------- Cargo.toml | 2 +- rustlings-macros/Cargo.toml | 2 +- src/exercise.rs | 2 +- src/init.rs | 2 +- src/list.rs | 150 ++++++++------- src/list/state.rs | 367 +++++++++++++++++------------------- src/main.rs | 1 + src/progress_bar.rs | 93 +++------ src/run.rs | 2 +- src/term.rs | 12 +- src/watch/state.rs | 14 +- src/watch/terminal_event.rs | 4 +- 14 files changed, 310 insertions(+), 507 deletions(-) diff --git a/.typos.toml b/.typos.toml index a74498ab1c..2de6d580e8 100644 --- a/.typos.toml +++ b/.typos.toml @@ -2,6 +2,3 @@ extend-exclude = [ "CHANGELOG.md", ] - -[default.extend-words] -"ratatui" = "ratatui" diff --git a/Cargo.lock b/Cargo.lock index a66f4ba312..93b2051be2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,12 +14,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - [[package]] name = "anstream" version = "0.6.15" @@ -93,21 +87,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" -dependencies = [ - "rustversion", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -160,20 +139,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" -[[package]] -name = "compact_str" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -214,12 +179,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - [[package]] name = "equivalent" version = "1.0.1" @@ -268,10 +227,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "heck" @@ -315,31 +270,12 @@ dependencies = [ "libc", ] -[[package]] -name = "instability" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -405,15 +341,6 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -[[package]] -name = "lru" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" -dependencies = [ - "hashbrown", -] - [[package]] name = "memchr" version = "2.7.4" @@ -513,12 +440,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "proc-macro2" version = "1.0.86" @@ -530,34 +451,13 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] -[[package]] -name = "ratatui" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303" -dependencies = [ - "bitflags 2.6.0", - "cassowary", - "compact_str", - "crossterm", - "instability", - "itertools", - "lru", - "paste", - "strum", - "strum_macros", - "unicode-segmentation", - "unicode-truncate", - "unicode-width", -] - [[package]] name = "redox_syscall" version = "0.5.3" @@ -587,9 +487,9 @@ dependencies = [ "ahash", "anyhow", "clap", + "crossterm", "notify-debouncer-mini", "os_pipe", - "ratatui", "rustlings-macros", "serde", "serde_json", @@ -606,12 +506,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -710,40 +604,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - [[package]] name = "syn" version = "2.0.75" @@ -796,29 +662,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools", - "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "unicode-width" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" - [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index a8b81eb2a9..4b3e98c415 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,9 +49,9 @@ include = [ ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.86" clap = { version = "4.5.16", features = ["derive"] } +crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" -ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" } serde_json = "1.0.125" serde.workspace = true diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 8a85201856..3ed56a1878 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -16,7 +16,7 @@ include = [ proc-macro = true [dependencies] -quote = "1.0.36" +quote = "1.0.37" serde.workspace = true toml_edit.workspace = true diff --git a/src/exercise.rs b/src/exercise.rs index 5318b9a598..ac5c6e6e45 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use ratatui::crossterm::style::{style, StyledContent, Stylize}; +use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, io::Write, diff --git a/src/init.rs b/src/init.rs index 95b04e9fd2..2c172dcaba 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Context, Result}; -use ratatui::crossterm::style::Stylize; +use crossterm::style::Stylize; use serde::Deserialize; use std::{ env::set_current_dir, diff --git a/src/list.rs b/src/list.rs index 6ff69596b8..754c5e2f24 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,95 +1,101 @@ use anyhow::{Context, Result}; -use ratatui::{ - backend::CrosstermBackend, - crossterm::{ - event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind}, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, - QueueableCommand, +use crossterm::{ + cursor, + event::{ + self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, MouseEventKind, }, - Terminal, + terminal::{ + disable_raw_mode, enable_raw_mode, DisableLineWrap, EnableLineWrap, EnterAlternateScreen, + LeaveAlternateScreen, + }, + QueueableCommand, }; use std::io::{self, StdoutLock, Write}; use crate::app_state::AppState; -use self::state::{Filter, UiState}; +use self::state::{Filter, ListState}; mod state; fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> { - let mut terminal = Terminal::new(CrosstermBackend::new(stdout))?; - terminal.clear()?; - - let mut ui_state = UiState::new(app_state); - - 'outer: loop { - terminal.try_draw(|frame| ui_state.draw(frame).map_err(io::Error::other))?; - - let key = loop { - match event::read().context("Failed to read terminal event")? { - Event::Key(key) => match key.kind { - KeyEventKind::Press | KeyEventKind::Repeat => break key, - KeyEventKind::Release => (), - }, - // Redraw - Event::Resize(_, _) => continue 'outer, - // Ignore - Event::FocusGained | Event::FocusLost | Event::Mouse(_) | Event::Paste(_) => (), + let mut list_state = ListState::new(app_state, stdout)?; + + loop { + match event::read().context("Failed to read terminal event")? { + Event::Key(key) => { + match key.kind { + KeyEventKind::Release => continue, + KeyEventKind::Press | KeyEventKind::Repeat => (), + } + + list_state.message.clear(); + + match key.code { + KeyCode::Char('q') => return Ok(()), + KeyCode::Down | KeyCode::Char('j') => list_state.select_next(), + KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(), + KeyCode::Home | KeyCode::Char('g') => list_state.select_first(), + KeyCode::End | KeyCode::Char('G') => list_state.select_last(), + KeyCode::Char('d') => { + let message = if list_state.filter() == Filter::Done { + list_state.set_filter(Filter::None); + "Disabled filter DONE" + } else { + list_state.set_filter(Filter::Done); + "Enabled filter DONE β”‚ Press d again to disable the filter" + }; + + list_state.message.push_str(message); + } + KeyCode::Char('p') => { + let message = if list_state.filter() == Filter::Pending { + list_state.set_filter(Filter::None); + "Disabled filter PENDING" + } else { + list_state.set_filter(Filter::Pending); + "Enabled filter PENDING β”‚ Press p again to disable the filter" + }; + + list_state.message.push_str(message); + } + KeyCode::Char('r') => { + list_state.reset_selected()?; + } + KeyCode::Char('c') => { + return list_state.selected_to_current_exercise(); + } + // Redraw to remove the message. + KeyCode::Esc => (), + _ => continue, + } + + list_state.redraw(stdout)?; } - }; - - ui_state.message.clear(); - - match key.code { - KeyCode::Char('q') => break, - KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(), - KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(), - KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(), - KeyCode::End | KeyCode::Char('G') => ui_state.select_last(), - KeyCode::Char('d') => { - let message = if ui_state.filter == Filter::Done { - ui_state.filter = Filter::None; - "Disabled filter DONE" - } else { - ui_state.filter = Filter::Done; - "Enabled filter DONE β”‚ Press d again to disable the filter" - }; - - ui_state = ui_state.with_updated_rows(); - ui_state.message.push_str(message); + Event::Mouse(event) => { + match event.kind { + MouseEventKind::ScrollDown => list_state.select_next(), + MouseEventKind::ScrollUp => list_state.select_previous(), + _ => continue, + } + + list_state.redraw(stdout)?; } - KeyCode::Char('p') => { - let message = if ui_state.filter == Filter::Pending { - ui_state.filter = Filter::None; - "Disabled filter PENDING" - } else { - ui_state.filter = Filter::Pending; - "Enabled filter PENDING β”‚ Press p again to disable the filter" - }; - - ui_state = ui_state.with_updated_rows(); - ui_state.message.push_str(message); - } - KeyCode::Char('r') => { - ui_state = ui_state.with_reset_selected()?; - } - KeyCode::Char('c') => { - ui_state.selected_to_current_exercise()?; - break; - } - _ => (), + // Redraw + Event::Resize(_, _) => list_state.redraw(stdout)?, + // Ignore + Event::FocusGained | Event::FocusLost => (), } } - - Ok(()) } pub fn list(app_state: &mut AppState) -> Result<()> { let mut stdout = io::stdout().lock(); stdout .queue(EnterAlternateScreen)? - .queue(EnableMouseCapture)? - .flush()?; + .queue(cursor::Hide)? + .queue(DisableLineWrap)? + .queue(EnableMouseCapture)?; enable_raw_mode()?; let res = handle_list(app_state, &mut stdout); @@ -97,6 +103,8 @@ pub fn list(app_state: &mut AppState) -> Result<()> { // Restore the terminal even if we got an error. stdout .queue(LeaveAlternateScreen)? + .queue(cursor::Show)? + .queue(EnableLineWrap)? .queue(DisableMouseCapture)? .flush()?; disable_raw_mode()?; diff --git a/src/list/state.rs b/src/list/state.rs index 48a404f36e..cf147b44ea 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -1,14 +1,19 @@ use anyhow::{Context, Result}; -use ratatui::{ - layout::{Constraint, Rect}, - style::{Style, Stylize}, - text::{Span, Text}, - widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, - Frame, +use crossterm::{ + cursor::{MoveDown, MoveTo}, + style::{Color, ResetColor, SetForegroundColor}, + terminal::{self, BeginSynchronizedUpdate, EndSynchronizedUpdate}, + QueueableCommand, }; -use std::{fmt::Write, mem}; +use std::{ + fmt::Write as _, + io::{self, StdoutLock, Write as _}, +}; + +use crate::{app_state::AppState, term::clear_terminal, MAX_EXERCISE_NAME_LEN}; -use crate::{app_state::AppState, progress_bar::progress_bar_ratatui}; +// +1 for padding. +const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -17,230 +22,213 @@ pub enum Filter { None, } -pub struct UiState<'a> { - pub table: Table<'static>, +pub struct ListState<'a> { pub message: String, - pub filter: Filter, + filter: Filter, app_state: &'a mut AppState, - table_state: TableState, - n_rows: usize, + n_rows_with_filter: usize, + name_col_width: usize, + offset: usize, + selected: Option, } -impl<'a> UiState<'a> { - pub fn with_updated_rows(mut self) -> Self { - let current_exercise_ind = self.app_state.current_exercise_ind(); - - self.n_rows = 0; - let rows = self - .app_state - .exercises() - .iter() - .enumerate() - .filter_map(|(ind, exercise)| { - let exercise_state = if exercise.done { - if self.filter == Filter::Pending { - return None; - } - - "DONE".green() - } else { - if self.filter == Filter::Done { - return None; - } - - "PENDING".yellow() - }; - - self.n_rows += 1; - - let next = if ind == current_exercise_ind { - ">>>>".bold().red() - } else { - Span::default() - }; - - Some(Row::new([ - next, - exercise_state, - Span::raw(exercise.name), - Span::raw(exercise.path), - ])) - }); - - self.table = self.table.rows(rows); - - if self.n_rows == 0 { - self.table_state.select(None); - } else { - self.table_state.select(Some( - self.table_state - .selected() - .map_or(0, |selected| selected.min(self.n_rows - 1)), - )); - } - - self - } - - pub fn new(app_state: &'a mut AppState) -> Self { - let header = Row::new(["Next", "State", "Name", "Path"]); - - let max_name_len = app_state +impl<'a> ListState<'a> { + pub fn new(app_state: &'a mut AppState, stdout: &mut StdoutLock) -> io::Result { + let name_col_width = app_state .exercises() .iter() .map(|exercise| exercise.name.len()) .max() - .unwrap_or(4) as u16; - - let widths = [ - Constraint::Length(4), - Constraint::Length(7), - Constraint::Length(max_name_len), - Constraint::Fill(1), - ]; - - let table = Table::default() - .widths(widths) - .header(header) - .column_spacing(2) - .highlight_spacing(HighlightSpacing::Always) - .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50))) - .highlight_symbol("πŸ¦€") - .block(Block::default().borders(Borders::BOTTOM)); + .map_or(4, |max| max.max(4)); - let selected = app_state.current_exercise_ind(); - let table_state = TableState::default() - .with_offset(selected.saturating_sub(10)) - .with_selected(Some(selected)); + clear_terminal(stdout)?; + stdout.write_all(b" Current State Name ")?; + stdout.write_all(&SPACE[..name_col_width - 4])?; + stdout.write_all(b"Path\r\n")?; - let filter = Filter::None; - let n_rows = app_state.exercises().len(); + let selected = app_state.current_exercise_ind(); + let n_rows_with_filter = app_state.exercises().len(); - let slf = Self { - table, + let mut slf = Self { message: String::with_capacity(128), - filter, + filter: Filter::None, app_state, - table_state, - n_rows, + n_rows_with_filter, + name_col_width, + offset: selected.saturating_sub(10), + selected: Some(selected), + }; + + slf.redraw(stdout)?; + + Ok(slf) + } + + #[inline] + pub fn filter(&self) -> Filter { + self.filter + } + + pub fn set_filter(&mut self, filter: Filter) { + self.filter = filter; + self.n_rows_with_filter = match filter { + Filter::Done => self + .app_state + .exercises() + .iter() + .filter(|exercise| !exercise.done) + .count(), + Filter::Pending => self + .app_state + .exercises() + .iter() + .filter(|exercise| exercise.done) + .count(), + Filter::None => self.app_state.exercises().len(), }; - slf.with_updated_rows() + if self.n_rows_with_filter == 0 { + self.selected = None; + } else { + self.selected = Some( + self.selected + .map_or(0, |selected| selected.min(self.n_rows_with_filter - 1)), + ); + } } pub fn select_next(&mut self) { - if self.n_rows > 0 { - let next = self - .table_state - .selected() - .map_or(0, |selected| (selected + 1).min(self.n_rows - 1)); - self.table_state.select(Some(next)); + if self.n_rows_with_filter > 0 { + let next = self.selected.map_or(0, |selected| { + (selected + 1).min(self.n_rows_with_filter - 1) + }); + self.selected = Some(next); } } pub fn select_previous(&mut self) { - if self.n_rows > 0 { + if self.n_rows_with_filter > 0 { let previous = self - .table_state - .selected() + .selected .map_or(0, |selected| selected.saturating_sub(1)); - self.table_state.select(Some(previous)); + self.selected = Some(previous); } } pub fn select_first(&mut self) { - if self.n_rows > 0 { - self.table_state.select(Some(0)); + if self.n_rows_with_filter > 0 { + self.selected = Some(0); } } pub fn select_last(&mut self) { - if self.n_rows > 0 { - self.table_state.select(Some(self.n_rows - 1)); + if self.n_rows_with_filter > 0 { + self.selected = Some(self.n_rows_with_filter - 1); } } - pub fn draw(&mut self, frame: &mut Frame) -> Result<()> { - let area = frame.area(); - let narrow = area.width < 95; + pub fn redraw(&mut self, stdout: &mut StdoutLock) -> io::Result<()> { + stdout.queue(BeginSynchronizedUpdate)?; + stdout.queue(MoveTo(0, 1))?; + let (width, height) = terminal::size()?; + let narrow = width < 95; let narrow_u16 = u16::from(narrow); - let table_height = area.height - 3 - narrow_u16; - - frame.render_stateful_widget( - &self.table, - Rect { - x: 0, - y: 0, - width: area.width, - height: table_height, - }, - &mut self.table_state, - ); - - frame.render_widget( - Paragraph::new(progress_bar_ratatui( - self.app_state.n_done(), - self.app_state.exercises().len() as u16, - area.width, - )?) - .block(Block::default().borders(Borders::BOTTOM)), - Rect { - x: 0, - y: table_height, - width: area.width, - height: 2, - }, - ); - - let message = if self.message.is_empty() { - // Help footer. - let mut text = Text::default(); - let mut spans = Vec::with_capacity(4); - spans.push(Span::raw( - "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚", - )); - - if narrow { - text.push_line(mem::take(&mut spans)); - spans.push(Span::raw("filter ")); + let max_n_rows_to_display = height.saturating_sub(narrow_u16 + 4); + + let displayed_exercises = self + .app_state + .exercises() + .iter() + .enumerate() + .filter(|(_, exercise)| match self.filter { + Filter::Done => exercise.done, + Filter::Pending => !exercise.done, + Filter::None => true, + }) + .skip(self.offset) + .take(max_n_rows_to_display as usize); + + let mut n_displayed_rows: u16 = 0; + let current_exercise_ind = self.app_state.current_exercise_ind(); + for (ind, exercise) in displayed_exercises { + if self.selected == Some(n_displayed_rows as usize) { + write!(stdout, "πŸ¦€")?; } else { - spans.push(Span::raw(" filter ")); + stdout.write_all(b" ")?; } - match self.filter { - Filter::Done => { - spans.push("one".underlined().magenta()); - spans.push(Span::raw("/

ending")); - } - Filter::Pending => { - spans.push(Span::raw("one/")); - spans.push("

ending".underlined().magenta()); - } - Filter::None => spans.push(Span::raw("one/

ending")), + if ind == current_exercise_ind { + stdout.queue(SetForegroundColor(Color::Red))?; + stdout.write_all(b">>>>>>> ")?; + } else { + stdout.write_all(b" ")?; } - spans.push(Span::raw(" β”‚ uit list")); - text.push_line(spans); - text - } else { - Text::from(self.message.as_str().light_blue()) - }; - frame.render_widget( - message, - Rect { - x: 0, - y: table_height + 2, - width: area.width, - height: 1 + narrow_u16, - }, - ); + if exercise.done { + stdout.queue(SetForegroundColor(Color::Yellow))?; + stdout.write_all(b"DONE ")?; + } else { + stdout.queue(SetForegroundColor(Color::Green))?; + stdout.write_all(b"PENDING ")?; + } + + stdout.queue(ResetColor)?; + + stdout.write_all(exercise.name.as_bytes())?; + stdout.write_all(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; + + stdout.write_all(exercise.path.as_bytes())?; + stdout.write_all(b"\r\n")?; + + n_displayed_rows += 1; + } + + stdout.queue(MoveDown(max_n_rows_to_display - n_displayed_rows))?; + + // TODO + // let message = if self.message.is_empty() { + // // Help footer. + // let mut text = Text::default(); + // let mut spans = Vec::with_capacity(4); + // spans.push(Span::raw( + // "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚", + // )); + + // if narrow { + // text.push_line(mem::take(&mut spans)); + // spans.push(Span::raw("filter ")); + // } else { + // spans.push(Span::raw(" filter ")); + // } + + // match self.filter { + // Filter::Done => { + // spans.push("one".underlined().magenta()); + // spans.push(Span::raw("/

ending")); + // } + // Filter::Pending => { + // spans.push(Span::raw("one/")); + // spans.push("

ending".underlined().magenta()); + // } + // Filter::None => spans.push(Span::raw("one/

ending")), + // } + + // spans.push(Span::raw(" β”‚ uit list")); + // text.push_line(spans); + // text + // } else { + // Text::from(self.message.as_str().light_blue()) + // }; + + stdout.queue(EndSynchronizedUpdate)?; + stdout.flush()?; Ok(()) } - pub fn with_reset_selected(mut self) -> Result { - let Some(selected) = self.table_state.selected() else { - return Ok(self); + pub fn reset_selected(&mut self) -> Result<()> { + let Some(selected) = self.selected else { + return Ok(()); }; let ind = self @@ -259,11 +247,12 @@ impl<'a> UiState<'a> { let exercise_path = self.app_state.reset_exercise_by_ind(ind)?; write!(self.message, "The exercise {exercise_path} has been reset")?; - Ok(self.with_updated_rows()) + Ok(()) } pub fn selected_to_current_exercise(&mut self) -> Result<()> { - let Some(selected) = self.table_state.selected() else { + let Some(selected) = self.selected else { + // TODO: Don't exit list return Ok(()); }; diff --git a/src/main.rs b/src/main.rs index 0855d4352b..59513671d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,7 @@ mod terminal_link; mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; +const MAX_EXERCISE_NAME_LEN: usize = 32; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] diff --git a/src/progress_bar.rs b/src/progress_bar.rs index 7f07ad593f..837c4c78ad 100644 --- a/src/progress_bar.rs +++ b/src/progress_bar.rs @@ -1,100 +1,53 @@ -use anyhow::{bail, Result}; -use ratatui::text::{Line, Span}; -use std::fmt::Write; +use std::io::{self, StdoutLock, Write}; -const PREFIX: &str = "Progress: ["; +use crossterm::{ + style::{Color, ResetColor, SetForegroundColor}, + QueueableCommand, +}; + +const PREFIX: &[u8] = b"Progress: ["; const PREFIX_WIDTH: u16 = PREFIX.len() as u16; // Leaving the last char empty (_) for `total` > 99. const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16; const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH; const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; -const PROGRESS_EXCEEDS_MAX_ERR: &str = - "The progress of the progress bar is higher than the maximum"; - /// Terminal progress bar to be used when not using Ratataui. -pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result { - use ratatui::crossterm::style::Stylize; - - if progress > total { - bail!(PROGRESS_EXCEEDS_MAX_ERR); - } +pub fn progress_bar( + stdout: &mut StdoutLock, + progress: u16, + total: u16, + line_width: u16, +) -> io::Result<()> { + debug_assert!(progress <= total); if line_width < MIN_LINE_WIDTH { - return Ok(format!("Progress: {progress}/{total} exercises")); + return write!(stdout, "Progress: {progress}/{total} exercises"); } - let mut line = String::with_capacity(usize::from(line_width)); - line.push_str(PREFIX); + stdout.write_all(PREFIX)?; let width = line_width - WRAPPER_WIDTH; let filled = (width * progress) / total; - let mut green_part = String::with_capacity(usize::from(filled + 1)); + stdout.queue(SetForegroundColor(Color::Green))?; for _ in 0..filled { - green_part.push('#'); + stdout.write_all(b"#")?; } if filled < width { - green_part.push('>'); + stdout.write_all(b">")?; } - write!(line, "{}", green_part.green()).unwrap(); let width_minus_filled = width - filled; if width_minus_filled > 1 { let red_part_width = width_minus_filled - 1; - let mut red_part = String::with_capacity(usize::from(red_part_width)); + stdout.queue(SetForegroundColor(Color::Red))?; for _ in 0..red_part_width { - red_part.push('-'); + stdout.write_all(b"-")?; } - write!(line, "{}", red_part.red()).unwrap(); - } - - writeln!(line, "] {progress:>3}/{total} exercises").unwrap(); - - Ok(line) -} - -/// Progress bar to be used with Ratataui. -// Not using Ratatui's Gauge widget to keep the progress bar consistent. -pub fn progress_bar_ratatui(progress: u16, total: u16, line_width: u16) -> Result> { - use ratatui::style::Stylize; - - if progress > total { - bail!(PROGRESS_EXCEEDS_MAX_ERR); - } - - if line_width < MIN_LINE_WIDTH { - return Ok(Line::raw(format!("Progress: {progress}/{total} exercises"))); } - let mut spans = Vec::with_capacity(4); - spans.push(Span::raw(PREFIX)); - - let width = line_width - WRAPPER_WIDTH; - let filled = (width * progress) / total; - - let mut green_part = String::with_capacity(usize::from(filled + 1)); - for _ in 0..filled { - green_part.push('#'); - } - - if filled < width { - green_part.push('>'); - } - spans.push(green_part.green()); - - let width_minus_filled = width - filled; - if width_minus_filled > 1 { - let red_part_width = width_minus_filled - 1; - let mut red_part = String::with_capacity(usize::from(red_part_width)); - for _ in 0..red_part_width { - red_part.push('-'); - } - spans.push(red_part.red()); - } - - spans.push(Span::raw(format!("] {progress:>3}/{total} exercises"))); - - Ok(Line::from(spans)) + stdout.queue(ResetColor)?; + write!(stdout, "] {progress:>3}/{total} exercises") } diff --git a/src/run.rs b/src/run.rs index 0bc965cd41..09e53ec9e1 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use ratatui::crossterm::style::{style, Stylize}; +use crossterm::style::{style, Stylize}; use std::io::{self, Write}; use crate::{ diff --git a/src/term.rs b/src/term.rs index e1ac3da95b..07edf9003b 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,7 +1,17 @@ use std::io::{self, BufRead, StdoutLock, Write}; +use crossterm::{ + cursor::MoveTo, + terminal::{Clear, ClearType}, + QueueableCommand, +}; + pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { - stdout.write_all(b"\x1b[H\x1b[2J\x1b[3J") + stdout + .queue(MoveTo(0, 0))? + .queue(Clear(ClearType::All))? + .queue(Clear(ClearType::Purge)) + .map(|_| ()) } pub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> { diff --git a/src/watch/state.rs b/src/watch/state.rs index 6bf8e69289..26c83d502c 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use ratatui::crossterm::{ +use crossterm::{ style::{style, Stylize}, terminal, }; @@ -76,7 +76,8 @@ impl<'a> WatchState<'a> { self.done_status = DoneStatus::Pending; } - self.render() + self.render()?; + Ok(()) } pub fn handle_file_change(&mut self, exercise_ind: usize) -> Result<()> { @@ -120,7 +121,7 @@ impl<'a> WatchState<'a> { self.writer.flush() } - pub fn render(&mut self) -> Result<()> { + pub fn render(&mut self) -> io::Result<()> { // Prevent having the first line shifted if clearing wasn't successful. self.writer.write_all(b"\n")?; clear_terminal(&mut self.writer)?; @@ -155,14 +156,15 @@ impl<'a> WatchState<'a> { } let line_width = terminal::size()?.0; - let progress_bar = progress_bar( + progress_bar( + &mut self.writer, self.app_state.n_done(), self.app_state.exercises().len() as u16, line_width, )?; writeln!( self.writer, - "{progress_bar}Current exercise: {}", + "\nCurrent exercise: {}", self.app_state.current_exercise().terminal_link(), )?; @@ -171,7 +173,7 @@ impl<'a> WatchState<'a> { Ok(()) } - pub fn show_hint(&mut self) -> Result<()> { + pub fn show_hint(&mut self) -> io::Result<()> { self.show_hint = true; self.render() } diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 3a1762d300..3e8c272a98 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -1,4 +1,4 @@ -use ratatui::crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; +use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; use std::sync::mpsc::Sender; use super::WatchEvent; @@ -78,7 +78,7 @@ pub fn terminal_event_handler(tx: Sender, manual_run: bool) { return; } } - Event::FocusGained | Event::FocusLost | Event::Mouse(_) | Event::Paste(_) => continue, + Event::FocusGained | Event::FocusLost | Event::Mouse(_) => continue, } }; From 4e12725616abe1918d6a4f21b23288dfac237cc4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 24 Aug 2024 00:23:45 +0200 Subject: [PATCH 1196/1432] Don't exit the list on "to current" if nothing is selected --- src/list.rs | 4 +++- src/list/state.rs | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/list.rs b/src/list.rs index 754c5e2f24..27a31d1305 100644 --- a/src/list.rs +++ b/src/list.rs @@ -63,7 +63,9 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.reset_selected()?; } KeyCode::Char('c') => { - return list_state.selected_to_current_exercise(); + if list_state.selected_to_current_exercise()? { + return Ok(()); + } } // Redraw to remove the message. KeyCode::Esc => (), diff --git a/src/list/state.rs b/src/list/state.rs index cf147b44ea..645c768f80 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -250,25 +250,27 @@ impl<'a> ListState<'a> { Ok(()) } - pub fn selected_to_current_exercise(&mut self) -> Result<()> { + // Return `true` if there was something to select. + pub fn selected_to_current_exercise(&mut self) -> Result { let Some(selected) = self.selected else { - // TODO: Don't exit list - return Ok(()); + self.message.push_str("Nothing selected to continue at!"); + return Ok(false); }; - let ind = self + let (ind, _) = self .app_state .exercises() .iter() .enumerate() - .filter_map(|(ind, exercise)| match self.filter { - Filter::Done => exercise.done.then_some(ind), - Filter::Pending => (!exercise.done).then_some(ind), - Filter::None => Some(ind), + .filter(|(_, exercise)| match self.filter { + Filter::Done => exercise.done, + Filter::Pending => !exercise.done, + Filter::None => true, }) .nth(selected) .context("Invalid selection index")?; - self.app_state.set_current_exercise_ind(ind) + self.app_state.set_current_exercise_ind(ind)?; + Ok(true) } } From b779c431268da50989257056d21a870a61a1702e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 24 Aug 2024 17:17:56 +0200 Subject: [PATCH 1197/1432] Almost done with list display --- src/list.rs | 35 +++-- src/list/state.rs | 334 ++++++++++++++++++++++++++------------------ src/main.rs | 1 - src/progress_bar.rs | 53 ------- src/term.rs | 48 +++++++ src/watch/state.rs | 2 +- 6 files changed, 260 insertions(+), 213 deletions(-) delete mode 100644 src/progress_bar.rs diff --git a/src/list.rs b/src/list.rs index 27a31d1305..a571eeec38 100644 --- a/src/list.rs +++ b/src/list.rs @@ -38,15 +38,15 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> KeyCode::Home | KeyCode::Char('g') => list_state.select_first(), KeyCode::End | KeyCode::Char('G') => list_state.select_last(), KeyCode::Char('d') => { - let message = if list_state.filter() == Filter::Done { + if list_state.filter() == Filter::Done { list_state.set_filter(Filter::None); - "Disabled filter DONE" + list_state.message.push_str("Disabled filter DONE"); } else { list_state.set_filter(Filter::Done); - "Enabled filter DONE β”‚ Press d again to disable the filter" - }; - - list_state.message.push_str(message); + list_state.message.push_str( + "Enabled filter DONE β”‚ Press d again to disable the filter", + ); + } } KeyCode::Char('p') => { let message = if list_state.filter() == Filter::Pending { @@ -71,23 +71,20 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> KeyCode::Esc => (), _ => continue, } - - list_state.redraw(stdout)?; } - Event::Mouse(event) => { - match event.kind { - MouseEventKind::ScrollDown => list_state.select_next(), - MouseEventKind::ScrollUp => list_state.select_previous(), - _ => continue, - } - - list_state.redraw(stdout)?; + Event::Mouse(event) => match event.kind { + MouseEventKind::ScrollDown => list_state.select_next(), + MouseEventKind::ScrollUp => list_state.select_previous(), + _ => continue, + }, + Event::Resize(width, height) => { + list_state.set_term_size(width, height); } - // Redraw - Event::Resize(_, _) => list_state.redraw(stdout)?, // Ignore - Event::FocusGained | Event::FocusLost => (), + Event::FocusGained | Event::FocusLost => continue, } + + list_state.redraw(stdout)?; } } diff --git a/src/list/state.rs b/src/list/state.rs index 645c768f80..d8744352e5 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -1,19 +1,30 @@ use anyhow::{Context, Result}; use crossterm::{ - cursor::{MoveDown, MoveTo}, - style::{Color, ResetColor, SetForegroundColor}, - terminal::{self, BeginSynchronizedUpdate, EndSynchronizedUpdate}, + cursor::{MoveTo, MoveToNextLine}, + style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, + terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate}, QueueableCommand, }; use std::{ fmt::Write as _, - io::{self, StdoutLock, Write as _}, + io::{self, StdoutLock, Write}, }; -use crate::{app_state::AppState, term::clear_terminal, MAX_EXERCISE_NAME_LEN}; +use crate::{app_state::AppState, term::progress_bar, MAX_EXERCISE_NAME_LEN}; -// +1 for padding. -const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; +fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { + if CLEAR_LAST_CHAR { + // Avoids having the last written char as the last displayed one when + // the written width is higher than the terminal width. + // Happens on the Gnome terminal for example. + stdout.write_all(b" ")?; + } + + stdout + .queue(Clear(ClearType::UntilNewLine))? + .queue(MoveToNextLine(1))?; + Ok(()) +} #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -30,10 +41,16 @@ pub struct ListState<'a> { name_col_width: usize, offset: usize, selected: Option, + term_width: u16, + term_height: u16, + separator: Vec, } impl<'a> ListState<'a> { pub fn new(app_state: &'a mut AppState, stdout: &mut StdoutLock) -> io::Result { + let (term_width, term_height) = terminal::size()?; + stdout.queue(Clear(ClearType::All))?; + let name_col_width = app_state .exercises() .iter() @@ -41,13 +58,8 @@ impl<'a> ListState<'a> { .max() .map_or(4, |max| max.max(4)); - clear_terminal(stdout)?; - stdout.write_all(b" Current State Name ")?; - stdout.write_all(&SPACE[..name_col_width - 4])?; - stdout.write_all(b"Path\r\n")?; - - let selected = app_state.current_exercise_ind(); let n_rows_with_filter = app_state.exercises().len(); + let selected = app_state.current_exercise_ind(); let mut slf = Self { message: String::with_capacity(128), @@ -57,6 +69,9 @@ impl<'a> ListState<'a> { name_col_width, offset: selected.saturating_sub(10), selected: Some(selected), + term_width, + term_height, + separator: "─".as_bytes().repeat(term_width as usize), }; slf.redraw(stdout)?; @@ -64,6 +79,145 @@ impl<'a> ListState<'a> { Ok(slf) } + pub fn redraw(&mut self, stdout: &mut StdoutLock) -> io::Result<()> { + if self.term_height == 0 { + return Ok(()); + } + + stdout.queue(BeginSynchronizedUpdate)?.queue(MoveTo(0, 0))?; + + // +1 for padding. + const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; + stdout.write_all(b" Current State Name")?; + stdout.write_all(&SPACE[..self.name_col_width - 2])?; + stdout.write_all(b"Path")?; + next_ln::(stdout)?; + + let narrow = self.term_width < 96; + let show_footer = self.term_height > 6; + let max_n_rows_to_display = + (self.term_height - 1 - u16::from(show_footer) * (4 + u16::from(narrow))) as usize; + + let displayed_exercises = self + .app_state + .exercises() + .iter() + .enumerate() + .filter(|(_, exercise)| match self.filter { + Filter::Done => exercise.done, + Filter::Pending => !exercise.done, + Filter::None => true, + }) + .skip(self.offset) + .take(max_n_rows_to_display); + + let current_exercise_ind = self.app_state.current_exercise_ind(); + let mut n_displayed_rows = 0; + for (exercise_ind, exercise) in displayed_exercises { + if self.selected == Some(n_displayed_rows) { + stdout.write_all("πŸ¦€".as_bytes())?; + } else { + stdout.write_all(b" ")?; + } + + if exercise_ind == current_exercise_ind { + stdout.queue(SetForegroundColor(Color::Red))?; + stdout.write_all(b">>>>>>> ")?; + } else { + stdout.write_all(b" ")?; + } + + if exercise.done { + stdout.queue(SetForegroundColor(Color::Yellow))?; + stdout.write_all(b"DONE ")?; + } else { + stdout.queue(SetForegroundColor(Color::Green))?; + stdout.write_all(b"PENDING ")?; + } + + stdout.queue(ResetColor)?; + + stdout.write_all(exercise.name.as_bytes())?; + stdout.write_all(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; + + stdout.write_all(exercise.path.as_bytes())?; + + next_ln::(stdout)?; + n_displayed_rows += 1; + } + + for _ in 0..max_n_rows_to_display - n_displayed_rows { + next_ln::(stdout)?; + } + + if show_footer { + stdout.write_all(&self.separator)?; + next_ln::(stdout)?; + + progress_bar( + stdout, + self.app_state.n_done(), + self.app_state.exercises().len() as u16, + self.term_width, + )?; + next_ln::(stdout)?; + + stdout.write_all(&self.separator)?; + next_ln::(stdout)?; + + if self.message.is_empty() { + // Help footer. + stdout.write_all( + "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚".as_bytes(), + )?; + if narrow { + next_ln::(stdout)?; + stdout.write_all(b"filter ")?; + } else { + stdout.write_all(b" filter ")?; + } + + match self.filter { + Filter::Done => { + stdout + .queue(SetForegroundColor(Color::Magenta))? + .queue(SetAttribute(Attribute::Underlined))?; + stdout.write_all(b"one")?; + stdout.queue(ResetColor)?; + stdout.write_all(b"/

ending")?; + } + Filter::Pending => { + stdout.write_all(b"one/")?; + stdout + .queue(SetForegroundColor(Color::Magenta))? + .queue(SetAttribute(Attribute::Underlined))?; + stdout.write_all(b"

ending")?; + stdout.queue(ResetColor)?; + } + Filter::None => stdout.write_all(b"one/

ending")?, + } + stdout.write_all(" β”‚ uit list".as_bytes())?; + next_ln::(stdout)?; + } else { + stdout.queue(SetForegroundColor(Color::Magenta))?; + stdout.write_all(self.message.as_bytes())?; + stdout.queue(ResetColor)?; + next_ln::(stdout)?; + if narrow { + next_ln::(stdout)?; + } + } + } + + stdout.queue(EndSynchronizedUpdate)?.flush() + } + + pub fn set_term_size(&mut self, width: u16, height: u16) { + self.term_width = width; + self.term_height = height; + self.separator = "─".as_bytes().repeat(width as usize); + } + #[inline] pub fn filter(&self) -> Filter { self.filter @@ -76,13 +230,13 @@ impl<'a> ListState<'a> { .app_state .exercises() .iter() - .filter(|exercise| !exercise.done) + .filter(|exercise| exercise.done) .count(), Filter::Pending => self .app_state .exercises() .iter() - .filter(|exercise| exercise.done) + .filter(|exercise| !exercise.done) .count(), Filter::None => self.app_state.exercises().len(), }; @@ -127,124 +281,38 @@ impl<'a> ListState<'a> { } } - pub fn redraw(&mut self, stdout: &mut StdoutLock) -> io::Result<()> { - stdout.queue(BeginSynchronizedUpdate)?; - stdout.queue(MoveTo(0, 1))?; - let (width, height) = terminal::size()?; - let narrow = width < 95; - let narrow_u16 = u16::from(narrow); - let max_n_rows_to_display = height.saturating_sub(narrow_u16 + 4); - - let displayed_exercises = self - .app_state - .exercises() - .iter() - .enumerate() - .filter(|(_, exercise)| match self.filter { - Filter::Done => exercise.done, - Filter::Pending => !exercise.done, - Filter::None => true, - }) - .skip(self.offset) - .take(max_n_rows_to_display as usize); - - let mut n_displayed_rows: u16 = 0; - let current_exercise_ind = self.app_state.current_exercise_ind(); - for (ind, exercise) in displayed_exercises { - if self.selected == Some(n_displayed_rows as usize) { - write!(stdout, "πŸ¦€")?; - } else { - stdout.write_all(b" ")?; - } - - if ind == current_exercise_ind { - stdout.queue(SetForegroundColor(Color::Red))?; - stdout.write_all(b">>>>>>> ")?; - } else { - stdout.write_all(b" ")?; - } - - if exercise.done { - stdout.queue(SetForegroundColor(Color::Yellow))?; - stdout.write_all(b"DONE ")?; - } else { - stdout.queue(SetForegroundColor(Color::Green))?; - stdout.write_all(b"PENDING ")?; - } - - stdout.queue(ResetColor)?; - - stdout.write_all(exercise.name.as_bytes())?; - stdout.write_all(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; - - stdout.write_all(exercise.path.as_bytes())?; - stdout.write_all(b"\r\n")?; - - n_displayed_rows += 1; + fn selected_to_exercise_ind(&self, selected: usize) -> Result { + match self.filter { + Filter::Done => self + .app_state + .exercises() + .iter() + .enumerate() + .filter(|(_, exercise)| exercise.done) + .nth(selected) + .context("Invalid selection index") + .map(|(ind, _)| ind), + Filter::Pending => self + .app_state + .exercises() + .iter() + .enumerate() + .filter(|(_, exercise)| !exercise.done) + .nth(selected) + .context("Invalid selection index") + .map(|(ind, _)| ind), + Filter::None => Ok(selected), } - - stdout.queue(MoveDown(max_n_rows_to_display - n_displayed_rows))?; - - // TODO - // let message = if self.message.is_empty() { - // // Help footer. - // let mut text = Text::default(); - // let mut spans = Vec::with_capacity(4); - // spans.push(Span::raw( - // "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚", - // )); - - // if narrow { - // text.push_line(mem::take(&mut spans)); - // spans.push(Span::raw("filter ")); - // } else { - // spans.push(Span::raw(" filter ")); - // } - - // match self.filter { - // Filter::Done => { - // spans.push("one".underlined().magenta()); - // spans.push(Span::raw("/

ending")); - // } - // Filter::Pending => { - // spans.push(Span::raw("one/")); - // spans.push("

ending".underlined().magenta()); - // } - // Filter::None => spans.push(Span::raw("one/

ending")), - // } - - // spans.push(Span::raw(" β”‚ uit list")); - // text.push_line(spans); - // text - // } else { - // Text::from(self.message.as_str().light_blue()) - // }; - - stdout.queue(EndSynchronizedUpdate)?; - stdout.flush()?; - - Ok(()) } pub fn reset_selected(&mut self) -> Result<()> { let Some(selected) = self.selected else { + self.message.push_str("Nothing selected to reset!"); return Ok(()); }; - let ind = self - .app_state - .exercises() - .iter() - .enumerate() - .filter_map(|(ind, exercise)| match self.filter { - Filter::Done => exercise.done.then_some(ind), - Filter::Pending => (!exercise.done).then_some(ind), - Filter::None => Some(ind), - }) - .nth(selected) - .context("Invalid selection index")?; - - let exercise_path = self.app_state.reset_exercise_by_ind(ind)?; + let exercise_ind = self.selected_to_exercise_ind(selected)?; + let exercise_path = self.app_state.reset_exercise_by_ind(exercise_ind)?; write!(self.message, "The exercise {exercise_path} has been reset")?; Ok(()) @@ -257,20 +325,8 @@ impl<'a> ListState<'a> { return Ok(false); }; - let (ind, _) = self - .app_state - .exercises() - .iter() - .enumerate() - .filter(|(_, exercise)| match self.filter { - Filter::Done => exercise.done, - Filter::Pending => !exercise.done, - Filter::None => true, - }) - .nth(selected) - .context("Invalid selection index")?; - - self.app_state.set_current_exercise_ind(ind)?; + let exercise_ind = self.selected_to_exercise_ind(selected)?; + self.app_state.set_current_exercise_ind(exercise_ind)?; Ok(true) } } diff --git a/src/main.rs b/src/main.rs index 59513671d6..61dd8ea8db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,6 @@ mod exercise; mod info_file; mod init; mod list; -mod progress_bar; mod run; mod term; mod terminal_link; diff --git a/src/progress_bar.rs b/src/progress_bar.rs deleted file mode 100644 index 837c4c78ad..0000000000 --- a/src/progress_bar.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::io::{self, StdoutLock, Write}; - -use crossterm::{ - style::{Color, ResetColor, SetForegroundColor}, - QueueableCommand, -}; - -const PREFIX: &[u8] = b"Progress: ["; -const PREFIX_WIDTH: u16 = PREFIX.len() as u16; -// Leaving the last char empty (_) for `total` > 99. -const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16; -const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH; -const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; - -/// Terminal progress bar to be used when not using Ratataui. -pub fn progress_bar( - stdout: &mut StdoutLock, - progress: u16, - total: u16, - line_width: u16, -) -> io::Result<()> { - debug_assert!(progress <= total); - - if line_width < MIN_LINE_WIDTH { - return write!(stdout, "Progress: {progress}/{total} exercises"); - } - - stdout.write_all(PREFIX)?; - - let width = line_width - WRAPPER_WIDTH; - let filled = (width * progress) / total; - - stdout.queue(SetForegroundColor(Color::Green))?; - for _ in 0..filled { - stdout.write_all(b"#")?; - } - - if filled < width { - stdout.write_all(b">")?; - } - - let width_minus_filled = width - filled; - if width_minus_filled > 1 { - let red_part_width = width_minus_filled - 1; - stdout.queue(SetForegroundColor(Color::Red))?; - for _ in 0..red_part_width { - stdout.write_all(b"-")?; - } - } - - stdout.queue(ResetColor)?; - write!(stdout, "] {progress:>3}/{total} exercises") -} diff --git a/src/term.rs b/src/term.rs index 07edf9003b..b993108ea4 100644 --- a/src/term.rs +++ b/src/term.rs @@ -2,10 +2,58 @@ use std::io::{self, BufRead, StdoutLock, Write}; use crossterm::{ cursor::MoveTo, + style::{Color, ResetColor, SetForegroundColor}, terminal::{Clear, ClearType}, QueueableCommand, }; +/// Terminal progress bar to be used when not using Ratataui. +pub fn progress_bar( + stdout: &mut StdoutLock, + progress: u16, + total: u16, + line_width: u16, +) -> io::Result<()> { + debug_assert!(progress <= total); + + const PREFIX: &[u8] = b"Progress: ["; + const PREFIX_WIDTH: u16 = PREFIX.len() as u16; + // Leaving the last char empty (_) for `total` > 99. + const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16; + const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH; + const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; + + if line_width < MIN_LINE_WIDTH { + return write!(stdout, "Progress: {progress}/{total} exercises"); + } + + stdout.write_all(PREFIX)?; + + let width = line_width - WRAPPER_WIDTH; + let filled = (width * progress) / total; + + stdout.queue(SetForegroundColor(Color::Green))?; + for _ in 0..filled { + stdout.write_all(b"#")?; + } + + if filled < width { + stdout.write_all(b">")?; + } + + let width_minus_filled = width - filled; + if width_minus_filled > 1 { + let red_part_width = width_minus_filled - 1; + stdout.queue(SetForegroundColor(Color::Red))?; + for _ in 0..red_part_width { + stdout.write_all(b"-")?; + } + } + + stdout.queue(ResetColor)?; + write!(stdout, "] {progress:>3}/{total} exercises") +} + pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { stdout .queue(MoveTo(0, 0))? diff --git a/src/watch/state.rs b/src/watch/state.rs index 26c83d502c..40e3d3ece7 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -9,7 +9,7 @@ use crate::{ app_state::{AppState, ExercisesProgress}, clear_terminal, exercise::{RunnableExercise, OUTPUT_CAPACITY}, - progress_bar::progress_bar, + term::progress_bar, terminal_link::TerminalFileLink, }; From 28d0b0a21ec2d916309733dcce8aecdbdf305d46 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 24 Aug 2024 17:45:02 +0200 Subject: [PATCH 1198/1432] Highlight selected row --- src/list/state.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index d8744352e5..4ba3d4eb23 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use crossterm::{ cursor::{MoveTo, MoveToNextLine}, - style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, + style::{Attribute, Color, ResetColor, SetAttribute, SetBackgroundColor, SetForegroundColor}, terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate}, QueueableCommand, }; @@ -115,6 +115,11 @@ impl<'a> ListState<'a> { let mut n_displayed_rows = 0; for (exercise_ind, exercise) in displayed_exercises { if self.selected == Some(n_displayed_rows) { + stdout.queue(SetBackgroundColor(Color::Rgb { + r: 50, + g: 50, + b: 50, + }))?; stdout.write_all("πŸ¦€".as_bytes())?; } else { stdout.write_all(b" ")?; @@ -135,7 +140,7 @@ impl<'a> ListState<'a> { stdout.write_all(b"PENDING ")?; } - stdout.queue(ResetColor)?; + stdout.queue(SetForegroundColor(Color::Reset))?; stdout.write_all(exercise.name.as_bytes())?; stdout.write_all(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; @@ -143,6 +148,7 @@ impl<'a> ListState<'a> { stdout.write_all(exercise.path.as_bytes())?; next_ln::(stdout)?; + stdout.queue(ResetColor)?; n_displayed_rows += 1; } From b6129ad0811e05a256713614db899a98308cb62c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 24 Aug 2024 17:45:38 +0200 Subject: [PATCH 1199/1432] Use the full length for the wide footer --- src/list/state.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 4ba3d4eb23..d45074165e 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -93,7 +93,7 @@ impl<'a> ListState<'a> { stdout.write_all(b"Path")?; next_ln::(stdout)?; - let narrow = self.term_width < 96; + let narrow = self.term_width < 95; let show_footer = self.term_height > 6; let max_n_rows_to_display = (self.term_height - 1 - u16::from(show_footer) * (4 + u16::from(narrow))) as usize; @@ -203,7 +203,11 @@ impl<'a> ListState<'a> { Filter::None => stdout.write_all(b"one/

ending")?, } stdout.write_all(" β”‚ uit list".as_bytes())?; - next_ln::(stdout)?; + if narrow { + next_ln::(stdout)?; + } else { + next_ln::(stdout)?; + } } else { stdout.queue(SetForegroundColor(Color::Magenta))?; stdout.write_all(self.message.as_bytes())?; From fd2a8c01cb35fcff4a5358cce9473ff91272c790 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 24 Aug 2024 19:18:13 +0200 Subject: [PATCH 1200/1432] Separate drawing rows --- src/list.rs | 2 +- src/list/state.rs | 90 ++++++++++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/list.rs b/src/list.rs index a571eeec38..e3601825e3 100644 --- a/src/list.rs +++ b/src/list.rs @@ -84,7 +84,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> Event::FocusGained | Event::FocusLost => continue, } - list_state.redraw(stdout)?; + list_state.draw(stdout)?; } } diff --git a/src/list/state.rs b/src/list/state.rs index d45074165e..cbca1d9ece 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -10,7 +10,10 @@ use std::{ io::{self, StdoutLock, Write}, }; -use crate::{app_state::AppState, term::progress_bar, MAX_EXERCISE_NAME_LEN}; +use crate::{app_state::AppState, exercise::Exercise, term::progress_bar, MAX_EXERCISE_NAME_LEN}; + +// +1 for padding. +const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { if CLEAR_LAST_CHAR { @@ -74,46 +77,24 @@ impl<'a> ListState<'a> { separator: "─".as_bytes().repeat(term_width as usize), }; - slf.redraw(stdout)?; + slf.draw(stdout)?; Ok(slf) } - pub fn redraw(&mut self, stdout: &mut StdoutLock) -> io::Result<()> { - if self.term_height == 0 { - return Ok(()); - } - - stdout.queue(BeginSynchronizedUpdate)?.queue(MoveTo(0, 0))?; - - // +1 for padding. - const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; - stdout.write_all(b" Current State Name")?; - stdout.write_all(&SPACE[..self.name_col_width - 2])?; - stdout.write_all(b"Path")?; - next_ln::(stdout)?; - - let narrow = self.term_width < 95; - let show_footer = self.term_height > 6; - let max_n_rows_to_display = - (self.term_height - 1 - u16::from(show_footer) * (4 + u16::from(narrow))) as usize; - - let displayed_exercises = self - .app_state - .exercises() - .iter() - .enumerate() - .filter(|(_, exercise)| match self.filter { - Filter::Done => exercise.done, - Filter::Pending => !exercise.done, - Filter::None => true, - }) - .skip(self.offset) - .take(max_n_rows_to_display); - + fn draw_rows( + &self, + stdout: &mut StdoutLock, + max_n_rows_to_display: usize, + filtered_exercises: impl Iterator, + ) -> io::Result { let current_exercise_ind = self.app_state.current_exercise_ind(); let mut n_displayed_rows = 0; - for (exercise_ind, exercise) in displayed_exercises { + + for (exercise_ind, exercise) in filtered_exercises + .skip(self.offset) + .take(max_n_rows_to_display) + { if self.selected == Some(n_displayed_rows) { stdout.queue(SetBackgroundColor(Color::Rgb { r: 50, @@ -152,6 +133,43 @@ impl<'a> ListState<'a> { n_displayed_rows += 1; } + Ok(n_displayed_rows) + } + + pub fn draw(&mut self, stdout: &mut StdoutLock) -> io::Result<()> { + if self.term_height == 0 { + return Ok(()); + } + + stdout.queue(BeginSynchronizedUpdate)?.queue(MoveTo(0, 0))?; + + // Header + stdout.write_all(b" Current State Name")?; + stdout.write_all(&SPACE[..self.name_col_width - 2])?; + stdout.write_all(b"Path")?; + next_ln::(stdout)?; + + let narrow = self.term_width < 95; + let show_footer = self.term_height > 6; + let max_n_rows_to_display = + (self.term_height - 1 - u16::from(show_footer) * (4 + u16::from(narrow))) as usize; + + // Rows + let iter = self.app_state.exercises().iter().enumerate(); + let n_displayed_rows = match self.filter { + Filter::Done => self.draw_rows( + stdout, + max_n_rows_to_display, + iter.filter(|(_, exercise)| exercise.done), + )?, + Filter::Pending => self.draw_rows( + stdout, + max_n_rows_to_display, + iter.filter(|(_, exercise)| !exercise.done), + )?, + Filter::None => self.draw_rows(stdout, max_n_rows_to_display, iter)?, + }; + for _ in 0..max_n_rows_to_display - n_displayed_rows { next_ln::(stdout)?; } @@ -172,7 +190,7 @@ impl<'a> ListState<'a> { next_ln::(stdout)?; if self.message.is_empty() { - // Help footer. + // Help footer stdout.write_all( "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚".as_bytes(), )?; From 5f4875e2bae07d3c8ce6505abbc67bbe447b7aa6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 25 Aug 2024 19:24:12 +0200 Subject: [PATCH 1201/1432] Almost done with list --- src/list.rs | 8 +- src/list/state.rs | 230 +++++++++++++++++++++++++++------------------- 2 files changed, 135 insertions(+), 103 deletions(-) diff --git a/src/list.rs b/src/list.rs index e3601825e3..a8e52254d2 100644 --- a/src/list.rs +++ b/src/list.rs @@ -59,9 +59,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.message.push_str(message); } - KeyCode::Char('r') => { - list_state.reset_selected()?; - } + KeyCode::Char('r') => list_state.reset_selected()?, KeyCode::Char('c') => { if list_state.selected_to_current_exercise()? { return Ok(()); @@ -77,9 +75,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> MouseEventKind::ScrollUp => list_state.select_previous(), _ => continue, }, - Event::Resize(width, height) => { - list_state.set_term_size(width, height); - } + Event::Resize(width, height) => list_state.set_term_size(width, height), // Ignore Event::FocusGained | Event::FocusLost => continue, } diff --git a/src/list/state.rs b/src/list/state.rs index cbca1d9ece..b8fdfcbcaa 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -15,20 +15,21 @@ use crate::{app_state::AppState, exercise::Exercise, term::progress_bar, MAX_EXE // +1 for padding. const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; -fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { - if CLEAR_LAST_CHAR { - // Avoids having the last written char as the last displayed one when - // the written width is higher than the terminal width. - // Happens on the Gnome terminal for example. - stdout.write_all(b" ")?; - } - +fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { stdout .queue(Clear(ClearType::UntilNewLine))? .queue(MoveToNextLine(1))?; Ok(()) } +// Avoids having the last written char as the last displayed one when the +// written width is higher than the terminal width. +// Happens on the Gnome terminal for example. +fn next_ln_overwrite(stdout: &mut StdoutLock) -> io::Result<()> { + stdout.write_all(b" ")?; + next_ln(stdout) +} + #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { Done, @@ -37,65 +38,111 @@ pub enum Filter { } pub struct ListState<'a> { + /// Footer message to be displayed if not empty. pub message: String, - filter: Filter, app_state: &'a mut AppState, - n_rows_with_filter: usize, name_col_width: usize, - offset: usize, - selected: Option, + filter: Filter, + n_rows_with_filter: usize, + /// Selected row out of the displayed ones. + selected_row: Option, term_width: u16, term_height: u16, - separator: Vec, + separator_line: Vec, + narrow_term: bool, + show_footer: bool, + max_n_rows_to_display: usize, + scroll_padding: usize, + row_offset: usize, } impl<'a> ListState<'a> { pub fn new(app_state: &'a mut AppState, stdout: &mut StdoutLock) -> io::Result { - let (term_width, term_height) = terminal::size()?; stdout.queue(Clear(ClearType::All))?; + let name_col_title_len = 4; let name_col_width = app_state .exercises() .iter() .map(|exercise| exercise.name.len()) .max() - .map_or(4, |max| max.max(4)); + .map_or(name_col_title_len, |max| max.max(name_col_title_len)); + let filter = Filter::None; let n_rows_with_filter = app_state.exercises().len(); - let selected = app_state.current_exercise_ind(); + let selected = Some(app_state.current_exercise_ind()); let mut slf = Self { message: String::with_capacity(128), - filter: Filter::None, app_state, - n_rows_with_filter, name_col_width, - offset: selected.saturating_sub(10), - selected: Some(selected), - term_width, - term_height, - separator: "─".as_bytes().repeat(term_width as usize), + filter, + n_rows_with_filter, + selected_row: selected, + // Set by `set_term_size` + term_width: 0, + term_height: 0, + separator_line: Vec::new(), + narrow_term: false, + show_footer: true, + max_n_rows_to_display: 0, + scroll_padding: 0, + // Updated by `draw` + row_offset: 0, }; + let (width, height) = terminal::size()?; + slf.set_term_size(width, height); slf.draw(stdout)?; Ok(slf) } + pub fn set_term_size(&mut self, width: u16, height: u16) { + self.term_width = width; + self.term_height = height; + + self.separator_line = "─".as_bytes().repeat(width as usize); + + self.narrow_term = width < 95 && self.selected_row.is_some(); + self.show_footer = height > 6; + self.max_n_rows_to_display = + (height - 1 - u16::from(self.show_footer) * (4 + u16::from(self.narrow_term))) as usize; + self.scroll_padding = (self.max_n_rows_to_display / 4).min(5); + } + + fn update_offset(&mut self) { + let Some(selected) = self.selected_row else { + return; + }; + + let min_offset = (selected + self.scroll_padding) + .saturating_sub(self.max_n_rows_to_display.saturating_sub(1)); + let max_offset = selected.saturating_sub(self.scroll_padding); + let global_max_offset = self + .n_rows_with_filter + .saturating_sub(self.max_n_rows_to_display); + + self.row_offset = self + .row_offset + .max(min_offset) + .min(max_offset) + .min(global_max_offset); + } + fn draw_rows( &self, stdout: &mut StdoutLock, - max_n_rows_to_display: usize, filtered_exercises: impl Iterator, ) -> io::Result { let current_exercise_ind = self.app_state.current_exercise_ind(); let mut n_displayed_rows = 0; for (exercise_ind, exercise) in filtered_exercises - .skip(self.offset) - .take(max_n_rows_to_display) + .skip(self.row_offset) + .take(self.max_n_rows_to_display) { - if self.selected == Some(n_displayed_rows) { + if self.selected_row == Some(self.row_offset + n_displayed_rows) { stdout.queue(SetBackgroundColor(Color::Rgb { r: 50, g: 50, @@ -128,7 +175,7 @@ impl<'a> ListState<'a> { stdout.write_all(exercise.path.as_bytes())?; - next_ln::(stdout)?; + next_ln_overwrite(stdout)?; stdout.queue(ResetColor)?; n_displayed_rows += 1; } @@ -147,36 +194,27 @@ impl<'a> ListState<'a> { stdout.write_all(b" Current State Name")?; stdout.write_all(&SPACE[..self.name_col_width - 2])?; stdout.write_all(b"Path")?; - next_ln::(stdout)?; + next_ln_overwrite(stdout)?; - let narrow = self.term_width < 95; - let show_footer = self.term_height > 6; - let max_n_rows_to_display = - (self.term_height - 1 - u16::from(show_footer) * (4 + u16::from(narrow))) as usize; + self.update_offset(); // Rows let iter = self.app_state.exercises().iter().enumerate(); let n_displayed_rows = match self.filter { - Filter::Done => self.draw_rows( - stdout, - max_n_rows_to_display, - iter.filter(|(_, exercise)| exercise.done), - )?, - Filter::Pending => self.draw_rows( - stdout, - max_n_rows_to_display, - iter.filter(|(_, exercise)| !exercise.done), - )?, - Filter::None => self.draw_rows(stdout, max_n_rows_to_display, iter)?, + Filter::Done => self.draw_rows(stdout, iter.filter(|(_, exercise)| exercise.done))?, + Filter::Pending => { + self.draw_rows(stdout, iter.filter(|(_, exercise)| !exercise.done))? + } + Filter::None => self.draw_rows(stdout, iter)?, }; - for _ in 0..max_n_rows_to_display - n_displayed_rows { - next_ln::(stdout)?; + for _ in 0..self.max_n_rows_to_display - n_displayed_rows { + next_ln(stdout)?; } - if show_footer { - stdout.write_all(&self.separator)?; - next_ln::(stdout)?; + if self.show_footer { + stdout.write_all(&self.separator_line)?; + next_ln(stdout)?; progress_bar( stdout, @@ -184,21 +222,25 @@ impl<'a> ListState<'a> { self.app_state.exercises().len() as u16, self.term_width, )?; - next_ln::(stdout)?; + next_ln(stdout)?; - stdout.write_all(&self.separator)?; - next_ln::(stdout)?; + stdout.write_all(&self.separator_line)?; + next_ln(stdout)?; if self.message.is_empty() { // Help footer - stdout.write_all( - "↓/j ↑/k home/g end/G β”‚ ontinue at β”‚ eset exercise β”‚".as_bytes(), - )?; - if narrow { - next_ln::(stdout)?; - stdout.write_all(b"filter ")?; + if self.selected_row.is_some() { + stdout.write_all( + "↓/j ↑/k home/g end/G | ontinue at | eset exercise".as_bytes(), + )?; + if self.narrow_term { + next_ln_overwrite(stdout)?; + stdout.write_all(b"filter ")?; + } else { + stdout.write_all(b" | filter ")?; + } } else { - stdout.write_all(b" filter ")?; + stdout.write_all(b"filter ")?; } match self.filter { @@ -220,19 +262,19 @@ impl<'a> ListState<'a> { } Filter::None => stdout.write_all(b"one/

ending")?, } - stdout.write_all(" β”‚ uit list".as_bytes())?; - if narrow { - next_ln::(stdout)?; + stdout.write_all(b" | uit list")?; + if self.narrow_term { + next_ln_overwrite(stdout)?; } else { - next_ln::(stdout)?; + next_ln(stdout)?; } } else { stdout.queue(SetForegroundColor(Color::Magenta))?; stdout.write_all(self.message.as_bytes())?; stdout.queue(ResetColor)?; - next_ln::(stdout)?; - if narrow { - next_ln::(stdout)?; + next_ln_overwrite(stdout)?; + if self.narrow_term { + next_ln(stdout)?; } } } @@ -240,20 +282,8 @@ impl<'a> ListState<'a> { stdout.queue(EndSynchronizedUpdate)?.flush() } - pub fn set_term_size(&mut self, width: u16, height: u16) { - self.term_width = width; - self.term_height = height; - self.separator = "─".as_bytes().repeat(width as usize); - } - - #[inline] - pub fn filter(&self) -> Filter { - self.filter - } - - pub fn set_filter(&mut self, filter: Filter) { - self.filter = filter; - self.n_rows_with_filter = match filter { + fn update_rows(&mut self) { + self.n_rows_with_filter = match self.filter { Filter::Done => self .app_state .exercises() @@ -270,42 +300,46 @@ impl<'a> ListState<'a> { }; if self.n_rows_with_filter == 0 { - self.selected = None; + self.selected_row = None; } else { - self.selected = Some( - self.selected + self.selected_row = Some( + self.selected_row .map_or(0, |selected| selected.min(self.n_rows_with_filter - 1)), ); } } + #[inline] + pub fn filter(&self) -> Filter { + self.filter + } + + pub fn set_filter(&mut self, filter: Filter) { + self.filter = filter; + self.update_rows(); + } + pub fn select_next(&mut self) { - if self.n_rows_with_filter > 0 { - let next = self.selected.map_or(0, |selected| { - (selected + 1).min(self.n_rows_with_filter - 1) - }); - self.selected = Some(next); + if let Some(selected) = self.selected_row { + self.selected_row = Some((selected + 1).min(self.n_rows_with_filter - 1)); } } pub fn select_previous(&mut self) { - if self.n_rows_with_filter > 0 { - let previous = self - .selected - .map_or(0, |selected| selected.saturating_sub(1)); - self.selected = Some(previous); + if let Some(selected) = self.selected_row { + self.selected_row = Some(selected.saturating_sub(1)); } } pub fn select_first(&mut self) { if self.n_rows_with_filter > 0 { - self.selected = Some(0); + self.selected_row = Some(0); } } pub fn select_last(&mut self) { if self.n_rows_with_filter > 0 { - self.selected = Some(self.n_rows_with_filter - 1); + self.selected_row = Some(self.n_rows_with_filter - 1); } } @@ -334,13 +368,14 @@ impl<'a> ListState<'a> { } pub fn reset_selected(&mut self) -> Result<()> { - let Some(selected) = self.selected else { + let Some(selected) = self.selected_row else { self.message.push_str("Nothing selected to reset!"); return Ok(()); }; let exercise_ind = self.selected_to_exercise_ind(selected)?; let exercise_path = self.app_state.reset_exercise_by_ind(exercise_ind)?; + self.update_rows(); write!(self.message, "The exercise {exercise_path} has been reset")?; Ok(()) @@ -348,13 +383,14 @@ impl<'a> ListState<'a> { // Return `true` if there was something to select. pub fn selected_to_current_exercise(&mut self) -> Result { - let Some(selected) = self.selected else { + let Some(selected) = self.selected_row else { self.message.push_str("Nothing selected to continue at!"); return Ok(false); }; let exercise_ind = self.selected_to_exercise_ind(selected)?; self.app_state.set_current_exercise_ind(exercise_ind)?; + Ok(true) } } From 64772544fad6788fd3fce5db3f357dba6f2d8d23 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 25 Aug 2024 20:29:54 +0200 Subject: [PATCH 1202/1432] Final touches :D --- src/app_state.rs | 3 +- src/list/state.rs | 95 ++++++++++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index b72469c4df..8fd8f3be62 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -271,6 +271,7 @@ impl AppState { Ok(exercise.path) } + // Reset the exercise by index and return its name. pub fn reset_exercise_by_ind(&mut self, exercise_ind: usize) -> Result<&'static str> { if exercise_ind >= self.exercises.len() { bail!(BAD_INDEX_ERR); @@ -280,7 +281,7 @@ impl AppState { let exercise = &self.exercises[exercise_ind]; self.reset(exercise_ind, exercise.path)?; - Ok(exercise.path) + Ok(exercise.name) } // Return the index of the next pending exercise or `None` if all exercises are done. diff --git a/src/list/state.rs b/src/list/state.rs index b8fdfcbcaa..25ca1deded 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -12,7 +12,8 @@ use std::{ use crate::{app_state::AppState, exercise::Exercise, term::progress_bar, MAX_EXERCISE_NAME_LEN}; -// +1 for padding. +const MAX_SCROLL_PADDING: usize = 8; +// +1 for column padding. const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { @@ -44,8 +45,9 @@ pub struct ListState<'a> { name_col_width: usize, filter: Filter, n_rows_with_filter: usize, - /// Selected row out of the displayed ones. + /// Selected row out of the filtered ones. selected_row: Option, + row_offset: usize, term_width: u16, term_height: u16, separator_line: Vec, @@ -53,7 +55,6 @@ pub struct ListState<'a> { show_footer: bool, max_n_rows_to_display: usize, scroll_padding: usize, - row_offset: usize, } impl<'a> ListState<'a> { @@ -70,7 +71,7 @@ impl<'a> ListState<'a> { let filter = Filter::None; let n_rows_with_filter = app_state.exercises().len(); - let selected = Some(app_state.current_exercise_ind()); + let selected = app_state.current_exercise_ind(); let mut slf = Self { message: String::with_capacity(128), @@ -78,7 +79,8 @@ impl<'a> ListState<'a> { name_col_width, filter, n_rows_with_filter, - selected_row: selected, + selected_row: Some(selected), + row_offset: selected.saturating_sub(MAX_SCROLL_PADDING), // Set by `set_term_size` term_width: 0, term_height: 0, @@ -87,8 +89,6 @@ impl<'a> ListState<'a> { show_footer: true, max_n_rows_to_display: 0, scroll_padding: 0, - // Updated by `draw` - row_offset: 0, }; let (width, height) = terminal::size()?; @@ -98,19 +98,6 @@ impl<'a> ListState<'a> { Ok(slf) } - pub fn set_term_size(&mut self, width: u16, height: u16) { - self.term_width = width; - self.term_height = height; - - self.separator_line = "─".as_bytes().repeat(width as usize); - - self.narrow_term = width < 95 && self.selected_row.is_some(); - self.show_footer = height > 6; - self.max_n_rows_to_display = - (height - 1 - u16::from(self.show_footer) * (4 + u16::from(self.narrow_term))) as usize; - self.scroll_padding = (self.max_n_rows_to_display / 4).min(5); - } - fn update_offset(&mut self) { let Some(selected) = self.selected_row else { return; @@ -130,6 +117,36 @@ impl<'a> ListState<'a> { .min(global_max_offset); } + pub fn set_term_size(&mut self, width: u16, height: u16) { + self.term_width = width; + self.term_height = height; + + if height == 0 { + return; + } + + let wide_help_footer_width = 95; + // The help footer is shorter when nothing is selected. + self.narrow_term = width < wide_help_footer_width && self.selected_row.is_some(); + + let header_height = 1; + // 2 separator, 1 progress bar, 1-2 footer message. + let footer_height = 4 + u16::from(self.narrow_term); + self.show_footer = height > header_height + footer_height; + + if self.show_footer { + self.separator_line = "─".as_bytes().repeat(width as usize); + } + + self.max_n_rows_to_display = height + .saturating_sub(header_height + u16::from(self.show_footer) * footer_height) + as usize; + + self.scroll_padding = (self.max_n_rows_to_display / 4).min(MAX_SCROLL_PADDING); + + self.update_offset(); + } + fn draw_rows( &self, stdout: &mut StdoutLock, @@ -196,8 +213,6 @@ impl<'a> ListState<'a> { stdout.write_all(b"Path")?; next_ln_overwrite(stdout)?; - self.update_offset(); - // Rows let iter = self.app_state.exercises().iter().enumerate(); let n_displayed_rows = match self.filter { @@ -228,7 +243,7 @@ impl<'a> ListState<'a> { next_ln(stdout)?; if self.message.is_empty() { - // Help footer + // Help footer message if self.selected_row.is_some() { stdout.write_all( "↓/j ↑/k home/g end/G | ontinue at | eset exercise".as_bytes(), @@ -240,6 +255,7 @@ impl<'a> ListState<'a> { stdout.write_all(b" | filter ")?; } } else { + // Nothing selected (and nothing shown), so only display filter and quit. stdout.write_all(b"filter ")?; } @@ -262,7 +278,9 @@ impl<'a> ListState<'a> { } Filter::None => stdout.write_all(b"one/

ending")?, } + stdout.write_all(b" | uit list")?; + if self.narrow_term { next_ln_overwrite(stdout)?; } else { @@ -282,6 +300,11 @@ impl<'a> ListState<'a> { stdout.queue(EndSynchronizedUpdate)?.flush() } + fn set_selected(&mut self, selected: usize) { + self.selected_row = Some(selected); + self.update_offset(); + } + fn update_rows(&mut self) { self.n_rows_with_filter = match self.filter { Filter::Done => self @@ -301,12 +324,13 @@ impl<'a> ListState<'a> { if self.n_rows_with_filter == 0 { self.selected_row = None; - } else { - self.selected_row = Some( - self.selected_row - .map_or(0, |selected| selected.min(self.n_rows_with_filter - 1)), - ); + return; } + + self.set_selected( + self.selected_row + .map_or(0, |selected| selected.min(self.n_rows_with_filter - 1)), + ); } #[inline] @@ -321,25 +345,25 @@ impl<'a> ListState<'a> { pub fn select_next(&mut self) { if let Some(selected) = self.selected_row { - self.selected_row = Some((selected + 1).min(self.n_rows_with_filter - 1)); + self.set_selected((selected + 1).min(self.n_rows_with_filter - 1)); } } pub fn select_previous(&mut self) { if let Some(selected) = self.selected_row { - self.selected_row = Some(selected.saturating_sub(1)); + self.set_selected(selected.saturating_sub(1)); } } pub fn select_first(&mut self) { if self.n_rows_with_filter > 0 { - self.selected_row = Some(0); + self.set_selected(0); } } pub fn select_last(&mut self) { if self.n_rows_with_filter > 0 { - self.selected_row = Some(self.n_rows_with_filter - 1); + self.set_selected(self.n_rows_with_filter - 1); } } @@ -374,9 +398,12 @@ impl<'a> ListState<'a> { }; let exercise_ind = self.selected_to_exercise_ind(selected)?; - let exercise_path = self.app_state.reset_exercise_by_ind(exercise_ind)?; + let exercise_name = self.app_state.reset_exercise_by_ind(exercise_ind)?; self.update_rows(); - write!(self.message, "The exercise {exercise_path} has been reset")?; + write!( + self.message, + "The exercise `{exercise_name}` has been reset", + )?; Ok(()) } From d29e9e7e07a16adda35aea9ce9dd120b6ecc9dfc Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 25 Aug 2024 20:42:13 +0200 Subject: [PATCH 1203/1432] Update deps --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93b2051be2..048a82ba31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "filetime" @@ -529,18 +529,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", @@ -549,9 +549,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", "memchr", @@ -612,9 +612,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.75" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4b3e98c415..5eb25b491a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.208", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] } [package] @@ -53,7 +53,7 @@ crossterm = { version = "0.28.1", default-features = false, features = ["windows notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" } -serde_json = "1.0.125" +serde_json = "1.0.127" serde.workspace = true toml_edit.workspace = true From b1898f6d8b2c2ae45279ca4c67fa1b1a94acb936 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 25 Aug 2024 23:53:50 +0200 Subject: [PATCH 1204/1432] Use queue instead of Stylize --- src/app_state.rs | 18 ++++---- src/exercise.rs | 49 ++++++++++---------- src/init.rs | 23 +++++---- src/main.rs | 1 - src/run.rs | 54 +++++++++++----------- src/term.rs | 49 ++++++++++++++++++-- src/terminal_link.rs | 26 ----------- src/watch/state.rs | 108 +++++++++++++++++++++++++++---------------- 8 files changed, 189 insertions(+), 139 deletions(-) delete mode 100644 src/terminal_link.rs diff --git a/src/app_state.rs b/src/app_state.rs index 8fd8f3be62..d7de1fdbd7 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -338,7 +338,7 @@ impl AppState { /// Mark the current exercise as done and move on to the next pending exercise if one exists. /// If all exercises are marked as done, run all of them to make sure that they are actually /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. - pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { + pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result { let exercise = &mut self.exercises[self.current_exercise_ind]; if !exercise.done { exercise.done = true; @@ -350,7 +350,7 @@ impl AppState { return Ok(ExercisesProgress::NewPending); } - writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; + stdout.write_all(RERUNNING_ALL_EXERCISES_MSG)?; let n_exercises = self.exercises.len(); @@ -368,12 +368,12 @@ impl AppState { .collect::>(); for (exercise_ind, handle) in handles.into_iter().enumerate() { - write!(writer, "\rProgress: {exercise_ind}/{n_exercises}")?; - writer.flush()?; + write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; + stdout.flush()?; let success = handle.join().unwrap()?; if !success { - writer.write_all(b"\n\n")?; + stdout.write_all(b"\n\n")?; return Ok(Some(exercise_ind)); } } @@ -395,13 +395,13 @@ impl AppState { // Write that the last exercise is done. self.write()?; - clear_terminal(writer)?; - writer.write_all(FENISH_LINE.as_bytes())?; + clear_terminal(stdout)?; + stdout.write_all(FENISH_LINE.as_bytes())?; let final_message = self.final_message.trim_ascii(); if !final_message.is_empty() { - writer.write_all(final_message.as_bytes())?; - writer.write_all(b"\n")?; + stdout.write_all(final_message.as_bytes())?; + stdout.write_all(b"\n")?; } Ok(ExercisesProgress::AllDone) diff --git a/src/exercise.rs b/src/exercise.rs index ac5c6e6e45..462287db37 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,15 +1,27 @@ use anyhow::Result; -use crossterm::style::{style, StyledContent, Stylize}; -use std::{ - fmt::{self, Display, Formatter}, - io::Write, +use crossterm::{ + style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, + QueueableCommand, }; +use std::io::{self, StdoutLock, Write}; -use crate::{cmd::CmdRunner, terminal_link::TerminalFileLink}; +use crate::{ + cmd::CmdRunner, + term::{terminal_file_link, write_ansi}, +}; /// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; +pub fn solution_link_line(stdout: &mut StdoutLock, solution_path: &str) -> io::Result<()> { + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"Solution")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" for comparison: ")?; + terminal_file_link(stdout, solution_path, Color::Cyan)?; + stdout.write_all(b"\n") +} + // Run an exercise binary and append its output to the `output` buffer. // Compilation must be done before calling this method. fn run_bin( @@ -18,7 +30,9 @@ fn run_bin( cmd_runner: &CmdRunner, ) -> Result { if let Some(output) = output.as_deref_mut() { - writeln!(output, "{}", "Output".underlined())?; + write_ansi(output, SetAttribute(Attribute::Underlined)); + output.extend_from_slice(b"Output\n"); + write_ansi(output, ResetColor); } let success = cmd_runner.run_debug_bin(bin_name, output.as_deref_mut())?; @@ -28,13 +42,10 @@ fn run_bin( // This output is important to show the user that something went wrong. // Otherwise, calling something like `exit(1)` in an exercise without further output // leaves the user confused about why the exercise isn't done yet. - writeln!( - output, - "{}", - "The exercise didn't run successfully (nonzero exit code)" - .bold() - .red(), - )?; + write_ansi(output, SetAttribute(Attribute::Bold)); + write_ansi(output, SetForegroundColor(Color::Red)); + output.extend_from_slice(b"The exercise didn't run successfully (nonzero exit code)\n"); + write_ansi(output, ResetColor); } } @@ -53,18 +64,6 @@ pub struct Exercise { pub done: bool, } -impl Exercise { - pub fn terminal_link(&self) -> StyledContent> { - style(TerminalFileLink(self.path)).underlined().blue() - } -} - -impl Display for Exercise { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.path.fmt(f) - } -} - pub trait RunnableExercise { fn name(&self) -> &str; fn strict_clippy(&self) -> bool; diff --git a/src/init.rs b/src/init.rs index 2c172dcaba..40d9910d26 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,5 +1,8 @@ use anyhow::{bail, Context, Result}; -use crossterm::style::Stylize; +use crossterm::{ + style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, + QueueableCommand, +}; use serde::Deserialize; use std::{ env::set_current_dir, @@ -144,12 +147,13 @@ pub fn init() -> Result<()> { .status(); } - writeln!( - stdout, - "{}\n\n{}", - "Initialization done βœ“".green(), - POST_INIT_MSG.bold(), - )?; + stdout.queue(SetForegroundColor(Color::Green))?; + stdout.write_all("Initialization done βœ“\n\n".as_bytes())?; + stdout + .queue(ResetColor)? + .queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(POST_INIT_MSG)?; + stdout.queue(ResetColor)?; Ok(()) } @@ -182,5 +186,6 @@ You probably already initialized Rustlings. Run `cd rustlings` Then run `rustlings` again"; -const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory. -Then run `rustlings` to get started."; +const POST_INIT_MSG: &[u8] = b"Run `cd rustlings` to go into the generated directory. +Then run `rustlings` to get started. +"; diff --git a/src/main.rs b/src/main.rs index 61dd8ea8db..998d3d118f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,6 @@ mod init; mod list; mod run; mod term; -mod terminal_link; mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; diff --git a/src/run.rs b/src/run.rs index 09e53ec9e1..929b4751d0 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,11 +1,17 @@ -use anyhow::{bail, Result}; -use crossterm::style::{style, Stylize}; -use std::io::{self, Write}; +use anyhow::Result; +use crossterm::{ + style::{Color, ResetColor, SetForegroundColor}, + QueueableCommand, +}; +use std::{ + io::{self, Write}, + process::exit, +}; use crate::{ app_state::{AppState, ExercisesProgress}, - exercise::{RunnableExercise, OUTPUT_CAPACITY}, - terminal_link::TerminalFileLink, + exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, + term::terminal_file_link, }; pub fn run(app_state: &mut AppState) -> Result<()> { @@ -19,35 +25,31 @@ pub fn run(app_state: &mut AppState) -> Result<()> { if !success { app_state.set_pending(app_state.current_exercise_ind())?; - bail!( - "Ran {} with errors", - app_state.current_exercise().terminal_link(), - ); + stdout.write_all(b"Ran ")?; + terminal_file_link(&mut stdout, app_state.current_exercise().path, Color::Blue)?; + stdout.write_all(b" with errors\n")?; + exit(1); } - writeln!( - stdout, - "{}{}", - "βœ“ Successfully ran ".green(), - exercise.path.green(), - )?; + stdout.queue(SetForegroundColor(Color::Green))?; + stdout.write_all("βœ“ Successfully ran ".as_bytes())?; + stdout.write_all(exercise.path.as_bytes())?; + stdout.queue(ResetColor)?; + stdout.write_all(b"\n")?; if let Some(solution_path) = app_state.current_solution_path()? { - writeln!( - stdout, - "\n{} for comparison: {}\n", - "Solution".bold(), - style(TerminalFileLink(&solution_path)).underlined().cyan(), - )?; + stdout.write_all(b"\n")?; + solution_link_line(&mut stdout, &solution_path)?; + stdout.write_all(b"\n")?; } match app_state.done_current_exercise(&mut stdout)? { + ExercisesProgress::CurrentPending | ExercisesProgress::NewPending => { + stdout.write_all(b"Next exercise: ")?; + terminal_file_link(&mut stdout, app_state.current_exercise().path, Color::Blue)?; + stdout.write_all(b"\n")?; + } ExercisesProgress::AllDone => (), - ExercisesProgress::CurrentPending | ExercisesProgress::NewPending => writeln!( - stdout, - "Next exercise: {}", - app_state.current_exercise().terminal_link(), - )?, } Ok(()) diff --git a/src/term.rs b/src/term.rs index b993108ea4..4c6ac90481 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,10 +1,13 @@ -use std::io::{self, BufRead, StdoutLock, Write}; +use std::{ + fmt, fs, + io::{self, BufRead, StdoutLock, Write}, +}; use crossterm::{ cursor::MoveTo, - style::{Color, ResetColor, SetForegroundColor}, + style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, terminal::{Clear, ClearType}, - QueueableCommand, + Command, QueueableCommand, }; /// Terminal progress bar to be used when not using Ratataui. @@ -68,3 +71,43 @@ pub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> { stdout.write_all(b"\n")?; Ok(()) } + +pub fn terminal_file_link(stdout: &mut StdoutLock, path: &str, color: Color) -> io::Result<()> { + let canonical_path = fs::canonicalize(path).ok(); + + let Some(canonical_path) = canonical_path.as_deref().and_then(|p| p.to_str()) else { + return stdout.write_all(path.as_bytes()); + }; + + // Windows itself can't handle its verbatim paths. + #[cfg(windows)] + let canonical_path = if canonical_path.len() > 5 && &canonical_path[0..4] == r"\\?\" { + &canonical_path[4..] + } else { + canonical_path + }; + + stdout + .queue(SetForegroundColor(color))? + .queue(SetAttribute(Attribute::Underlined))?; + write!( + stdout, + "\x1b]8;;file://{canonical_path}\x1b\\{path}\x1b]8;;\x1b\\", + )?; + stdout.queue(ResetColor)?; + + Ok(()) +} + +pub fn write_ansi(output: &mut Vec, command: impl Command) { + struct FmtWriter<'a>(&'a mut Vec); + + impl fmt::Write for FmtWriter<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0.extend_from_slice(s.as_bytes()); + Ok(()) + } + } + + let _ = command.write_ansi(&mut FmtWriter(output)); +} diff --git a/src/terminal_link.rs b/src/terminal_link.rs deleted file mode 100644 index 9bea07d9ee..0000000000 --- a/src/terminal_link.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::{ - fmt::{self, Display, Formatter}, - fs, -}; - -pub struct TerminalFileLink<'a>(pub &'a str); - -impl<'a> Display for TerminalFileLink<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let path = fs::canonicalize(self.0); - - if let Some(path) = path.as_deref().ok().and_then(|path| path.to_str()) { - // Windows itself can't handle its verbatim paths. - #[cfg(windows)] - let path = if path.len() > 5 && &path[0..4] == r"\\?\" { - &path[4..] - } else { - path - }; - - write!(f, "\x1b]8;;file://{path}\x1b\\{}\x1b]8;;\x1b\\", self.0) - } else { - write!(f, "{}", self.0) - } - } -} diff --git a/src/watch/state.rs b/src/watch/state.rs index 40e3d3ece7..f9fd1389e0 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,16 +1,17 @@ use anyhow::Result; use crossterm::{ - style::{style, Stylize}, - terminal, + style::{ + Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor, + }, + terminal, QueueableCommand, }; use std::io::{self, StdoutLock, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, clear_terminal, - exercise::{RunnableExercise, OUTPUT_CAPACITY}, - term::progress_bar, - terminal_link::TerminalFileLink, + exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, + term::{progress_bar, terminal_file_link}, }; #[derive(PartialEq, Eq)] @@ -21,7 +22,7 @@ enum DoneStatus { } pub struct WatchState<'a> { - writer: StdoutLock<'a>, + stdout: StdoutLock<'a>, app_state: &'a mut AppState, output: Vec, show_hint: bool, @@ -31,10 +32,11 @@ pub struct WatchState<'a> { impl<'a> WatchState<'a> { pub fn new(app_state: &'a mut AppState, manual_run: bool) -> Self { - let writer = io::stdout().lock(); + // TODO: Take stdout as arg. + let stdout = io::stdout().lock(); Self { - writer, + stdout, app_state, output: Vec::with_capacity(OUTPUT_CAPACITY), show_hint: false, @@ -45,14 +47,14 @@ impl<'a> WatchState<'a> { #[inline] pub fn into_writer(self) -> StdoutLock<'a> { - self.writer + self.stdout } pub fn run_current_exercise(&mut self) -> Result<()> { self.show_hint = false; writeln!( - self.writer, + self.stdout, "\nChecking the exercise `{}`. Please wait…", self.app_state.current_exercise().name, )?; @@ -98,75 +100,101 @@ impl<'a> WatchState<'a> { return Ok(ExercisesProgress::CurrentPending); } - self.app_state.done_current_exercise(&mut self.writer) + self.app_state.done_current_exercise(&mut self.stdout) } fn show_prompt(&mut self) -> io::Result<()> { - self.writer.write_all(b"\n")?; - if self.manual_run { - write!(self.writer, "{}:run / ", 'r'.bold())?; + self.stdout.queue(SetAttribute(Attribute::Bold))?; + self.stdout.write_all(b"r")?; + self.stdout.queue(ResetColor)?; + self.stdout.write_all(b":run / ")?; } if self.done_status != DoneStatus::Pending { - write!(self.writer, "{}:{} / ", 'n'.bold(), "next".underlined())?; + self.stdout.queue(SetAttribute(Attribute::Bold))?; + self.stdout.write_all(b"n")?; + self.stdout.queue(ResetColor)?; + self.stdout.write_all(b":")?; + self.stdout.queue(SetAttribute(Attribute::Underlined))?; + self.stdout.write_all(b"next")?; + self.stdout.queue(ResetColor)?; + self.stdout.write_all(b" / ")?; } if !self.show_hint { - write!(self.writer, "{}:hint / ", 'h'.bold())?; + self.stdout.queue(SetAttribute(Attribute::Bold))?; + self.stdout.write_all(b"h")?; + self.stdout.queue(ResetColor)?; + self.stdout.write_all(b":hint / ")?; } - write!(self.writer, "{}:list / {}:quit ? ", 'l'.bold(), 'q'.bold())?; + self.stdout.queue(SetAttribute(Attribute::Bold))?; + self.stdout.write_all(b"l")?; + self.stdout.queue(ResetColor)?; + self.stdout.write_all(b":list / ")?; + + self.stdout.queue(SetAttribute(Attribute::Bold))?; + self.stdout.write_all(b"q")?; + self.stdout.queue(ResetColor)?; + self.stdout.write_all(b":quit ? ")?; - self.writer.flush() + self.stdout.flush() } pub fn render(&mut self) -> io::Result<()> { // Prevent having the first line shifted if clearing wasn't successful. - self.writer.write_all(b"\n")?; - clear_terminal(&mut self.writer)?; + self.stdout.write_all(b"\n")?; + clear_terminal(&mut self.stdout)?; - self.writer.write_all(&self.output)?; + self.stdout.write_all(&self.output)?; if self.show_hint { - writeln!( - self.writer, - "{}\n{}\n", - "Hint".bold().cyan().underlined(), - self.app_state.current_exercise().hint, - )?; + self.stdout + .queue(SetAttributes( + Attributes::from(Attribute::Bold).with(Attribute::Underlined), + ))? + .queue(SetForegroundColor(Color::Cyan))?; + self.stdout.write_all(b"Hint\n")?; + self.stdout.queue(ResetColor)?; + + self.stdout + .write_all(self.app_state.current_exercise().hint.as_bytes())?; + self.stdout.write_all(b"\n\n")?; } if self.done_status != DoneStatus::Pending { - writeln!(self.writer, "{}", "Exercise done βœ“".bold().green())?; + self.stdout + .queue(SetAttribute(Attribute::Bold))? + .queue(SetForegroundColor(Color::Green))?; + self.stdout.write_all("Exercise done βœ“\n".as_bytes())?; + self.stdout.queue(ResetColor)?; if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { - writeln!( - self.writer, - "{} for comparison: {}", - "Solution".bold(), - style(TerminalFileLink(solution_path)).underlined().cyan(), - )?; + solution_link_line(&mut self.stdout, solution_path)?; } writeln!( - self.writer, + self.stdout, "When done experimenting, enter `n` to move on to the next exercise πŸ¦€\n", )?; } let line_width = terminal::size()?.0; progress_bar( - &mut self.writer, + &mut self.stdout, self.app_state.n_done(), self.app_state.exercises().len() as u16, line_width, )?; - writeln!( - self.writer, - "\nCurrent exercise: {}", - self.app_state.current_exercise().terminal_link(), + + self.stdout.write_all(b"\nCurrent exercise: ")?; + terminal_file_link( + &mut self.stdout, + self.app_state.current_exercise().path, + Color::Blue, )?; + self.stdout.write_all(b"\n\n")?; self.show_prompt()?; From a1f0eaab549300dbd7d2dbd85cf11aba34f57c2d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 25 Aug 2024 23:54:04 +0200 Subject: [PATCH 1205/1432] Add disallowed types and methods in Clippy --- Cargo.toml | 2 ++ clippy.toml | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 clippy.toml diff --git a/Cargo.toml b/Cargo.toml index 5eb25b491a..b332f40bb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,8 @@ unstable_features = "forbid" [workspace.lints.clippy] empty_loop = "forbid" +disallowed-types = "deny" +disallowed-methods = "deny" infinite_loop = "deny" mem_forget = "deny" dbg_macro = "warn" diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000000..81e372a747 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,13 @@ +disallowed-types = [ + # Inefficient. Use `.queue(…)` instead. + "crossterm::style::Stylize", + "crossterm::style::styled_content::StyledContent", +] + +disallowed-methods = [ + # We use `ahash` instead of the default hasher. + "std::collections::HashSet::new", + "std::collections::HashSet::with_capacity", + # Inefficient. Use `.queue(…)` instead. + "crossterm::style::style", +] From 631f2db1a31ce5a32bc954412a7cf42158046113 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 25 Aug 2024 23:54:18 +0200 Subject: [PATCH 1206/1432] Lower the maximum scroll padding --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index 25ca1deded..756814f6d1 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -12,7 +12,7 @@ use std::{ use crate::{app_state::AppState, exercise::Exercise, term::progress_bar, MAX_EXERCISE_NAME_LEN}; -const MAX_SCROLL_PADDING: usize = 8; +const MAX_SCROLL_PADDING: usize = 5; // +1 for column padding. const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; From 159273e53291cd72d27795cd2cfe0820587e3009 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 00:09:04 +0200 Subject: [PATCH 1207/1432] Take stdout as argument in watch mode --- src/watch.rs | 25 ++++----- src/watch/state.rs | 127 +++++++++++++++++++++------------------------ 2 files changed, 69 insertions(+), 83 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index c669030494..e14d3c5771 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -72,35 +72,32 @@ pub fn watch( let mut watch_state = WatchState::new(app_state, manual_run); - watch_state.run_current_exercise()?; + let mut stdout = io::stdout().lock(); + watch_state.run_current_exercise(&mut stdout)?; thread::spawn(move || terminal_event_handler(tx, manual_run)); while let Ok(event) = rx.recv() { match event { - WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise()? { + WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise(&mut stdout)? { ExercisesProgress::AllDone => break, - ExercisesProgress::CurrentPending => watch_state.render()?, - ExercisesProgress::NewPending => watch_state.run_current_exercise()?, + ExercisesProgress::CurrentPending => watch_state.render(&mut stdout)?, + ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?, }, - WatchEvent::Input(InputEvent::Hint) => { - watch_state.show_hint()?; - } + WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?, WatchEvent::Input(InputEvent::List) => { return Ok(WatchExit::List); } WatchEvent::Input(InputEvent::Quit) => { - watch_state.into_writer().write_all(QUIT_MSG)?; + stdout.write_all(QUIT_MSG)?; break; } - WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise()?, - WatchEvent::Input(InputEvent::Unrecognized) => watch_state.render()?, + WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?, + WatchEvent::Input(InputEvent::Unrecognized) => watch_state.render(&mut stdout)?, WatchEvent::FileChange { exercise_ind } => { - watch_state.handle_file_change(exercise_ind)?; - } - WatchEvent::TerminalResize => { - watch_state.render()?; + watch_state.handle_file_change(exercise_ind, &mut stdout)?; } + WatchEvent::TerminalResize => watch_state.render(&mut stdout)?, WatchEvent::NotifyErr(e) => { return Err(Error::from(e).context(NOTIFY_ERR)); } diff --git a/src/watch/state.rs b/src/watch/state.rs index f9fd1389e0..47af9193a5 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -22,7 +22,6 @@ enum DoneStatus { } pub struct WatchState<'a> { - stdout: StdoutLock<'a>, app_state: &'a mut AppState, output: Vec, show_hint: bool, @@ -32,11 +31,7 @@ pub struct WatchState<'a> { impl<'a> WatchState<'a> { pub fn new(app_state: &'a mut AppState, manual_run: bool) -> Self { - // TODO: Take stdout as arg. - let stdout = io::stdout().lock(); - Self { - stdout, app_state, output: Vec::with_capacity(OUTPUT_CAPACITY), show_hint: false, @@ -45,16 +40,11 @@ impl<'a> WatchState<'a> { } } - #[inline] - pub fn into_writer(self) -> StdoutLock<'a> { - self.stdout - } - - pub fn run_current_exercise(&mut self) -> Result<()> { + pub fn run_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> { self.show_hint = false; writeln!( - self.stdout, + stdout, "\nChecking the exercise `{}`. Please wait…", self.app_state.current_exercise().name, )?; @@ -78,11 +68,15 @@ impl<'a> WatchState<'a> { self.done_status = DoneStatus::Pending; } - self.render()?; + self.render(stdout)?; Ok(()) } - pub fn handle_file_change(&mut self, exercise_ind: usize) -> Result<()> { + pub fn handle_file_change( + &mut self, + exercise_ind: usize, + stdout: &mut StdoutLock, + ) -> Result<()> { // Don't skip exercises on file changes to avoid confusion from missing exercises. // Skipping exercises must be explicit in the interactive list. // But going back to an earlier exercise on file change is fine. @@ -91,118 +85,113 @@ impl<'a> WatchState<'a> { } self.app_state.set_current_exercise_ind(exercise_ind)?; - self.run_current_exercise() + self.run_current_exercise(stdout) } /// Move on to the next exercise if the current one is done. - pub fn next_exercise(&mut self) -> Result { + pub fn next_exercise(&mut self, stdout: &mut StdoutLock) -> Result { if self.done_status == DoneStatus::Pending { return Ok(ExercisesProgress::CurrentPending); } - self.app_state.done_current_exercise(&mut self.stdout) + self.app_state.done_current_exercise(stdout) } - fn show_prompt(&mut self) -> io::Result<()> { + fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> { if self.manual_run { - self.stdout.queue(SetAttribute(Attribute::Bold))?; - self.stdout.write_all(b"r")?; - self.stdout.queue(ResetColor)?; - self.stdout.write_all(b":run / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"r")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":run / ")?; } if self.done_status != DoneStatus::Pending { - self.stdout.queue(SetAttribute(Attribute::Bold))?; - self.stdout.write_all(b"n")?; - self.stdout.queue(ResetColor)?; - self.stdout.write_all(b":")?; - self.stdout.queue(SetAttribute(Attribute::Underlined))?; - self.stdout.write_all(b"next")?; - self.stdout.queue(ResetColor)?; - self.stdout.write_all(b" / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"n")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":")?; + stdout.queue(SetAttribute(Attribute::Underlined))?; + stdout.write_all(b"next")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" / ")?; } if !self.show_hint { - self.stdout.queue(SetAttribute(Attribute::Bold))?; - self.stdout.write_all(b"h")?; - self.stdout.queue(ResetColor)?; - self.stdout.write_all(b":hint / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"h")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":hint / ")?; } - self.stdout.queue(SetAttribute(Attribute::Bold))?; - self.stdout.write_all(b"l")?; - self.stdout.queue(ResetColor)?; - self.stdout.write_all(b":list / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"l")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":list / ")?; - self.stdout.queue(SetAttribute(Attribute::Bold))?; - self.stdout.write_all(b"q")?; - self.stdout.queue(ResetColor)?; - self.stdout.write_all(b":quit ? ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"q")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":quit ? ")?; - self.stdout.flush() + stdout.flush() } - pub fn render(&mut self) -> io::Result<()> { + pub fn render(&self, stdout: &mut StdoutLock) -> io::Result<()> { // Prevent having the first line shifted if clearing wasn't successful. - self.stdout.write_all(b"\n")?; - clear_terminal(&mut self.stdout)?; + stdout.write_all(b"\n")?; + clear_terminal(stdout)?; - self.stdout.write_all(&self.output)?; + stdout.write_all(&self.output)?; if self.show_hint { - self.stdout + stdout .queue(SetAttributes( Attributes::from(Attribute::Bold).with(Attribute::Underlined), ))? .queue(SetForegroundColor(Color::Cyan))?; - self.stdout.write_all(b"Hint\n")?; - self.stdout.queue(ResetColor)?; + stdout.write_all(b"Hint\n")?; + stdout.queue(ResetColor)?; - self.stdout - .write_all(self.app_state.current_exercise().hint.as_bytes())?; - self.stdout.write_all(b"\n\n")?; + stdout.write_all(self.app_state.current_exercise().hint.as_bytes())?; + stdout.write_all(b"\n\n")?; } if self.done_status != DoneStatus::Pending { - self.stdout + stdout .queue(SetAttribute(Attribute::Bold))? .queue(SetForegroundColor(Color::Green))?; - self.stdout.write_all("Exercise done βœ“\n".as_bytes())?; - self.stdout.queue(ResetColor)?; + stdout.write_all("Exercise done βœ“\n".as_bytes())?; + stdout.queue(ResetColor)?; if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { - solution_link_line(&mut self.stdout, solution_path)?; + solution_link_line(stdout, solution_path)?; } writeln!( - self.stdout, + stdout, "When done experimenting, enter `n` to move on to the next exercise πŸ¦€\n", )?; } let line_width = terminal::size()?.0; progress_bar( - &mut self.stdout, + stdout, self.app_state.n_done(), self.app_state.exercises().len() as u16, line_width, )?; - self.stdout.write_all(b"\nCurrent exercise: ")?; - terminal_file_link( - &mut self.stdout, - self.app_state.current_exercise().path, - Color::Blue, - )?; - self.stdout.write_all(b"\n\n")?; + stdout.write_all(b"\nCurrent exercise: ")?; + terminal_file_link(stdout, self.app_state.current_exercise().path, Color::Blue)?; + stdout.write_all(b"\n\n")?; - self.show_prompt()?; + self.show_prompt(stdout)?; Ok(()) } - pub fn show_hint(&mut self) -> io::Result<()> { + pub fn show_hint(&mut self, stdout: &mut StdoutLock) -> io::Result<()> { self.show_hint = true; - self.render() + self.render(stdout) } } From 833e6e0c92c1d24948ffc086e4d1c69e90dd04c2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 00:24:39 +0200 Subject: [PATCH 1208/1432] Newline after resetting attributes --- src/exercise.rs | 6 ++++-- src/init.rs | 9 +++++---- src/watch/state.rs | 12 +++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 462287db37..ea15465c41 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -31,8 +31,9 @@ fn run_bin( ) -> Result { if let Some(output) = output.as_deref_mut() { write_ansi(output, SetAttribute(Attribute::Underlined)); - output.extend_from_slice(b"Output\n"); + output.extend_from_slice(b"Output"); write_ansi(output, ResetColor); + output.push(b'\n'); } let success = cmd_runner.run_debug_bin(bin_name, output.as_deref_mut())?; @@ -44,8 +45,9 @@ fn run_bin( // leaves the user confused about why the exercise isn't done yet. write_ansi(output, SetAttribute(Attribute::Bold)); write_ansi(output, SetForegroundColor(Color::Red)); - output.extend_from_slice(b"The exercise didn't run successfully (nonzero exit code)\n"); + output.extend_from_slice(b"The exercise didn't run successfully (nonzero exit code)"); write_ansi(output, ResetColor); + output.push(b'\n'); } } diff --git a/src/init.rs b/src/init.rs index 40d9910d26..aecb2d8ced 100644 --- a/src/init.rs +++ b/src/init.rs @@ -148,10 +148,11 @@ pub fn init() -> Result<()> { } stdout.queue(SetForegroundColor(Color::Green))?; - stdout.write_all("Initialization done βœ“\n\n".as_bytes())?; - stdout - .queue(ResetColor)? - .queue(SetAttribute(Attribute::Bold))?; + stdout.write_all("Initialization done βœ“".as_bytes())?; + stdout.queue(ResetColor)?; + stdout.write_all(b"\n\n")?; + + stdout.queue(SetAttribute(Attribute::Bold))?; stdout.write_all(POST_INIT_MSG)?; stdout.queue(ResetColor)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 47af9193a5..1c2e2a9ab8 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -149,8 +149,9 @@ impl<'a> WatchState<'a> { Attributes::from(Attribute::Bold).with(Attribute::Underlined), ))? .queue(SetForegroundColor(Color::Cyan))?; - stdout.write_all(b"Hint\n")?; + stdout.write_all(b"Hint")?; stdout.queue(ResetColor)?; + stdout.write_all(b"\n")?; stdout.write_all(self.app_state.current_exercise().hint.as_bytes())?; stdout.write_all(b"\n\n")?; @@ -160,16 +161,17 @@ impl<'a> WatchState<'a> { stdout .queue(SetAttribute(Attribute::Bold))? .queue(SetForegroundColor(Color::Green))?; - stdout.write_all("Exercise done βœ“\n".as_bytes())?; + stdout.write_all("Exercise done βœ“".as_bytes())?; stdout.queue(ResetColor)?; + stdout.write_all(b"\n")?; if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { solution_link_line(stdout, solution_path)?; } - writeln!( - stdout, - "When done experimenting, enter `n` to move on to the next exercise πŸ¦€\n", + stdout.write_all( + "When done experimenting, enter `n` to move on to the next exercise πŸ¦€\n\n" + .as_bytes(), )?; } From cb86b44dea79b538a9ce62fb230de4b74e95ccf4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 00:40:30 +0200 Subject: [PATCH 1209/1432] LOL, swapped colors --- src/list/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 756814f6d1..eeda110b92 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -178,10 +178,10 @@ impl<'a> ListState<'a> { } if exercise.done { - stdout.queue(SetForegroundColor(Color::Yellow))?; + stdout.queue(SetForegroundColor(Color::Green))?; stdout.write_all(b"DONE ")?; } else { - stdout.queue(SetForegroundColor(Color::Green))?; + stdout.queue(SetForegroundColor(Color::Yellow))?; stdout.write_all(b"PENDING ")?; } From d1571d18f915943418fb8d13a3997d0d7d384e77 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 00:48:12 +0200 Subject: [PATCH 1210/1432] Only reset color and underline after link --- src/term.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/term.rs b/src/term.rs index 4c6ac90481..7b8642b953 100644 --- a/src/term.rs +++ b/src/term.rs @@ -94,7 +94,9 @@ pub fn terminal_file_link(stdout: &mut StdoutLock, path: &str, color: Color) -> stdout, "\x1b]8;;file://{canonical_path}\x1b\\{path}\x1b]8;;\x1b\\", )?; - stdout.queue(ResetColor)?; + stdout + .queue(SetForegroundColor(Color::Reset))? + .queue(SetAttribute(Attribute::NoUnderline))?; Ok(()) } From 5c355468c1c0ef6561348591bb755ff67b561c30 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 00:49:56 +0200 Subject: [PATCH 1211/1432] File link in the list? No problem :D --- src/list/state.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index eeda110b92..c1c75d797b 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -10,7 +10,12 @@ use std::{ io::{self, StdoutLock, Write}, }; -use crate::{app_state::AppState, exercise::Exercise, term::progress_bar, MAX_EXERCISE_NAME_LEN}; +use crate::{ + app_state::AppState, + exercise::Exercise, + term::{progress_bar, terminal_file_link}, + MAX_EXERCISE_NAME_LEN, +}; const MAX_SCROLL_PADDING: usize = 5; // +1 for column padding. @@ -190,7 +195,7 @@ impl<'a> ListState<'a> { stdout.write_all(exercise.name.as_bytes())?; stdout.write_all(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; - stdout.write_all(exercise.path.as_bytes())?; + terminal_file_link(stdout, exercise.path, Color::Blue)?; next_ln_overwrite(stdout)?; stdout.queue(ResetColor)?; From 594e212b8a49cae001c0a45818debaceeda3b9a3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 00:53:42 +0200 Subject: [PATCH 1212/1432] Darker highlighting in the list --- src/list/state.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index c1c75d797b..cc56346261 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -166,9 +166,9 @@ impl<'a> ListState<'a> { { if self.selected_row == Some(self.row_offset + n_displayed_rows) { stdout.queue(SetBackgroundColor(Color::Rgb { - r: 50, - g: 50, - b: 50, + r: 40, + g: 40, + b: 40, }))?; stdout.write_all("πŸ¦€".as_bytes())?; } else { From ee25a7d45805def5cb6516dec6c9edf54fad5e48 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 02:41:22 +0200 Subject: [PATCH 1213/1432] Disable terminal links in VS-Code --- src/term.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/term.rs b/src/term.rs index 7b8642b953..6efe4d51b8 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,5 +1,6 @@ use std::{ - fmt, fs, + cell::Cell, + env, fmt, fs, io::{self, BufRead, StdoutLock, Write}, }; @@ -10,6 +11,10 @@ use crossterm::{ Command, QueueableCommand, }; +thread_local! { + static VS_CODE: Cell = Cell::new(env::var_os("TERM").is_some_and(|v| v == "vscode")); +} + /// Terminal progress bar to be used when not using Ratataui. pub fn progress_bar( stdout: &mut StdoutLock, @@ -73,6 +78,11 @@ pub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> { } pub fn terminal_file_link(stdout: &mut StdoutLock, path: &str, color: Color) -> io::Result<()> { + // VS Code shows its own links. This also avoids some issues, especially on Windows. + if VS_CODE.get() { + return stdout.write_all(path.as_bytes()); + } + let canonical_path = fs::canonicalize(path).ok(); let Some(canonical_path) = canonical_path.as_deref().and_then(|p| p.to_str()) else { From f22700a4eca613f1b3cbbd6f8b3bd4fc37569039 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 02:43:08 +0200 Subject: [PATCH 1214/1432] Use the correct environment variable --- src/term.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/term.rs b/src/term.rs index 6efe4d51b8..924291972e 100644 --- a/src/term.rs +++ b/src/term.rs @@ -12,7 +12,7 @@ use crossterm::{ }; thread_local! { - static VS_CODE: Cell = Cell::new(env::var_os("TERM").is_some_and(|v| v == "vscode")); + static VS_CODE: Cell = Cell::new(env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode")); } /// Terminal progress bar to be used when not using Ratataui. From e811dd15b56d839b0e43e51eeaea1a2a700c0ebb Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 04:29:58 +0200 Subject: [PATCH 1215/1432] Fix list on terminals that don't disable line wrapping --- src/list/state.rs | 109 +++++++++++++++++++++------------------------ src/term.rs | 110 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 149 insertions(+), 70 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index cc56346261..3876884bf4 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -13,7 +13,7 @@ use std::{ use crate::{ app_state::AppState, exercise::Exercise, - term::{progress_bar, terminal_file_link}, + term::{progress_bar, terminal_file_link, CountedWrite, MaxLenWriter}, MAX_EXERCISE_NAME_LEN, }; @@ -28,14 +28,6 @@ fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { Ok(()) } -// Avoids having the last written char as the last displayed one when the -// written width is higher than the terminal width. -// Happens on the Gnome terminal for example. -fn next_ln_overwrite(stdout: &mut StdoutLock) -> io::Result<()> { - stdout.write_all(b" ")?; - next_ln(stdout) -} - #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { Done, @@ -164,40 +156,44 @@ impl<'a> ListState<'a> { .skip(self.row_offset) .take(self.max_n_rows_to_display) { + let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); + if self.selected_row == Some(self.row_offset + n_displayed_rows) { - stdout.queue(SetBackgroundColor(Color::Rgb { + writer.stdout.queue(SetBackgroundColor(Color::Rgb { r: 40, g: 40, b: 40, }))?; - stdout.write_all("πŸ¦€".as_bytes())?; + // The crab emoji has the width of two ascii chars. + writer.add_to_len(2); + writer.stdout.write_all("πŸ¦€".as_bytes())?; } else { - stdout.write_all(b" ")?; + writer.write_ascii(b" ")?; } if exercise_ind == current_exercise_ind { - stdout.queue(SetForegroundColor(Color::Red))?; - stdout.write_all(b">>>>>>> ")?; + writer.stdout.queue(SetForegroundColor(Color::Red))?; + writer.write_ascii(b">>>>>>> ")?; } else { - stdout.write_all(b" ")?; + writer.write_ascii(b" ")?; } if exercise.done { - stdout.queue(SetForegroundColor(Color::Green))?; - stdout.write_all(b"DONE ")?; + writer.stdout.queue(SetForegroundColor(Color::Green))?; + writer.write_ascii(b"DONE ")?; } else { - stdout.queue(SetForegroundColor(Color::Yellow))?; - stdout.write_all(b"PENDING ")?; + writer.stdout.queue(SetForegroundColor(Color::Yellow))?; + writer.write_ascii(b"PENDING ")?; } - stdout.queue(SetForegroundColor(Color::Reset))?; + writer.stdout.queue(SetForegroundColor(Color::Reset))?; - stdout.write_all(exercise.name.as_bytes())?; - stdout.write_all(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; + writer.write_str(exercise.name)?; + writer.write_ascii(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; - terminal_file_link(stdout, exercise.path, Color::Blue)?; + terminal_file_link(&mut writer, exercise.path, Color::Blue)?; - next_ln_overwrite(stdout)?; + next_ln(stdout)?; stdout.queue(ResetColor)?; n_displayed_rows += 1; } @@ -213,10 +209,11 @@ impl<'a> ListState<'a> { stdout.queue(BeginSynchronizedUpdate)?.queue(MoveTo(0, 0))?; // Header - stdout.write_all(b" Current State Name")?; - stdout.write_all(&SPACE[..self.name_col_width - 2])?; - stdout.write_all(b"Path")?; - next_ln_overwrite(stdout)?; + let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); + writer.write_ascii(b" Current State Name")?; + writer.write_ascii(&SPACE[..self.name_col_width - 2])?; + writer.write_ascii(b"Path")?; + next_ln(stdout)?; // Rows let iter = self.app_state.exercises().iter().enumerate(); @@ -237,7 +234,7 @@ impl<'a> ListState<'a> { next_ln(stdout)?; progress_bar( - stdout, + &mut MaxLenWriter::new(stdout, self.term_width as usize), self.app_state.n_done(), self.app_state.exercises().len() as u16, self.term_width, @@ -247,59 +244,55 @@ impl<'a> ListState<'a> { stdout.write_all(&self.separator_line)?; next_ln(stdout)?; + let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); if self.message.is_empty() { // Help footer message if self.selected_row.is_some() { - stdout.write_all( - "↓/j ↑/k home/g end/G | ontinue at | eset exercise".as_bytes(), - )?; + writer.write_str("↓/j ↑/k home/g end/G | ontinue at | eset exercise")?; if self.narrow_term { - next_ln_overwrite(stdout)?; - stdout.write_all(b"filter ")?; + next_ln(stdout)?; + writer = MaxLenWriter::new(stdout, self.term_width as usize); + + writer.write_ascii(b"filter ")?; } else { - stdout.write_all(b" | filter ")?; + writer.write_ascii(b" | filter ")?; } } else { // Nothing selected (and nothing shown), so only display filter and quit. - stdout.write_all(b"filter ")?; + writer.write_ascii(b"filter ")?; } match self.filter { Filter::Done => { - stdout + writer + .stdout .queue(SetForegroundColor(Color::Magenta))? .queue(SetAttribute(Attribute::Underlined))?; - stdout.write_all(b"one")?; - stdout.queue(ResetColor)?; - stdout.write_all(b"/

ending")?; + writer.write_ascii(b"one")?; + writer.stdout.queue(ResetColor)?; + writer.write_ascii(b"/

ending")?; } Filter::Pending => { - stdout.write_all(b"one/")?; - stdout + writer.write_ascii(b"one/")?; + writer + .stdout .queue(SetForegroundColor(Color::Magenta))? .queue(SetAttribute(Attribute::Underlined))?; - stdout.write_all(b"

ending")?; - stdout.queue(ResetColor)?; + writer.write_ascii(b"

ending")?; + writer.stdout.queue(ResetColor)?; } - Filter::None => stdout.write_all(b"one/

ending")?, + Filter::None => writer.write_ascii(b"one/

ending")?, } - stdout.write_all(b" | uit list")?; - - if self.narrow_term { - next_ln_overwrite(stdout)?; - } else { - next_ln(stdout)?; - } + writer.write_ascii(b" | uit list")?; } else { - stdout.queue(SetForegroundColor(Color::Magenta))?; - stdout.write_all(self.message.as_bytes())?; + writer.stdout.queue(SetForegroundColor(Color::Magenta))?; + writer.write_str(&self.message)?; stdout.queue(ResetColor)?; - next_ln_overwrite(stdout)?; - if self.narrow_term { - next_ln(stdout)?; - } + next_ln(stdout)?; } + + next_ln(stdout)?; } stdout.queue(EndSynchronizedUpdate)?.flush() diff --git a/src/term.rs b/src/term.rs index 924291972e..51fcad10ea 100644 --- a/src/term.rs +++ b/src/term.rs @@ -15,9 +15,83 @@ thread_local! { static VS_CODE: Cell = Cell::new(env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode")); } +pub struct MaxLenWriter<'a, 'b> { + pub stdout: &'a mut StdoutLock<'b>, + len: usize, + max_len: usize, +} + +impl<'a, 'b> MaxLenWriter<'a, 'b> { + #[inline] + pub fn new(stdout: &'a mut StdoutLock<'b>, max_len: usize) -> Self { + Self { + stdout, + len: 0, + max_len, + } + } + + // Additional is for emojis that take more space. + #[inline] + pub fn add_to_len(&mut self, additional: usize) { + self.len += additional; + } +} + +pub trait CountedWrite<'a> { + fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()>; + fn write_str(&mut self, unicode: &str) -> io::Result<()>; + fn stdout(&mut self) -> &mut StdoutLock<'a>; +} + +impl<'a, 'b> CountedWrite<'b> for MaxLenWriter<'a, 'b> { + fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> { + let n = ascii.len().min(self.max_len.saturating_sub(self.len)); + self.stdout.write_all(&ascii[..n])?; + self.len += n; + Ok(()) + } + + fn write_str(&mut self, unicode: &str) -> io::Result<()> { + if let Some((ind, c)) = unicode + .char_indices() + .take(self.max_len.saturating_sub(self.len)) + .last() + { + self.stdout + .write_all(&unicode.as_bytes()[..ind + c.len_utf8()])?; + self.len += ind + 1; + } + + Ok(()) + } + + #[inline] + fn stdout(&mut self) -> &mut StdoutLock<'b> { + self.stdout + } +} + +impl<'a> CountedWrite<'a> for StdoutLock<'a> { + #[inline] + fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> { + self.write_all(ascii) + } + + #[inline] + fn write_str(&mut self, unicode: &str) -> io::Result<()> { + self.write_all(unicode.as_bytes()) + } + + #[inline] + fn stdout(&mut self) -> &mut StdoutLock<'a> { + self + } +} + /// Terminal progress bar to be used when not using Ratataui. -pub fn progress_bar( - stdout: &mut StdoutLock, +pub fn progress_bar<'a>( + writer: &mut impl CountedWrite<'a>, progress: u16, total: u16, line_width: u16, @@ -32,9 +106,13 @@ pub fn progress_bar( const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; if line_width < MIN_LINE_WIDTH { - return write!(stdout, "Progress: {progress}/{total} exercises"); + writer.write_ascii(b"Progress: ")?; + // Integers are in ASCII. + writer.write_ascii(format!("{progress}/{total}").as_bytes())?; + return writer.write_ascii(b" exercises"); } + let stdout = writer.stdout(); stdout.write_all(PREFIX)?; let width = line_width - WRAPPER_WIDTH; @@ -77,16 +155,20 @@ pub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> { Ok(()) } -pub fn terminal_file_link(stdout: &mut StdoutLock, path: &str, color: Color) -> io::Result<()> { +pub fn terminal_file_link<'a>( + writer: &mut impl CountedWrite<'a>, + path: &str, + color: Color, +) -> io::Result<()> { // VS Code shows its own links. This also avoids some issues, especially on Windows. if VS_CODE.get() { - return stdout.write_all(path.as_bytes()); + return writer.write_str(path); } let canonical_path = fs::canonicalize(path).ok(); let Some(canonical_path) = canonical_path.as_deref().and_then(|p| p.to_str()) else { - return stdout.write_all(path.as_bytes()); + return writer.write_str(path); }; // Windows itself can't handle its verbatim paths. @@ -97,14 +179,18 @@ pub fn terminal_file_link(stdout: &mut StdoutLock, path: &str, color: Color) -> canonical_path }; - stdout + writer + .stdout() .queue(SetForegroundColor(color))? .queue(SetAttribute(Attribute::Underlined))?; - write!( - stdout, - "\x1b]8;;file://{canonical_path}\x1b\\{path}\x1b]8;;\x1b\\", - )?; - stdout + writer.stdout().write_all(b"\x1b]8;;file://")?; + writer.stdout().write_all(canonical_path.as_bytes())?; + writer.stdout().write_all(b"\x1b\\")?; + // Only this part is visible. + writer.write_str(path)?; + writer.stdout().write_all(b"\x1b]8;;\x1b\\")?; + writer + .stdout() .queue(SetForegroundColor(Color::Reset))? .queue(SetAttribute(Attribute::NoUnderline))?; From 74388d4bf44cdfebc0d6dc8e5faa81bffe71ddd6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 04:41:26 +0200 Subject: [PATCH 1216/1432] Only trigger write when needed --- src/term.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/term.rs b/src/term.rs index 51fcad10ea..fa71ac65b8 100644 --- a/src/term.rs +++ b/src/term.rs @@ -47,8 +47,10 @@ pub trait CountedWrite<'a> { impl<'a, 'b> CountedWrite<'b> for MaxLenWriter<'a, 'b> { fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> { let n = ascii.len().min(self.max_len.saturating_sub(self.len)); - self.stdout.write_all(&ascii[..n])?; - self.len += n; + if n > 0 { + self.stdout.write_all(&ascii[..n])?; + self.len += n; + } Ok(()) } From 0f71a150ff292b1f18b30c7aa75dc8b3d48d2b8e Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 26 Aug 2024 22:03:09 +0200 Subject: [PATCH 1217/1432] Making code prettier :P --- src/main.rs | 4 +--- src/term.rs | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 998d3d118f..e8f274b083 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,9 +71,7 @@ fn main() -> Result<()> { } match args.command { - Some(Subcommands::Init) => { - return init::init().context("Initialization failed"); - } + Some(Subcommands::Init) => return init::init().context("Initialization failed"), Some(Subcommands::Dev(dev_command)) => return dev_command.run(), _ => (), } diff --git a/src/term.rs b/src/term.rs index fa71ac65b8..0416c30ead 100644 --- a/src/term.rs +++ b/src/term.rs @@ -153,8 +153,7 @@ pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { pub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> { stdout.flush()?; io::stdin().lock().read_until(b'\n', &mut Vec::new())?; - stdout.write_all(b"\n")?; - Ok(()) + stdout.write_all(b"\n") } pub fn terminal_file_link<'a>( From dd52e9cd7239276745c2fbad02a63931327a8e48 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 27 Aug 2024 00:03:50 +0200 Subject: [PATCH 1218/1432] Separate the scroll state --- src/list.rs | 1 + src/list/scroll_state.rs | 104 +++++++++++++++++++++++++++++++++++++++ src/list/state.rs | 104 +++++++++++---------------------------- 3 files changed, 135 insertions(+), 74 deletions(-) create mode 100644 src/list/scroll_state.rs diff --git a/src/list.rs b/src/list.rs index a8e52254d2..481fb2f41d 100644 --- a/src/list.rs +++ b/src/list.rs @@ -16,6 +16,7 @@ use crate::app_state::AppState; use self::state::{Filter, ListState}; +mod scroll_state; mod state; fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> { diff --git a/src/list/scroll_state.rs b/src/list/scroll_state.rs new file mode 100644 index 0000000000..25a737366f --- /dev/null +++ b/src/list/scroll_state.rs @@ -0,0 +1,104 @@ +pub struct ScrollState { + n_rows: usize, + max_n_rows_to_display: usize, + selected: Option, + offset: usize, + scroll_padding: usize, + max_scroll_padding: usize, +} + +impl ScrollState { + pub fn new(n_rows: usize, selected: Option, max_scroll_padding: usize) -> Self { + Self { + n_rows, + max_n_rows_to_display: 0, + selected, + offset: selected.map_or(0, |selected| selected.saturating_sub(max_scroll_padding)), + scroll_padding: 0, + max_scroll_padding, + } + } + + #[inline] + pub fn offset(&self) -> usize { + self.offset + } + + fn update_offset(&mut self) { + let Some(selected) = self.selected else { + return; + }; + + let min_offset = (selected + self.scroll_padding) + .saturating_sub(self.max_n_rows_to_display.saturating_sub(1)); + let max_offset = selected.saturating_sub(self.scroll_padding); + let global_max_offset = self.n_rows.saturating_sub(self.max_n_rows_to_display); + + self.offset = self + .offset + .max(min_offset) + .min(max_offset) + .min(global_max_offset); + } + + #[inline] + pub fn selected(&self) -> Option { + self.selected + } + + fn set_selected(&mut self, selected: usize) { + self.selected = Some(selected); + self.update_offset(); + } + + pub fn select_next(&mut self) { + if let Some(selected) = self.selected { + self.set_selected((selected + 1).min(self.n_rows - 1)); + } + } + + pub fn select_previous(&mut self) { + if let Some(selected) = self.selected { + self.set_selected(selected.saturating_sub(1)); + } + } + + pub fn select_first(&mut self) { + if self.n_rows > 0 { + self.set_selected(0); + } + } + + pub fn select_last(&mut self) { + if self.n_rows > 0 { + self.set_selected(self.n_rows - 1); + } + } + + pub fn set_n_rows(&mut self, n_rows: usize) { + self.n_rows = n_rows; + + if self.n_rows == 0 { + self.selected = None; + return; + } + + self.set_selected(self.selected.map_or(0, |selected| selected.min(n_rows - 1))); + } + + #[inline] + fn update_scroll_padding(&mut self) { + self.scroll_padding = (self.max_n_rows_to_display / 4).min(self.max_scroll_padding); + } + + #[inline] + pub fn max_n_rows_to_display(&self) -> usize { + self.max_n_rows_to_display + } + + pub fn set_max_n_rows_to_display(&mut self, max_n_rows_to_display: usize) { + self.max_n_rows_to_display = max_n_rows_to_display; + self.update_scroll_padding(); + self.update_offset(); + } +} diff --git a/src/list/state.rs b/src/list/state.rs index 3876884bf4..e263b7ed6e 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -17,7 +17,8 @@ use crate::{ MAX_EXERCISE_NAME_LEN, }; -const MAX_SCROLL_PADDING: usize = 5; +use super::scroll_state::ScrollState; + // +1 for column padding. const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; @@ -39,19 +40,14 @@ pub struct ListState<'a> { /// Footer message to be displayed if not empty. pub message: String, app_state: &'a mut AppState, + scroll_state: ScrollState, name_col_width: usize, filter: Filter, - n_rows_with_filter: usize, - /// Selected row out of the filtered ones. - selected_row: Option, - row_offset: usize, term_width: u16, term_height: u16, separator_line: Vec, narrow_term: bool, show_footer: bool, - max_n_rows_to_display: usize, - scroll_padding: usize, } impl<'a> ListState<'a> { @@ -70,50 +66,29 @@ impl<'a> ListState<'a> { let n_rows_with_filter = app_state.exercises().len(); let selected = app_state.current_exercise_ind(); + let (width, height) = terminal::size()?; + let scroll_state = ScrollState::new(n_rows_with_filter, Some(selected), 5); + let mut slf = Self { message: String::with_capacity(128), app_state, + scroll_state, name_col_width, filter, - n_rows_with_filter, - selected_row: Some(selected), - row_offset: selected.saturating_sub(MAX_SCROLL_PADDING), // Set by `set_term_size` term_width: 0, term_height: 0, separator_line: Vec::new(), narrow_term: false, show_footer: true, - max_n_rows_to_display: 0, - scroll_padding: 0, }; - let (width, height) = terminal::size()?; slf.set_term_size(width, height); slf.draw(stdout)?; Ok(slf) } - fn update_offset(&mut self) { - let Some(selected) = self.selected_row else { - return; - }; - - let min_offset = (selected + self.scroll_padding) - .saturating_sub(self.max_n_rows_to_display.saturating_sub(1)); - let max_offset = selected.saturating_sub(self.scroll_padding); - let global_max_offset = self - .n_rows_with_filter - .saturating_sub(self.max_n_rows_to_display); - - self.row_offset = self - .row_offset - .max(min_offset) - .min(max_offset) - .min(global_max_offset); - } - pub fn set_term_size(&mut self, width: u16, height: u16) { self.term_width = width; self.term_height = height; @@ -124,7 +99,7 @@ impl<'a> ListState<'a> { let wide_help_footer_width = 95; // The help footer is shorter when nothing is selected. - self.narrow_term = width < wide_help_footer_width && self.selected_row.is_some(); + self.narrow_term = width < wide_help_footer_width && self.scroll_state.selected().is_some(); let header_height = 1; // 2 separator, 1 progress bar, 1-2 footer message. @@ -135,13 +110,10 @@ impl<'a> ListState<'a> { self.separator_line = "─".as_bytes().repeat(width as usize); } - self.max_n_rows_to_display = height - .saturating_sub(header_height + u16::from(self.show_footer) * footer_height) - as usize; - - self.scroll_padding = (self.max_n_rows_to_display / 4).min(MAX_SCROLL_PADDING); - - self.update_offset(); + self.scroll_state.set_max_n_rows_to_display( + height.saturating_sub(header_height + u16::from(self.show_footer) * footer_height) + as usize, + ); } fn draw_rows( @@ -150,15 +122,16 @@ impl<'a> ListState<'a> { filtered_exercises: impl Iterator, ) -> io::Result { let current_exercise_ind = self.app_state.current_exercise_ind(); + let row_offset = self.scroll_state.offset(); let mut n_displayed_rows = 0; for (exercise_ind, exercise) in filtered_exercises - .skip(self.row_offset) - .take(self.max_n_rows_to_display) + .skip(row_offset) + .take(self.scroll_state.max_n_rows_to_display()) { let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); - if self.selected_row == Some(self.row_offset + n_displayed_rows) { + if self.scroll_state.selected() == Some(row_offset + n_displayed_rows) { writer.stdout.queue(SetBackgroundColor(Color::Rgb { r: 40, g: 40, @@ -225,7 +198,7 @@ impl<'a> ListState<'a> { Filter::None => self.draw_rows(stdout, iter)?, }; - for _ in 0..self.max_n_rows_to_display - n_displayed_rows { + for _ in 0..self.scroll_state.max_n_rows_to_display() - n_displayed_rows { next_ln(stdout)?; } @@ -247,7 +220,7 @@ impl<'a> ListState<'a> { let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); if self.message.is_empty() { // Help footer message - if self.selected_row.is_some() { + if self.scroll_state.selected().is_some() { writer.write_str("↓/j ↑/k home/g end/G | ontinue at | eset exercise")?; if self.narrow_term { next_ln(stdout)?; @@ -298,13 +271,8 @@ impl<'a> ListState<'a> { stdout.queue(EndSynchronizedUpdate)?.flush() } - fn set_selected(&mut self, selected: usize) { - self.selected_row = Some(selected); - self.update_offset(); - } - fn update_rows(&mut self) { - self.n_rows_with_filter = match self.filter { + let n_rows = match self.filter { Filter::Done => self .app_state .exercises() @@ -320,15 +288,7 @@ impl<'a> ListState<'a> { Filter::None => self.app_state.exercises().len(), }; - if self.n_rows_with_filter == 0 { - self.selected_row = None; - return; - } - - self.set_selected( - self.selected_row - .map_or(0, |selected| selected.min(self.n_rows_with_filter - 1)), - ); + self.scroll_state.set_n_rows(n_rows); } #[inline] @@ -341,28 +301,24 @@ impl<'a> ListState<'a> { self.update_rows(); } + #[inline] pub fn select_next(&mut self) { - if let Some(selected) = self.selected_row { - self.set_selected((selected + 1).min(self.n_rows_with_filter - 1)); - } + self.scroll_state.select_next(); } + #[inline] pub fn select_previous(&mut self) { - if let Some(selected) = self.selected_row { - self.set_selected(selected.saturating_sub(1)); - } + self.scroll_state.select_previous(); } + #[inline] pub fn select_first(&mut self) { - if self.n_rows_with_filter > 0 { - self.set_selected(0); - } + self.scroll_state.select_first(); } + #[inline] pub fn select_last(&mut self) { - if self.n_rows_with_filter > 0 { - self.set_selected(self.n_rows_with_filter - 1); - } + self.scroll_state.select_last(); } fn selected_to_exercise_ind(&self, selected: usize) -> Result { @@ -390,7 +346,7 @@ impl<'a> ListState<'a> { } pub fn reset_selected(&mut self) -> Result<()> { - let Some(selected) = self.selected_row else { + let Some(selected) = self.scroll_state.selected() else { self.message.push_str("Nothing selected to reset!"); return Ok(()); }; @@ -408,7 +364,7 @@ impl<'a> ListState<'a> { // Return `true` if there was something to select. pub fn selected_to_current_exercise(&mut self) -> Result { - let Some(selected) = self.selected_row else { + let Some(selected) = self.scroll_state.selected() else { self.message.push_str("Nothing selected to continue at!"); return Ok(false); }; From c209c874a9b0aad4a311ef9947c734e086f83a1c Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 28 Aug 2024 00:34:24 +0200 Subject: [PATCH 1219/1432] Check the exercise name length --- src/dev/check.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index e00d4cc1cb..4c5e07286b 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -14,7 +14,7 @@ use crate::{ collections::{hash_set_with_capacity, HashSet}, exercise::{RunnableExercise, OUTPUT_CAPACITY}, info_file::{ExerciseInfo, InfoFile}, - CURRENT_FORMAT_VERSION, + CURRENT_FORMAT_VERSION, MAX_EXERCISE_NAME_LEN, }; // Find a char that isn't allowed in the exercise's `name` or `dir`. @@ -59,6 +59,9 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { if name.is_empty() { bail!("Found an empty exercise name in `info.toml`"); } + if name.len() > MAX_EXERCISE_NAME_LEN { + bail!("The length of the exercise name `{name}` is bigger than the maximum {MAX_EXERCISE_NAME_LEN}"); + } if let Some(c) = forbidden_char(name) { bail!("Char `{c}` in the exercise name `{name}` is not allowed"); } From 7d2bc1c7a4333de5460cb86a8dca5e5ecad2a643 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 28 Aug 2024 00:56:22 +0200 Subject: [PATCH 1220/1432] Use a Vec for the name col padding --- src/dev/check.rs | 4 +++- src/list/state.rs | 13 ++++++------- src/main.rs | 1 - 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 4c5e07286b..a6db3c21ae 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -14,9 +14,11 @@ use crate::{ collections::{hash_set_with_capacity, HashSet}, exercise::{RunnableExercise, OUTPUT_CAPACITY}, info_file::{ExerciseInfo, InfoFile}, - CURRENT_FORMAT_VERSION, MAX_EXERCISE_NAME_LEN, + CURRENT_FORMAT_VERSION, }; +const MAX_EXERCISE_NAME_LEN: usize = 32; + // Find a char that isn't allowed in the exercise's `name` or `dir`. fn forbidden_char(input: &str) -> Option { input.chars().find(|c| !c.is_alphanumeric() && *c != '_') diff --git a/src/list/state.rs b/src/list/state.rs index e263b7ed6e..51e4cfa9ee 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -14,13 +14,11 @@ use crate::{ app_state::AppState, exercise::Exercise, term::{progress_bar, terminal_file_link, CountedWrite, MaxLenWriter}, - MAX_EXERCISE_NAME_LEN, }; use super::scroll_state::ScrollState; -// +1 for column padding. -const SPACE: &[u8] = &[b' '; MAX_EXERCISE_NAME_LEN + 1]; +const COL_SPACING: usize = 2; fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { stdout @@ -41,7 +39,7 @@ pub struct ListState<'a> { pub message: String, app_state: &'a mut AppState, scroll_state: ScrollState, - name_col_width: usize, + name_col_padding: Vec, filter: Filter, term_width: u16, term_height: u16, @@ -61,6 +59,7 @@ impl<'a> ListState<'a> { .map(|exercise| exercise.name.len()) .max() .map_or(name_col_title_len, |max| max.max(name_col_title_len)); + let name_col_padding = vec![b' '; name_col_width + COL_SPACING]; let filter = Filter::None; let n_rows_with_filter = app_state.exercises().len(); @@ -73,7 +72,7 @@ impl<'a> ListState<'a> { message: String::with_capacity(128), app_state, scroll_state, - name_col_width, + name_col_padding, filter, // Set by `set_term_size` term_width: 0, @@ -162,7 +161,7 @@ impl<'a> ListState<'a> { writer.stdout.queue(SetForegroundColor(Color::Reset))?; writer.write_str(exercise.name)?; - writer.write_ascii(&SPACE[..self.name_col_width + 2 - exercise.name.len()])?; + writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?; terminal_file_link(&mut writer, exercise.path, Color::Blue)?; @@ -184,7 +183,7 @@ impl<'a> ListState<'a> { // Header let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); writer.write_ascii(b" Current State Name")?; - writer.write_ascii(&SPACE[..self.name_col_width - 2])?; + writer.write_ascii(&self.name_col_padding[2..])?; writer.write_ascii(b"Path")?; next_ln(stdout)?; diff --git a/src/main.rs b/src/main.rs index e8f274b083..e53cd5a7da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,6 @@ mod term; mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; -const MAX_EXERCISE_NAME_LEN: usize = 32; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] From 5556d42b46e3bfe281343d69da588378c728c089 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 28 Aug 2024 01:10:19 +0200 Subject: [PATCH 1221/1432] Use sol_path --- src/app_state.rs | 12 ++++-------- src/cargo_toml.rs | 2 +- src/exercise.rs | 31 +++++++++++++++++++++++++++++++ src/info_file.rs | 29 +++++------------------------ src/init.rs | 4 ++-- 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index d7de1fdbd7..1000047d46 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -321,14 +321,10 @@ impl AppState { .write_solution_to_disk(self.current_exercise_ind, current_exercise.name) .map(Some) } else { - let solution_path = if let Some(dir) = current_exercise.dir { - format!("solutions/{dir}/{}.rs", current_exercise.name) - } else { - format!("solutions/{}.rs", current_exercise.name) - }; - - if Path::new(&solution_path).exists() { - return Ok(Some(solution_path)); + let sol_path = current_exercise.sol_path(); + + if Path::new(&sol_path).exists() { + return Ok(Some(sol_path)); } Ok(None) diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index 445b6b57b3..8d417ffa93 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use std::path::Path; -use crate::info_file::ExerciseInfo; +use crate::{exercise::RunnableExercise, info_file::ExerciseInfo}; /// Initial capacity of the bins buffer. pub const BINS_BUFFER_CAPACITY: usize = 1 << 14; diff --git a/src/exercise.rs b/src/exercise.rs index ea15465c41..11eea63805 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -68,6 +68,7 @@ pub struct Exercise { pub trait RunnableExercise { fn name(&self) -> &str; + fn dir(&self) -> Option<&str>; fn strict_clippy(&self) -> bool; fn test(&self) -> bool; @@ -145,6 +146,31 @@ pub trait RunnableExercise { self.run::(&bin_name, output, cmd_runner) } + + fn sol_path(&self) -> String { + let name = self.name(); + + let mut path = if let Some(dir) = self.dir() { + // 14 = 10 + 1 + 3 + // solutions/ + / + .rs + let mut path = String::with_capacity(14 + dir.len() + name.len()); + path.push_str("solutions/"); + path.push_str(dir); + path.push('/'); + path + } else { + // 13 = 10 + 3 + // solutions/ + .rs + let mut path = String::with_capacity(13 + name.len()); + path.push_str("solutions/"); + path + }; + + path.push_str(name); + path.push_str(".rs"); + + path + } } impl RunnableExercise for Exercise { @@ -153,6 +179,11 @@ impl RunnableExercise for Exercise { self.name } + #[inline] + fn dir(&self) -> Option<&str> { + self.dir + } + #[inline] fn strict_clippy(&self) -> bool { self.strict_clippy diff --git a/src/info_file.rs b/src/info_file.rs index d4e46110bd..fdc8f0f35c 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -52,30 +52,6 @@ impl ExerciseInfo { path } - - /// Path to the solution file starting with the `solutions/` directory. - pub fn sol_path(&self) -> String { - let mut path = if let Some(dir) = &self.dir { - // 14 = 10 + 1 + 3 - // solutions/ + / + .rs - let mut path = String::with_capacity(14 + dir.len() + self.name.len()); - path.push_str("solutions/"); - path.push_str(dir); - path.push('/'); - path - } else { - // 13 = 10 + 3 - // solutions/ + .rs - let mut path = String::with_capacity(13 + self.name.len()); - path.push_str("solutions/"); - path - }; - - path.push_str(&self.name); - path.push_str(".rs"); - - path - } } impl RunnableExercise for ExerciseInfo { @@ -84,6 +60,11 @@ impl RunnableExercise for ExerciseInfo { &self.name } + #[inline] + fn dir(&self) -> Option<&str> { + self.dir.as_deref() + } + #[inline] fn strict_clippy(&self) -> bool { self.strict_clippy diff --git a/src/init.rs b/src/init.rs index aecb2d8ced..332bf52e31 100644 --- a/src/init.rs +++ b/src/init.rs @@ -13,8 +13,8 @@ use std::{ }; use crate::{ - cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile, - term::press_enter_prompt, + cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, exercise::RunnableExercise, + info_file::InfoFile, term::press_enter_prompt, }; #[derive(Deserialize)] From cba4a6f9c8f3b76ccfbf8c4c2aab6adda649df64 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 28 Aug 2024 01:19:53 +0200 Subject: [PATCH 1222/1432] Only disable links in VS code in the list --- src/app_state.rs | 10 ++++++++++ src/list/state.rs | 8 +++++++- src/term.rs | 12 +----------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 1000047d46..b88c12578a 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,5 +1,6 @@ use anyhow::{bail, Context, Error, Result}; use std::{ + env, fs::{self, File}, io::{Read, StdoutLock, Write}, path::Path, @@ -44,6 +45,8 @@ pub struct AppState { file_buf: Vec, official_exercises: bool, cmd_runner: CmdRunner, + // Running in VS Code. + vs_code: bool, } impl AppState { @@ -131,6 +134,7 @@ impl AppState { file_buf: Vec::with_capacity(2048), official_exercises: !Path::new("info.toml").exists(), cmd_runner, + vs_code: env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode"), }; let state_file_status = slf.update_from_file(); @@ -163,6 +167,11 @@ impl AppState { &self.cmd_runner } + #[inline] + pub fn vs_code(&self) -> bool { + self.vs_code + } + // Write the state file. // The file's format is very simple: // - The first line is a comment. @@ -457,6 +466,7 @@ mod tests { file_buf: Vec::new(), official_exercises: true, cmd_runner: CmdRunner::build().unwrap(), + vs_code: false, }; let mut assert = |done: [bool; 3], expected: [Option; 3]| { diff --git a/src/list/state.rs b/src/list/state.rs index 51e4cfa9ee..5f0cda37f5 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -163,7 +163,13 @@ impl<'a> ListState<'a> { writer.write_str(exercise.name)?; writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?; - terminal_file_link(&mut writer, exercise.path, Color::Blue)?; + // The list links aren't shown correctly in VS Code on Windows. + // But VS Code shows its own links anyway. + if self.app_state.vs_code() { + writer.write_str(exercise.path)?; + } else { + terminal_file_link(&mut writer, exercise.path, Color::Blue)?; + } next_ln(stdout)?; stdout.queue(ResetColor)?; diff --git a/src/term.rs b/src/term.rs index 0416c30ead..ee8dbf8697 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,6 +1,5 @@ use std::{ - cell::Cell, - env, fmt, fs, + fmt, fs, io::{self, BufRead, StdoutLock, Write}, }; @@ -11,10 +10,6 @@ use crossterm::{ Command, QueueableCommand, }; -thread_local! { - static VS_CODE: Cell = Cell::new(env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode")); -} - pub struct MaxLenWriter<'a, 'b> { pub stdout: &'a mut StdoutLock<'b>, len: usize, @@ -161,11 +156,6 @@ pub fn terminal_file_link<'a>( path: &str, color: Color, ) -> io::Result<()> { - // VS Code shows its own links. This also avoids some issues, especially on Windows. - if VS_CODE.get() { - return writer.write_str(path); - } - let canonical_path = fs::canonicalize(path).ok(); let Some(canonical_path) = canonical_path.as_deref().and_then(|p| p.to_str()) else { From afc320bed4ca57d19b66f9d1d33d71806f333e27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 00:17:22 +0200 Subject: [PATCH 1223/1432] Fix error about too many open files during the final check --- src/app_state.rs | 101 ++++++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index b88c12578a..cc77711ab9 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,8 +1,8 @@ -use anyhow::{bail, Context, Error, Result}; +use anyhow::{bail, Context, Result}; use std::{ env, fs::{self, File}, - io::{Read, StdoutLock, Write}, + io::{self, Read, StdoutLock, Write}, path::Path, process::{Command, Stdio}, thread, @@ -35,6 +35,12 @@ pub enum StateFileStatus { NotRead, } +enum AllExercisesCheck { + Pending(usize), + AllDone, + CheckedUntil(usize), +} + pub struct AppState { current_exercise_ind: usize, exercises: Vec, @@ -340,59 +346,80 @@ impl AppState { } } - /// Mark the current exercise as done and move on to the next pending exercise if one exists. - /// If all exercises are marked as done, run all of them to make sure that they are actually - /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. - pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result { - let exercise = &mut self.exercises[self.current_exercise_ind]; - if !exercise.done { - exercise.done = true; - self.n_done += 1; - } - - if let Some(ind) = self.next_pending_exercise_ind() { - self.set_current_exercise_ind(ind)?; - return Ok(ExercisesProgress::NewPending); - } - + // Return the exercise index of the first pending exercise found. + fn check_all_exercises(&self, stdout: &mut StdoutLock) -> Result> { stdout.write_all(RERUNNING_ALL_EXERCISES_MSG)?; - let n_exercises = self.exercises.len(); - let pending_exercise_ind = thread::scope(|s| { + let status = thread::scope(|s| { let handles = self .exercises - .iter_mut() - .map(|exercise| { - s.spawn(|| { - let success = exercise.run_exercise(None, &self.cmd_runner)?; - exercise.done = success; - Ok::<_, Error>(success) - }) - }) + .iter() + .map(|exercise| s.spawn(|| exercise.run_exercise(None, &self.cmd_runner))) .collect::>(); for (exercise_ind, handle) in handles.into_iter().enumerate() { write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; stdout.flush()?; - let success = handle.join().unwrap()?; + let Ok(success) = handle.join().unwrap() else { + return Ok(AllExercisesCheck::CheckedUntil(exercise_ind)); + }; + if !success { - stdout.write_all(b"\n\n")?; - return Ok(Some(exercise_ind)); + return Ok(AllExercisesCheck::Pending(exercise_ind)); } } - Ok::<_, Error>(None) + Ok::<_, io::Error>(AllExercisesCheck::AllDone) })?; - if let Some(pending_exercise_ind) = pending_exercise_ind { + let mut exercise_ind = match status { + AllExercisesCheck::Pending(exercise_ind) => return Ok(Some(exercise_ind)), + AllExercisesCheck::AllDone => return Ok(None), + AllExercisesCheck::CheckedUntil(ind) => ind, + }; + + // We got an error while checking all exercises in parallel. + // This could be because we exceeded the limit of open file descriptors. + // Therefore, try to continue the check sequentially. + for exercise in &self.exercises[exercise_ind..] { + write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; + stdout.flush()?; + + let success = exercise.run_exercise(None, &self.cmd_runner)?; + if !success { + return Ok(Some(exercise_ind)); + } + + exercise_ind += 1; + } + + Ok(None) + } + + /// Mark the current exercise as done and move on to the next pending exercise if one exists. + /// If all exercises are marked as done, run all of them to make sure that they are actually + /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. + pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result { + let exercise = &mut self.exercises[self.current_exercise_ind]; + if !exercise.done { + exercise.done = true; + self.n_done += 1; + } + + if let Some(ind) = self.next_pending_exercise_ind() { + self.set_current_exercise_ind(ind)?; + return Ok(ExercisesProgress::NewPending); + } + + if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? { + stdout.write_all(b"\n\n")?; + self.current_exercise_ind = pending_exercise_ind; - self.n_done = self - .exercises - .iter() - .filter(|exercise| exercise.done) - .count() as u16; + self.exercises[pending_exercise_ind].done = false; + // All exercises were marked as done. + self.n_done -= 1; self.write()?; return Ok(ExercisesProgress::NewPending); } From 789492d1a9bb686e593b08dd8c4ca3af26652bee Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 00:32:58 +0200 Subject: [PATCH 1224/1432] The number of exercises can't be zero, but still --- src/app_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_state.rs b/src/app_state.rs index cc77711ab9..2a205682cb 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -301,7 +301,7 @@ impl AppState { // Return the index of the next pending exercise or `None` if all exercises are done. fn next_pending_exercise_ind(&self) -> Option { - if self.current_exercise_ind == self.exercises.len() - 1 { + if self.current_exercise_ind + 1 == self.exercises.len() { // The last exercise is done. // Search for exercises not done from the start. return self.exercises[..self.current_exercise_ind] From fc1f9f012431b129dea850443b6b3b8a760a45e1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 01:56:45 +0200 Subject: [PATCH 1225/1432] Optimize reading and writing the state file --- src/app_state.rs | 140 ++++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 2a205682cb..ef2f8741e8 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,8 +1,8 @@ use anyhow::{bail, Context, Result}; use std::{ env, - fs::{self, File}, - io::{self, Read, StdoutLock, Write}, + fs::{File, OpenOptions}, + io::{self, Read, Seek, StdoutLock, Write}, path::Path, process::{Command, Stdio}, thread, @@ -18,7 +18,6 @@ use crate::{ }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; -const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; #[must_use] pub enum ExercisesProgress { @@ -47,6 +46,7 @@ pub struct AppState { // Caches the number of done exercises to avoid iterating over all exercises every time. n_done: u16, final_message: String, + state_file: File, // Preallocated buffer for reading and writing the state file. file_buf: Vec, official_exercises: bool, @@ -56,59 +56,22 @@ pub struct AppState { } impl AppState { - // Update the app state from the state file. - fn update_from_file(&mut self) -> StateFileStatus { - self.file_buf.clear(); - self.n_done = 0; - - if File::open(STATE_FILE_NAME) - .and_then(|mut file| file.read_to_end(&mut self.file_buf)) - .is_err() - { - return StateFileStatus::NotRead; - } - - // See `Self::write` for more information about the file format. - let mut lines = self.file_buf.split(|c| *c == b'\n').skip(2); - - let Some(current_exercise_name) = lines.next() else { - return StateFileStatus::NotRead; - }; - - if current_exercise_name.is_empty() || lines.next().is_none() { - return StateFileStatus::NotRead; - } - - let mut done_exercises = hash_set_with_capacity(self.exercises.len()); - - for done_exerise_name in lines { - if done_exerise_name.is_empty() { - break; - } - done_exercises.insert(done_exerise_name); - } - - for (ind, exercise) in self.exercises.iter_mut().enumerate() { - if done_exercises.contains(exercise.name.as_bytes()) { - exercise.done = true; - self.n_done += 1; - } - - if exercise.name.as_bytes() == current_exercise_name { - self.current_exercise_ind = ind; - } - } - - StateFileStatus::Read - } - pub fn new( exercise_infos: Vec, final_message: String, ) -> Result<(Self, StateFileStatus)> { let cmd_runner = CmdRunner::build()?; - - let exercises = exercise_infos + let mut state_file = OpenOptions::new() + .create(true) + .read(true) + .write(true) + .truncate(false) + .open(STATE_FILE_NAME) + .with_context(|| { + format!("Failed to open or create the state file {STATE_FILE_NAME}") + })?; + + let mut exercises = exercise_infos .into_iter() .map(|exercise_info| { // Leaking to be able to borrow in the watch mode `Table`. @@ -126,25 +89,69 @@ impl AppState { test: exercise_info.test, strict_clippy: exercise_info.strict_clippy, hint, - // Updated in `Self::update_from_file`. + // Updated below. done: false, } }) .collect::>(); - let mut slf = Self { - current_exercise_ind: 0, + let mut current_exercise_ind = 0; + let mut n_done = 0; + let mut file_buf = Vec::with_capacity(2048); + let state_file_status = 'block: { + if state_file.read_to_end(&mut file_buf).is_err() { + break 'block StateFileStatus::NotRead; + } + + // See `Self::write` for more information about the file format. + let mut lines = file_buf.split(|c| *c == b'\n').skip(2); + + let Some(current_exercise_name) = lines.next() else { + break 'block StateFileStatus::NotRead; + }; + + if current_exercise_name.is_empty() || lines.next().is_none() { + break 'block StateFileStatus::NotRead; + } + + let mut done_exercises = hash_set_with_capacity(exercises.len()); + + for done_exerise_name in lines { + if done_exerise_name.is_empty() { + break; + } + done_exercises.insert(done_exerise_name); + } + + for (ind, exercise) in exercises.iter_mut().enumerate() { + if done_exercises.contains(exercise.name.as_bytes()) { + exercise.done = true; + n_done += 1; + } + + if exercise.name.as_bytes() == current_exercise_name { + current_exercise_ind = ind; + } + } + + StateFileStatus::Read + }; + + file_buf.clear(); + file_buf.extend_from_slice(STATE_FILE_HEADER); + + let slf = Self { + current_exercise_ind, exercises, - n_done: 0, + n_done, final_message, - file_buf: Vec::with_capacity(2048), + state_file, + file_buf, official_exercises: !Path::new("info.toml").exists(), cmd_runner, vs_code: env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode"), }; - let state_file_status = slf.update_from_file(); - Ok((slf, state_file_status)) } @@ -187,10 +194,8 @@ impl AppState { // - The fourth line is an empty line. // - All remaining lines are the names of done exercises. fn write(&mut self) -> Result<()> { - self.file_buf.clear(); + self.file_buf.truncate(STATE_FILE_HEADER.len()); - self.file_buf - .extend_from_slice(b"DON'T EDIT THIS FILE!\n\n"); self.file_buf .extend_from_slice(self.current_exercise().name.as_bytes()); self.file_buf.push(b'\n'); @@ -202,7 +207,14 @@ impl AppState { } } - fs::write(STATE_FILE_NAME, &self.file_buf) + self.state_file + .rewind() + .with_context(|| format!("Failed to rewind the state file {STATE_FILE_NAME}"))?; + self.state_file + .set_len(0) + .with_context(|| format!("Failed to truncate the state file {STATE_FILE_NAME}"))?; + self.state_file + .write_all(&self.file_buf) .with_context(|| format!("Failed to write the state file {STATE_FILE_NAME}"))?; Ok(()) @@ -440,11 +452,12 @@ impl AppState { } } +const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; +const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n"; const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" 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! | +-------------------------- ------------------------+ @@ -490,6 +503,7 @@ mod tests { exercises: vec![dummy_exercise(), dummy_exercise(), dummy_exercise()], n_done: 0, final_message: String::new(), + state_file: tempfile::tempfile().unwrap(), file_buf: Vec::new(), official_exercises: true, cmd_runner: CmdRunner::build().unwrap(), From fd2bf9f6f66f9ff680925cf0bea86c14c6da07c9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 01:59:04 +0200 Subject: [PATCH 1226/1432] Simplify next_pending_exercise_ind --- src/app_state.rs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index ef2f8741e8..058352a720 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -313,25 +313,22 @@ impl AppState { // Return the index of the next pending exercise or `None` if all exercises are done. fn next_pending_exercise_ind(&self) -> Option { - if self.current_exercise_ind + 1 == self.exercises.len() { - // The last exercise is done. - // Search for exercises not done from the start. - return self.exercises[..self.current_exercise_ind] - .iter() - .position(|exercise| !exercise.done); - } - - // The done exercise isn't the last one. - // Search for a pending exercise after the current one and then from the start. - match self.exercises[self.current_exercise_ind + 1..] - .iter() - .position(|exercise| !exercise.done) - { - Some(ind) => Some(self.current_exercise_ind + 1 + ind), - None => self.exercises[..self.current_exercise_ind] - .iter() - .position(|exercise| !exercise.done), - } + let next_ind = self.current_exercise_ind + 1; + self.exercises + // If the exercise done isn't the last, search for pending exercises after it. + .get(next_ind..) + .and_then(|later_exercises| { + later_exercises + .iter() + .position(|exercise| !exercise.done) + .map(|ind| next_ind + ind) + }) + // Search from the start. + .or_else(|| { + self.exercises[..self.current_exercise_ind] + .iter() + .position(|exercise| !exercise.done) + }) } /// Official exercises: Dump the solution file form the binary and return its path. From 10eb1a3aeecdb9329323228c5e697ccbe1c57508 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 16:01:41 +0200 Subject: [PATCH 1227/1432] Fix header padding --- src/list/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list/state.rs b/src/list/state.rs index 5f0cda37f5..b030aac830 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -189,7 +189,7 @@ impl<'a> ListState<'a> { // Header let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); writer.write_ascii(b" Current State Name")?; - writer.write_ascii(&self.name_col_padding[2..])?; + writer.write_ascii(&self.name_col_padding[4..])?; writer.write_ascii(b"Path")?; next_ln(stdout)?; From bfa00ffbdc8a5799d7ec504bed1d5d8b90388b4c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 16:40:40 +0200 Subject: [PATCH 1228/1432] Update deps --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 048a82ba31..fdb4e45d5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,9 +203,9 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "filetime" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", @@ -469,9 +469,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ "bitflags 2.6.0", "errno", From dbbeb7d4ede2c92dc7dd5027a48e7aae19622e26 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 17:06:37 +0200 Subject: [PATCH 1229/1432] Fix displaying the list message in narrow mode --- src/list/state.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index b030aac830..bc1ac729ad 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -263,14 +263,17 @@ impl<'a> ListState<'a> { } writer.write_ascii(b" | uit list")?; + next_ln(stdout)?; } else { writer.stdout.queue(SetForegroundColor(Color::Magenta))?; writer.write_str(&self.message)?; stdout.queue(ResetColor)?; next_ln(stdout)?; - } - next_ln(stdout)?; + if self.narrow_term { + next_ln(stdout)?; + } + } } stdout.queue(EndSynchronizedUpdate)?.flush() From ab2eb3442ec21b53be36686b37c7af897a03298f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 17:10:39 +0200 Subject: [PATCH 1230/1432] Update changelog --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 391d9c6ccb..19bb8fc356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,44 @@ + + +## 6.3.0 (2024-08-29) + +### Added + +- Add the following exercise lints: + - `forbid(unsafe_code)`: You shouldn't write unsafe code in Rustlings. + - `forbid(unstable_features)`: You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust. + - `forbid(todo)`: You forgot a `todo!()`. + - `forbid(empty_loop)`: This can only happen by mistake in Rustlings. + - `deny(infinite_loop)`: No infinite loops are needed in Rustlings. + - `deny(mem_forget)`: You shouldn't leak memory while still learning Rust. +- Show a link to every exercise file in the list. +- Add scroll padding in the list. +- Break the help footer of the list into two lines when the terminal width isn't big enough. +- Enable scrolling with the mouse in the list. +- `dev check`: Show the progress of checks. +- `dev check`: Check that the length of all exercise names is lower than 32. +- `dev check`: Check if exercise contains no tests and isn't marked with `test = false`. + +### Changed + +- The compilation time when installing Rustlings is reduced. +- Pressing `c` in the list for "continue on" now quits the list after setting the selected exercise as the current one. +- Better highlighting of the solution file after an exercise is done. +- Don't show the output of successful tests anymore. Instead, show the pretty output for tests. +- Be explicit about `q` only quitting the list and not the whole program in the list. +- Be explicit about `r` only resetting one exercise (the selected one) in the list. +- Ignore the standard output of `git init`. +- `threads3`: Remove the queue length and improve tests. +- `errors4`: Use match instead of a comparison chain in the solution. +- `functions3`: Only take `u8` to avoid using a too high number of iterations by mistake. +- `dev check`: Always check with strict Clippy (warnings to errors) when checking the solutions. + +### Fixed + +- Fix the error on some systems about too many open files during the final check of all exercises. +- Fix the list when the terminal height is too low. +- Restore the terminal after an error in the list. + ## 6.2.0 (2024-08-09) From c8d1d9c51fa218ad7e1afccab95c3a05ba0ba7cb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 29 Aug 2024 17:20:17 +0200 Subject: [PATCH 1231/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdb4e45d5d..e1048d4f65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,7 +482,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.2.0" +version = "6.3.0" dependencies = [ "ahash", "anyhow", @@ -499,7 +499,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.2.0" +version = "6.3.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index b332f40bb1..d22aa60c1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ exclude = [ ] [workspace.package] -version = "6.2.0" +version = "6.3.0" authors = [ "Mo Bitar ", # https://github.com/mo8it "Liv ", # https://github.com/shadows-withal @@ -52,7 +52,7 @@ clap = { version = "4.5.16", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" -rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } serde_json = "1.0.127" serde.workspace = true toml_edit.workspace = true From 7d4100ed8a1fb725a2592465f18147828fa9ff75 Mon Sep 17 00:00:00 2001 From: William Ugalde Gamboa Date: Fri, 30 Aug 2024 20:27:26 -0600 Subject: [PATCH 1232/1432] Fix example in 'primitive_types3' hint --- rustlings-macros/info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 504bfd94ff..fd4d94058b 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -253,7 +253,7 @@ require you to type in 100 items (but you certainly can if you want!). For example, you can do: ``` -let array = ["Are we there yet?"; 10]; +let array = ["Are we there yet?"; 100]; ``` Bonus: what are some other things you could have that would return `true` From ac62a3713c7a86172e5ff7d1e5b37f3960aecc35 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 1 Sep 2024 20:31:09 +0200 Subject: [PATCH 1233/1432] Fix typo --- src/app_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_state.rs b/src/app_state.rs index 058352a720..381aaf8ac2 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -331,7 +331,7 @@ impl AppState { }) } - /// Official exercises: Dump the solution file form the binary and return its path. + /// Official exercises: Dump the solution file from the binary and return its path. /// Third-party exercises: Check if a solution file exists and return its path in that case. pub fn current_solution_path(&self) -> Result> { if cfg!(debug_assertions) { From 75a38fa38b65c075f34233f4745eb6d1d7405a39 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 1 Sep 2024 20:44:19 +0200 Subject: [PATCH 1234/1432] Add search to the help footer --- src/list/state.rs | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index bc1ac729ad..49b6d5d8f6 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -44,7 +44,6 @@ pub struct ListState<'a> { term_width: u16, term_height: u16, separator_line: Vec, - narrow_term: bool, show_footer: bool, } @@ -78,7 +77,6 @@ impl<'a> ListState<'a> { term_width: 0, term_height: 0, separator_line: Vec::new(), - narrow_term: false, show_footer: true, }; @@ -96,13 +94,9 @@ impl<'a> ListState<'a> { return; } - let wide_help_footer_width = 95; - // The help footer is shorter when nothing is selected. - self.narrow_term = width < wide_help_footer_width && self.scroll_state.selected().is_some(); - let header_height = 1; - // 2 separator, 1 progress bar, 1-2 footer message. - let footer_height = 4 + u16::from(self.narrow_term); + // 2 separators, 1 progress bar, 2 footer message lines. + let footer_height = 5; self.show_footer = height > header_height + footer_height; if self.show_footer { @@ -227,14 +221,10 @@ impl<'a> ListState<'a> { // Help footer message if self.scroll_state.selected().is_some() { writer.write_str("↓/j ↑/k home/g end/G | ontinue at | eset exercise")?; - if self.narrow_term { - next_ln(stdout)?; - writer = MaxLenWriter::new(stdout, self.term_width as usize); + next_ln(stdout)?; + writer = MaxLenWriter::new(stdout, self.term_width as usize); - writer.write_ascii(b"filter ")?; - } else { - writer.write_ascii(b" | filter ")?; - } + writer.write_ascii(b"earch | filter ")?; } else { // Nothing selected (and nothing shown), so only display filter and quit. writer.write_ascii(b"filter ")?; @@ -263,17 +253,14 @@ impl<'a> ListState<'a> { } writer.write_ascii(b" | uit list")?; - next_ln(stdout)?; } else { writer.stdout.queue(SetForegroundColor(Color::Magenta))?; writer.write_str(&self.message)?; stdout.queue(ResetColor)?; next_ln(stdout)?; - - if self.narrow_term { - next_ln(stdout)?; - } } + + next_ln(stdout)?; } stdout.queue(EndSynchronizedUpdate)?.flush() From f82e47f2afad97d47173a8c4ee60b033df9e12f4 Mon Sep 17 00:00:00 2001 From: Jesse Jackson Date: Sun, 1 Sep 2024 14:48:28 -0500 Subject: [PATCH 1235/1432] style: reduce pre-formatted message line lengths to 80 columns --- rustlings-macros/info.toml | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index fd4d94058b..0fe8343953 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1,6 +1,7 @@ format_version = 1 -welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners! +welcome_message = """ +Is this your first time? Don't worry, Rustlings is made for beginners! We are going to teach you a lot of things about Rust, but before we can get started, here are some notes about how Rustlings operates: @@ -10,15 +11,16 @@ get started, here are some notes about how Rustlings operates: and fix them! 2. Make sure to have your editor open in the `rustlings/` directory. Rustlings will show you the path of the current exercise under the progress bar. Open - the exercise file in your editor, fix errors and save the file. Rustlings will - automatically detect the file change and rerun the exercise. If all errors are - fixed, Rustlings will ask you to move on to the next exercise. + the exercise file in your editor, fix errors and save the file. Rustlings + will automatically detect the file change and rerun the exercise. If all + errors are fixed, Rustlings will ask you to move on to the next exercise. 3. If you're stuck on an exercise, enter `h` to show a hint. -4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, - other learners do too so you can help each other out!""" +4. If an exercise doesn't make sense to you, feel free to open an issue on + GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and + sometimes, other learners do too so you can help each other out!""" -final_message = """We hope you enjoyed learning about the various aspects of Rust! +final_message = """ +We hope you enjoyed learning about the various aspects of Rust! If you noticed any issues, don't hesitate to report them on Github. You can also contribute your own exercises to help the greater community! @@ -122,8 +124,8 @@ hint = """ We know about variables and mutability, but there is another important type of variables available: constants. -Constants are always immutable. They are declared with the keyword `const` instead -of `let`. +Constants are always immutable. They are declared with the keyword `const` +instead of `let`. The type of Constants must always be annotated. @@ -319,7 +321,8 @@ hint = """ In the first function, we create an empty vector and want to push new elements to it. -In the second function, we map the values of the input and collect them into a vector. +In the second function, we map the values of the input and collect them into +a vector. After you've completed both functions, decide for yourself which approach you like better. @@ -332,8 +335,8 @@ What do you think is the more commonly used pattern under Rust developers?""" name = "move_semantics1" dir = "06_move_semantics" hint = """ -So you've got the "cannot borrow `vec` as mutable, as it is not declared as mutable" -error on the line where we push an element to the vector, right? +So you've got the "cannot borrow `vec` as mutable, as it is not declared as +mutable" error on the line where we push an element to the vector, right? The fix for this is going to be adding one keyword, and the addition is NOT on the line where we push to the vector (where the error is). @@ -369,7 +372,8 @@ hint = """ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of `x` immediately after the mutable reference is taken? -Read more about 'Mutable References' in the book's section 'References and Borrowing': +Read more about 'Mutable References' in the book's section 'References and +Borrowing': https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.""" [[exercises]] @@ -508,7 +512,8 @@ name = "strings4" dir = "09_strings" test = false hint = """ -Replace `placeholder` with either `string` or `string_slice` in the `main` function. +Replace `placeholder` with either `string` or `string_slice` in the `main` +function. Example: `placeholder("blue");` @@ -1200,7 +1205,8 @@ hint = """ Is there an implementation of `TryFrom` in the standard library that can both do the required integer conversion and check the range of the input? -Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" +Challenge: Can you make the `TryFrom` implementations generic over many integer +types?""" [[exercises]] name = "as_ref_mut" From 86fc573d7a538539ea32fd84a1cd30c5533cacca Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 1 Sep 2024 22:02:07 +0200 Subject: [PATCH 1236/1432] Remove the footer separators --- src/list/state.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 49b6d5d8f6..7a2d3bf03e 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -43,7 +43,6 @@ pub struct ListState<'a> { filter: Filter, term_width: u16, term_height: u16, - separator_line: Vec, show_footer: bool, } @@ -76,7 +75,6 @@ impl<'a> ListState<'a> { // Set by `set_term_size` term_width: 0, term_height: 0, - separator_line: Vec::new(), show_footer: true, }; @@ -95,14 +93,10 @@ impl<'a> ListState<'a> { } let header_height = 1; - // 2 separators, 1 progress bar, 2 footer message lines. - let footer_height = 5; + // 1 progress bar, 2 footer message lines. + let footer_height = 3; self.show_footer = height > header_height + footer_height; - if self.show_footer { - self.separator_line = "─".as_bytes().repeat(width as usize); - } - self.scroll_state.set_max_n_rows_to_display( height.saturating_sub(header_height + u16::from(self.show_footer) * footer_height) as usize, @@ -202,9 +196,6 @@ impl<'a> ListState<'a> { } if self.show_footer { - stdout.write_all(&self.separator_line)?; - next_ln(stdout)?; - progress_bar( &mut MaxLenWriter::new(stdout, self.term_width as usize), self.app_state.n_done(), @@ -213,9 +204,6 @@ impl<'a> ListState<'a> { )?; next_ln(stdout)?; - stdout.write_all(&self.separator_line)?; - next_ln(stdout)?; - let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); if self.message.is_empty() { // Help footer message From a8b13f5a821d40f11e206922d990aa7b2e66801f Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 1 Sep 2024 22:04:09 +0200 Subject: [PATCH 1237/1432] Remove "exercises" from the end of the progress bar --- src/term.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/term.rs b/src/term.rs index ee8dbf8697..489d6585b8 100644 --- a/src/term.rs +++ b/src/term.rs @@ -5,7 +5,7 @@ use std::{ use crossterm::{ cursor::MoveTo, - style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, + style::{Attribute, Color, SetAttribute, SetForegroundColor}, terminal::{Clear, ClearType}, Command, QueueableCommand, }; @@ -93,20 +93,19 @@ pub fn progress_bar<'a>( total: u16, line_width: u16, ) -> io::Result<()> { + debug_assert!(total < 1000); debug_assert!(progress <= total); const PREFIX: &[u8] = b"Progress: ["; const PREFIX_WIDTH: u16 = PREFIX.len() as u16; - // Leaving the last char empty (_) for `total` > 99. - const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16; + const POSTFIX_WIDTH: u16 = "] xxx/xxx".len() as u16; const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH; const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; if line_width < MIN_LINE_WIDTH { writer.write_ascii(b"Progress: ")?; // Integers are in ASCII. - writer.write_ascii(format!("{progress}/{total}").as_bytes())?; - return writer.write_ascii(b" exercises"); + return writer.write_ascii(format!("{progress}/{total}").as_bytes()); } let stdout = writer.stdout(); @@ -133,8 +132,9 @@ pub fn progress_bar<'a>( } } - stdout.queue(ResetColor)?; - write!(stdout, "] {progress:>3}/{total} exercises") + stdout.queue(SetForegroundColor(Color::Reset))?; + + write!(stdout, "] {progress:>3}/{total}") } pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { From c4fd29541b049f38a9a898974b0b098f6bffe777 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Sun, 1 Sep 2024 18:52:26 -0600 Subject: [PATCH 1238/1432] added a way to search through list, ref #2093 --- src/exercise.rs | 1 + src/list.rs | 55 +++++++++++++++++++++++++++++++++++++--- src/list/scroll_state.rs | 2 +- src/list/state.rs | 37 +++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 11eea63805..6806807628 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -55,6 +55,7 @@ fn run_bin( } /// See `info_file::ExerciseInfo` +#[derive(Debug)] pub struct Exercise { pub dir: Option<&'static str>, pub name: &'static str, diff --git a/src/list.rs b/src/list.rs index 481fb2f41d..8364da7dc0 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Ok, Result}; use crossterm::{ cursor, event::{ @@ -21,6 +21,7 @@ mod state; fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> { let mut list_state = ListState::new(app_state, stdout)?; + let mut is_searching = false; loop { match event::read().context("Failed to read terminal event")? { @@ -31,9 +32,50 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } list_state.message.clear(); + + let curr_key = key.code; + + if is_searching { + match curr_key { + KeyCode::Esc | KeyCode::Enter => { + is_searching = false; // not sure why rust analyzer thinks this is unused + list_state.search_query.clear(); + return Ok(()); + } + KeyCode::Char(k) => { + eprintln!("pressed while searching {:?}", curr_key); + + list_state.search_query.push(k); + list_state.message.push_str("search:"); + list_state.message.push_str(&list_state.search_query); + list_state.message.push_str("|"); + + list_state.select_if_matches_search_query(); + + list_state.draw(stdout)?; + continue; + } + KeyCode::Backspace => { + list_state.search_query.pop(); + list_state.message.push_str("search:"); + list_state.message.push_str(&list_state.search_query); + list_state.message.push_str("|"); + + list_state.select_if_matches_search_query(); + + list_state.draw(stdout)?; + continue; + } + _ => { + continue; + } + } + } match key.code { - KeyCode::Char('q') => return Ok(()), + KeyCode::Char('q') => { + return Ok(()); + } KeyCode::Down | KeyCode::Char('j') => list_state.select_next(), KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => list_state.select_first(), @@ -66,9 +108,16 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> return Ok(()); } } + KeyCode::Char('s') | KeyCode::Char('/') => { + eprintln!("starting search"); + list_state.message.push_str("search:|"); + is_searching = true; + } // Redraw to remove the message. KeyCode::Esc => (), - _ => continue, + _ => { + continue; + } } } Event::Mouse(event) => match event.kind { diff --git a/src/list/scroll_state.rs b/src/list/scroll_state.rs index 25a737366f..2c02ed4f7e 100644 --- a/src/list/scroll_state.rs +++ b/src/list/scroll_state.rs @@ -46,7 +46,7 @@ impl ScrollState { self.selected } - fn set_selected(&mut self, selected: usize) { + pub fn set_selected(&mut self, selected: usize) { self.selected = Some(selected); self.update_offset(); } diff --git a/src/list/state.rs b/src/list/state.rs index 49b6d5d8f6..117740cf79 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -45,6 +45,7 @@ pub struct ListState<'a> { term_height: u16, separator_line: Vec, show_footer: bool, + pub search_query: String, } impl<'a> ListState<'a> { @@ -78,6 +79,7 @@ impl<'a> ListState<'a> { term_height: 0, separator_line: Vec::new(), show_footer: true, + search_query: String::new(), }; slf.set_term_size(width, height); @@ -356,6 +358,41 @@ impl<'a> ListState<'a> { Ok(()) } + + pub fn select_if_matches_search_query(&mut self) { + eprintln!("search query: {:?}", self.search_query); + + let idx = self + .app_state + .exercises() + .iter() + .enumerate() + .find_map(|(i, s)| { + if s.name.contains(self.search_query.as_str()) { + Some(i) + } else { + None + } + }); + eprintln!("idx: {:?}", idx); + + match idx { + Some(i) => { + // ? do we need this function call? + // let exercise_ind = self.selected_to_exercise_ind(i).unwrap(); + let exercise_ind = i; + self.scroll_state.set_selected(exercise_ind); + eprintln!("exercise_ind: {:?}", exercise_ind); + self.update_rows(); + } + None => { + let msg = String::from("[NOT FOUND]") + &self.message.clone(); + self.message.clear(); + self.message.push_str(&msg); + } + } + + } // Return `true` if there was something to select. pub fn selected_to_current_exercise(&mut self) -> Result { From 388f8da97f2ff47011d1bebbf0d153ea85741562 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Sun, 1 Sep 2024 19:03:33 -0600 Subject: [PATCH 1239/1432] removed debug statements --- src/exercise.rs | 1 - src/list.rs | 7 +------ src/list/state.rs | 4 ---- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 6806807628..11eea63805 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -55,7 +55,6 @@ fn run_bin( } /// See `info_file::ExerciseInfo` -#[derive(Debug)] pub struct Exercise { pub dir: Option<&'static str>, pub name: &'static str, diff --git a/src/list.rs b/src/list.rs index 8364da7dc0..351b0b5a90 100644 --- a/src/list.rs +++ b/src/list.rs @@ -43,8 +43,6 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> return Ok(()); } KeyCode::Char(k) => { - eprintln!("pressed while searching {:?}", curr_key); - list_state.search_query.push(k); list_state.message.push_str("search:"); list_state.message.push_str(&list_state.search_query); @@ -109,15 +107,12 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } } KeyCode::Char('s') | KeyCode::Char('/') => { - eprintln!("starting search"); list_state.message.push_str("search:|"); is_searching = true; } // Redraw to remove the message. KeyCode::Esc => (), - _ => { - continue; - } + _ => continue, } } Event::Mouse(event) => match event.kind { diff --git a/src/list/state.rs b/src/list/state.rs index 53fed9a05c..7dfd6b5a38 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -348,8 +348,6 @@ impl<'a> ListState<'a> { } pub fn select_if_matches_search_query(&mut self) { - eprintln!("search query: {:?}", self.search_query); - let idx = self .app_state .exercises() @@ -362,7 +360,6 @@ impl<'a> ListState<'a> { None } }); - eprintln!("idx: {:?}", idx); match idx { Some(i) => { @@ -370,7 +367,6 @@ impl<'a> ListState<'a> { // let exercise_ind = self.selected_to_exercise_ind(i).unwrap(); let exercise_ind = i; self.scroll_state.set_selected(exercise_ind); - eprintln!("exercise_ind: {:?}", exercise_ind); self.update_rows(); } None => { From 92a1214dcdbead48f520cb4f1d8e53f59b5619e1 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Sun, 1 Sep 2024 19:05:23 -0600 Subject: [PATCH 1240/1432] passes clippy lints --- src/list.rs | 12 ++++++------ src/list/state.rs | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/list.rs b/src/list.rs index 351b0b5a90..0f0643c6fa 100644 --- a/src/list.rs +++ b/src/list.rs @@ -32,9 +32,9 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } list_state.message.clear(); - + let curr_key = key.code; - + if is_searching { match curr_key { KeyCode::Esc | KeyCode::Enter => { @@ -46,10 +46,10 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.search_query.push(k); list_state.message.push_str("search:"); list_state.message.push_str(&list_state.search_query); - list_state.message.push_str("|"); - + list_state.message.push('|'); + list_state.select_if_matches_search_query(); - + list_state.draw(stdout)?; continue; } @@ -57,7 +57,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.search_query.pop(); list_state.message.push_str("search:"); list_state.message.push_str(&list_state.search_query); - list_state.message.push_str("|"); + list_state.message.push('|'); list_state.select_if_matches_search_query(); diff --git a/src/list/state.rs b/src/list/state.rs index 7dfd6b5a38..be05f3b7eb 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -346,7 +346,7 @@ impl<'a> ListState<'a> { Ok(()) } - + pub fn select_if_matches_search_query(&mut self) { let idx = self .app_state @@ -360,7 +360,7 @@ impl<'a> ListState<'a> { None } }); - + match idx { Some(i) => { // ? do we need this function call? @@ -375,7 +375,6 @@ impl<'a> ListState<'a> { self.message.push_str(&msg); } } - } // Return `true` if there was something to select. From 547a9d947bf62052c263a6ee935b5451d3c0dbf1 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Mon, 2 Sep 2024 10:45:45 -0600 Subject: [PATCH 1241/1432] escape/enter no longer exits the list, exits only the search --- src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index 0f0643c6fa..b41c8913f6 100644 --- a/src/list.rs +++ b/src/list.rs @@ -40,7 +40,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> KeyCode::Esc | KeyCode::Enter => { is_searching = false; // not sure why rust analyzer thinks this is unused list_state.search_query.clear(); - return Ok(()); + continue; } KeyCode::Char(k) => { list_state.search_query.push(k); From abf1228a0a837e71d744f5f2881f386387802cc7 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Mon, 2 Sep 2024 10:59:23 -0600 Subject: [PATCH 1242/1432] search now filters the list first --- src/list.rs | 2 +- src/list/state.rs | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/list.rs b/src/list.rs index b41c8913f6..069cdda01f 100644 --- a/src/list.rs +++ b/src/list.rs @@ -38,7 +38,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> if is_searching { match curr_key { KeyCode::Esc | KeyCode::Enter => { - is_searching = false; // not sure why rust analyzer thinks this is unused + is_searching = false; list_state.search_query.clear(); continue; } diff --git a/src/list/state.rs b/src/list/state.rs index be05f3b7eb..8d5bf5b8b8 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -352,6 +352,27 @@ impl<'a> ListState<'a> { .app_state .exercises() .iter() + .filter_map(|exercise| { + match self.filter() { + Filter::None => { + Some(exercise) + }, + Filter::Done => { + if exercise.done { + Some(exercise) + } else { + None + } + }, + Filter::Pending => { + if !exercise.done { + Some(exercise) + } else { + None + } + } + } + }) .enumerate() .find_map(|(i, s)| { if s.name.contains(self.search_query.as_str()) { @@ -363,8 +384,6 @@ impl<'a> ListState<'a> { match idx { Some(i) => { - // ? do we need this function call? - // let exercise_ind = self.selected_to_exercise_ind(i).unwrap(); let exercise_ind = i; self.scroll_state.set_selected(exercise_ind); self.update_rows(); From 71494264ca7303071cfeafbdbf137e11e653190f Mon Sep 17 00:00:00 2001 From: Adhyan Date: Mon, 2 Sep 2024 11:02:17 -0600 Subject: [PATCH 1243/1432] fixed clippy lints --- src/list/state.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 8d5bf5b8b8..9e813a0fcf 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -352,26 +352,10 @@ impl<'a> ListState<'a> { .app_state .exercises() .iter() - .filter_map(|exercise| { - match self.filter() { - Filter::None => { - Some(exercise) - }, - Filter::Done => { - if exercise.done { - Some(exercise) - } else { - None - } - }, - Filter::Pending => { - if !exercise.done { - Some(exercise) - } else { - None - } - } - } + .filter(|exercise| match self.filter() { + Filter::None => true, + Filter::Done => exercise.done, + Filter::Pending => !exercise.done, }) .enumerate() .find_map(|(i, s)| { From 948e16e3c783bff20736b356d9f961af3bb00784 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 14:40:24 -0600 Subject: [PATCH 1244/1432] moved continue to end of if-block --- src/list.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/list.rs b/src/list.rs index 069cdda01f..dc224f00a1 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Ok, Result}; +use anyhow::{Context, Result}; use crossterm::{ cursor, event::{ @@ -40,7 +40,6 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> KeyCode::Esc | KeyCode::Enter => { is_searching = false; list_state.search_query.clear(); - continue; } KeyCode::Char(k) => { list_state.search_query.push(k); @@ -51,7 +50,6 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.select_if_matches_search_query(); list_state.draw(stdout)?; - continue; } KeyCode::Backspace => { list_state.search_query.pop(); @@ -62,12 +60,10 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.select_if_matches_search_query(); list_state.draw(stdout)?; - continue; - } - _ => { - continue; } + _ => {} } + continue; } match key.code { From fea917c8f2a8e9464ab5625f29f06fd622a26a04 Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 14:52:09 -0600 Subject: [PATCH 1245/1432] removed unnecessary update_rows() call and minor refactoring --- src/list.rs | 6 ++---- src/list/state.rs | 14 ++++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/list.rs b/src/list.rs index dc224f00a1..857d0ce97c 100644 --- a/src/list.rs +++ b/src/list.rs @@ -67,9 +67,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } match key.code { - KeyCode::Char('q') => { - return Ok(()); - } + KeyCode::Char('q') => return Ok(()), KeyCode::Down | KeyCode::Char('j') => list_state.select_next(), KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(), KeyCode::Home | KeyCode::Char('g') => list_state.select_first(), @@ -102,7 +100,7 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> return Ok(()); } } - KeyCode::Char('s') | KeyCode::Char('/') => { + KeyCode::Char('s' | '/') => { list_state.message.push_str("search:|"); is_searching = true; } diff --git a/src/list/state.rs b/src/list/state.rs index 8d5bf5b8b8..f150766752 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -374,23 +374,21 @@ impl<'a> ListState<'a> { } }) .enumerate() - .find_map(|(i, s)| { - if s.name.contains(self.search_query.as_str()) { - Some(i) + .find_map(|(idx, exercise)| { + if exercise.name.contains(self.search_query.as_str()) { + Some(idx) } else { None } }); match idx { - Some(i) => { - let exercise_ind = i; + Some(x) => { + let exercise_ind = x; self.scroll_state.set_selected(exercise_ind); - self.update_rows(); } None => { - let msg = String::from("[NOT FOUND]") + &self.message.clone(); - self.message.clear(); + let msg = String::from(" (not found)"); self.message.push_str(&msg); } } From 47148e78a32dc39001ec69642eeb2d87f4b4e1ad Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 15:03:25 -0600 Subject: [PATCH 1246/1432] replaced enumerate() with position(); converted select_if_matches_search_query to apply_search_query --- src/list.rs | 14 ++--------- src/list/state.rs | 62 ++++++++++++++++++++++------------------------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/list.rs b/src/list.rs index 857d0ce97c..5d7c8dd972 100644 --- a/src/list.rs +++ b/src/list.rs @@ -43,22 +43,12 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } KeyCode::Char(k) => { list_state.search_query.push(k); - list_state.message.push_str("search:"); - list_state.message.push_str(&list_state.search_query); - list_state.message.push('|'); - - list_state.select_if_matches_search_query(); - + list_state.apply_search_query(); list_state.draw(stdout)?; } KeyCode::Backspace => { list_state.search_query.pop(); - list_state.message.push_str("search:"); - list_state.message.push_str(&list_state.search_query); - list_state.message.push('|'); - - list_state.select_if_matches_search_query(); - + list_state.apply_search_query(); list_state.draw(stdout)?; } _ => {} diff --git a/src/list/state.rs b/src/list/state.rs index f150766752..be6c42e590 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -347,40 +347,36 @@ impl<'a> ListState<'a> { Ok(()) } - pub fn select_if_matches_search_query(&mut self) { + pub fn apply_search_query(&mut self) { + self.message.push_str("search:"); + self.message.push_str(&self.search_query); + self.message.push('|'); + + if self.search_query.is_empty() { return; } + let idx = self - .app_state - .exercises() - .iter() - .filter_map(|exercise| { - match self.filter() { - Filter::None => { - Some(exercise) - }, - Filter::Done => { - if exercise.done { - Some(exercise) - } else { - None - } - }, - Filter::Pending => { - if !exercise.done { - Some(exercise) - } else { - None - } - } - } - }) - .enumerate() - .find_map(|(idx, exercise)| { - if exercise.name.contains(self.search_query.as_str()) { - Some(idx) - } else { - None - } - }); + .app_state + .exercises() + .iter() + .filter_map(|exercise| { + match self.filter() { + Filter::None => Some(exercise), + Filter::Done if exercise.done => Some(exercise), + Filter::Pending if !exercise.done => Some(exercise), + _ => None, + } + }) + .position(|exercise| exercise.name.contains(self.search_query.as_str())); + + match idx { + Some(exercise_ind) => { + self.scroll_state.set_selected(exercise_ind); + } + None => { + let msg = String::from(" (not found)"); + self.message.push_str(&msg); + } + } match idx { Some(x) => { From f463cf86627411696922bd703e8c875eec7b367b Mon Sep 17 00:00:00 2001 From: Adhyan Date: Tue, 3 Sep 2024 15:10:44 -0600 Subject: [PATCH 1247/1432] passes clippy lints and removed extra code from the merge --- src/list/state.rs | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index be6c42e590..60077c78de 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -352,21 +352,20 @@ impl<'a> ListState<'a> { self.message.push_str(&self.search_query); self.message.push('|'); - if self.search_query.is_empty() { return; } + if self.search_query.is_empty() { + return; + } let idx = self - .app_state - .exercises() - .iter() - .filter_map(|exercise| { - match self.filter() { - Filter::None => Some(exercise), - Filter::Done if exercise.done => Some(exercise), - Filter::Pending if !exercise.done => Some(exercise), - _ => None, - } - }) - .position(|exercise| exercise.name.contains(self.search_query.as_str())); + .app_state + .exercises() + .iter() + .filter(|exercise| match self.filter() { + Filter::None => true, + Filter::Done => exercise.done, + Filter::Pending => !exercise.done, + }) + .position(|exercise| exercise.name.contains(self.search_query.as_str())); match idx { Some(exercise_ind) => { @@ -377,17 +376,6 @@ impl<'a> ListState<'a> { self.message.push_str(&msg); } } - - match idx { - Some(x) => { - let exercise_ind = x; - self.scroll_state.set_selected(exercise_ind); - } - None => { - let msg = String::from(" (not found)"); - self.message.push_str(&msg); - } - } } // Return `true` if there was something to select. From da8b3d143a5b7462baf912c58cc768f7cd210ab2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 4 Sep 2024 01:05:30 +0200 Subject: [PATCH 1248/1432] Final touches to searching --- src/list.rs | 16 +++++++--------- src/list/state.rs | 19 +++++++------------ 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/list.rs b/src/list.rs index 5d7c8dd972..5e3038480b 100644 --- a/src/list.rs +++ b/src/list.rs @@ -33,26 +33,24 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> list_state.message.clear(); - let curr_key = key.code; - if is_searching { - match curr_key { + match key.code { KeyCode::Esc | KeyCode::Enter => { is_searching = false; list_state.search_query.clear(); } - KeyCode::Char(k) => { - list_state.search_query.push(k); + KeyCode::Char(c) => { + list_state.search_query.push(c); list_state.apply_search_query(); - list_state.draw(stdout)?; } KeyCode::Backspace => { list_state.search_query.pop(); list_state.apply_search_query(); - list_state.draw(stdout)?; } - _ => {} + _ => continue, } + + list_state.draw(stdout)?; continue; } @@ -91,8 +89,8 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } } KeyCode::Char('s' | '/') => { - list_state.message.push_str("search:|"); is_searching = true; + list_state.apply_search_query(); } // Redraw to remove the message. KeyCode::Esc => (), diff --git a/src/list/state.rs b/src/list/state.rs index 60077c78de..f932fab4db 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -37,6 +37,7 @@ pub enum Filter { pub struct ListState<'a> { /// Footer message to be displayed if not empty. pub message: String, + pub search_query: String, app_state: &'a mut AppState, scroll_state: ScrollState, name_col_padding: Vec, @@ -44,7 +45,6 @@ pub struct ListState<'a> { term_width: u16, term_height: u16, show_footer: bool, - pub search_query: String, } impl<'a> ListState<'a> { @@ -69,6 +69,7 @@ impl<'a> ListState<'a> { let mut slf = Self { message: String::with_capacity(128), + search_query: String::new(), app_state, scroll_state, name_col_padding, @@ -77,7 +78,6 @@ impl<'a> ListState<'a> { term_width: 0, term_height: 0, show_footer: true, - search_query: String::new(), }; slf.set_term_size(width, height); @@ -356,25 +356,20 @@ impl<'a> ListState<'a> { return; } - let idx = self + let ind = self .app_state .exercises() .iter() - .filter(|exercise| match self.filter() { + .filter(|exercise| match self.filter { Filter::None => true, Filter::Done => exercise.done, Filter::Pending => !exercise.done, }) .position(|exercise| exercise.name.contains(self.search_query.as_str())); - match idx { - Some(exercise_ind) => { - self.scroll_state.set_selected(exercise_ind); - } - None => { - let msg = String::from(" (not found)"); - self.message.push_str(&msg); - } + match ind { + Some(exercise_ind) => self.scroll_state.set_selected(exercise_ind), + None => self.message.push_str(" (not found)"), } } From 03baa471d94f9dfe7575b985e227df1d4e7803d4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 4 Sep 2024 01:07:08 +0200 Subject: [PATCH 1249/1432] Simplify handling `p` in list --- src/list.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/list.rs b/src/list.rs index 5e3038480b..cfd3720ce9 100644 --- a/src/list.rs +++ b/src/list.rs @@ -72,15 +72,15 @@ fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> } } KeyCode::Char('p') => { - let message = if list_state.filter() == Filter::Pending { + if list_state.filter() == Filter::Pending { list_state.set_filter(Filter::None); - "Disabled filter PENDING" + list_state.message.push_str("Disabled filter PENDING"); } else { list_state.set_filter(Filter::Pending); - "Enabled filter PENDING β”‚ Press p again to disable the filter" - }; - - list_state.message.push_str(message); + list_state.message.push_str( + "Enabled filter PENDING β”‚ Press p again to disable the filter", + ); + } } KeyCode::Char('r') => list_state.reset_selected()?, KeyCode::Char('c') => { From e5ed11528855f6dddc5759df3426ff1296aba87e Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 4 Sep 2024 01:20:48 +0200 Subject: [PATCH 1250/1432] Match filter once --- src/list/state.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index f932fab4db..468049abc2 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -356,16 +356,17 @@ impl<'a> ListState<'a> { return; } - let ind = self - .app_state - .exercises() - .iter() - .filter(|exercise| match self.filter { - Filter::None => true, - Filter::Done => exercise.done, - Filter::Pending => !exercise.done, - }) - .position(|exercise| exercise.name.contains(self.search_query.as_str())); + let is_search_result = |exercise: &Exercise| exercise.name.contains(&self.search_query); + let mut iter = self.app_state.exercises().iter(); + let ind = match self.filter { + Filter::None => iter.position(is_search_result), + Filter::Done => iter + .filter(|exercise| exercise.done) + .position(is_search_result), + Filter::Pending => iter + .filter(|exercise| !exercise.done) + .position(is_search_result), + }; match ind { Some(exercise_ind) => self.scroll_state.set_selected(exercise_ind), From 247bd19f93e11fb037c945ff1dc464a1d1713471 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 4 Sep 2024 02:19:45 +0200 Subject: [PATCH 1251/1432] Canonicalize exercise paths only once --- src/app_state.rs | 27 ++++++++++++++++++++++++++- src/exercise.rs | 19 +++++++++++++++++-- src/list/state.rs | 4 ++-- src/run.rs | 9 ++++++--- src/term.rs | 41 ++++++++++++++++++++++------------------- src/watch/state.rs | 6 ++++-- 6 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 381aaf8ac2..7123d11a39 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -3,7 +3,7 @@ use std::{ env, fs::{File, OpenOptions}, io::{self, Read, Seek, StdoutLock, Write}, - path::Path, + path::{Path, MAIN_SEPARATOR_STR}, process::{Command, Stdio}, thread, }; @@ -15,6 +15,7 @@ use crate::{ embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, + term, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; @@ -71,6 +72,7 @@ impl AppState { format!("Failed to open or create the state file {STATE_FILE_NAME}") })?; + let dir_canonical_path = term::canonicalize("exercises"); let mut exercises = exercise_infos .into_iter() .map(|exercise_info| { @@ -82,10 +84,32 @@ impl AppState { let dir = exercise_info.dir.map(|dir| &*dir.leak()); let hint = exercise_info.hint.leak().trim_ascii(); + let canonical_path = dir_canonical_path.as_deref().map(|dir_canonical_path| { + let mut canonical_path; + if let Some(dir) = dir { + canonical_path = String::with_capacity( + 2 + dir_canonical_path.len() + dir.len() + name.len(), + ); + canonical_path.push_str(dir_canonical_path); + canonical_path.push_str(MAIN_SEPARATOR_STR); + canonical_path.push_str(dir); + } else { + canonical_path = + String::with_capacity(1 + dir_canonical_path.len() + name.len()); + canonical_path.push_str(dir_canonical_path); + } + + canonical_path.push_str(MAIN_SEPARATOR_STR); + canonical_path.push_str(name); + canonical_path.push_str(".rs"); + canonical_path + }); + Exercise { dir, name, path, + canonical_path, test: exercise_info.test, strict_clippy: exercise_info.strict_clippy, hint, @@ -486,6 +510,7 @@ mod tests { dir: None, name: "0", path: "exercises/0.rs", + canonical_path: None, test: false, strict_clippy: false, hint: "", diff --git a/src/exercise.rs b/src/exercise.rs index 11eea63805..7fb2343cbf 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -7,7 +7,7 @@ use std::io::{self, StdoutLock, Write}; use crate::{ cmd::CmdRunner, - term::{terminal_file_link, write_ansi}, + term::{self, terminal_file_link, write_ansi, CountedWrite}, }; /// The initial capacity of the output buffer. @@ -18,7 +18,11 @@ pub fn solution_link_line(stdout: &mut StdoutLock, solution_path: &str) -> io::R stdout.write_all(b"Solution")?; stdout.queue(ResetColor)?; stdout.write_all(b" for comparison: ")?; - terminal_file_link(stdout, solution_path, Color::Cyan)?; + if let Some(canonical_path) = term::canonicalize(solution_path) { + terminal_file_link(stdout, solution_path, &canonical_path, Color::Cyan)?; + } else { + stdout.write_all(solution_path.as_bytes())?; + } stdout.write_all(b"\n") } @@ -60,12 +64,23 @@ pub struct Exercise { pub name: &'static str, /// Path of the exercise file starting with the `exercises/` directory. pub path: &'static str, + pub canonical_path: Option, pub test: bool, pub strict_clippy: bool, pub hint: &'static str, pub done: bool, } +impl Exercise { + pub fn terminal_file_link<'a>(&self, writer: &mut impl CountedWrite<'a>) -> io::Result<()> { + if let Some(canonical_path) = self.canonical_path.as_deref() { + return terminal_file_link(writer, self.path, canonical_path, Color::Blue); + } + + writer.write_str(self.path) + } +} + pub trait RunnableExercise { fn name(&self) -> &str; fn dir(&self) -> Option<&str>; diff --git a/src/list/state.rs b/src/list/state.rs index 468049abc2..ed7c71f68f 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -13,7 +13,7 @@ use std::{ use crate::{ app_state::AppState, exercise::Exercise, - term::{progress_bar, terminal_file_link, CountedWrite, MaxLenWriter}, + term::{progress_bar, CountedWrite, MaxLenWriter}, }; use super::scroll_state::ScrollState; @@ -158,7 +158,7 @@ impl<'a> ListState<'a> { if self.app_state.vs_code() { writer.write_str(exercise.path)?; } else { - terminal_file_link(&mut writer, exercise.path, Color::Blue)?; + exercise.terminal_file_link(&mut writer)?; } next_ln(stdout)?; diff --git a/src/run.rs b/src/run.rs index 929b4751d0..f0faa69cba 100644 --- a/src/run.rs +++ b/src/run.rs @@ -11,7 +11,6 @@ use std::{ use crate::{ app_state::{AppState, ExercisesProgress}, exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, - term::terminal_file_link, }; pub fn run(app_state: &mut AppState) -> Result<()> { @@ -26,7 +25,9 @@ pub fn run(app_state: &mut AppState) -> Result<()> { app_state.set_pending(app_state.current_exercise_ind())?; stdout.write_all(b"Ran ")?; - terminal_file_link(&mut stdout, app_state.current_exercise().path, Color::Blue)?; + app_state + .current_exercise() + .terminal_file_link(&mut stdout)?; stdout.write_all(b" with errors\n")?; exit(1); } @@ -46,7 +47,9 @@ pub fn run(app_state: &mut AppState) -> Result<()> { match app_state.done_current_exercise(&mut stdout)? { ExercisesProgress::CurrentPending | ExercisesProgress::NewPending => { stdout.write_all(b"Next exercise: ")?; - terminal_file_link(&mut stdout, app_state.current_exercise().path, Color::Blue)?; + app_state + .current_exercise() + .terminal_file_link(&mut stdout)?; stdout.write_all(b"\n")?; } ExercisesProgress::AllDone => (), diff --git a/src/term.rs b/src/term.rs index 489d6585b8..5b557ecf40 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,14 +1,13 @@ -use std::{ - fmt, fs, - io::{self, BufRead, StdoutLock, Write}, -}; - use crossterm::{ cursor::MoveTo, style::{Attribute, Color, SetAttribute, SetForegroundColor}, terminal::{Clear, ClearType}, Command, QueueableCommand, }; +use std::{ + fmt, fs, + io::{self, BufRead, StdoutLock, Write}, +}; pub struct MaxLenWriter<'a, 'b> { pub stdout: &'a mut StdoutLock<'b>, @@ -151,25 +150,29 @@ pub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> { stdout.write_all(b"\n") } +/// Canonicalize, convert to string and remove verbatim part on Windows. +pub fn canonicalize(path: &str) -> Option { + fs::canonicalize(path) + .ok()? + .into_os_string() + .into_string() + .ok() + .map(|mut path| { + // Windows itself can't handle its verbatim paths. + if cfg!(windows) && path.as_bytes().starts_with(br"\\?\") { + path.drain(..4); + } + + path + }) +} + pub fn terminal_file_link<'a>( writer: &mut impl CountedWrite<'a>, path: &str, + canonical_path: &str, color: Color, ) -> io::Result<()> { - let canonical_path = fs::canonicalize(path).ok(); - - let Some(canonical_path) = canonical_path.as_deref().and_then(|p| p.to_str()) else { - return writer.write_str(path); - }; - - // Windows itself can't handle its verbatim paths. - #[cfg(windows)] - let canonical_path = if canonical_path.len() > 5 && &canonical_path[0..4] == r"\\?\" { - &canonical_path[4..] - } else { - canonical_path - }; - writer .stdout() .queue(SetForegroundColor(color))? diff --git a/src/watch/state.rs b/src/watch/state.rs index 1c2e2a9ab8..fe9e27482b 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -11,7 +11,7 @@ use crate::{ app_state::{AppState, ExercisesProgress}, clear_terminal, exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, - term::{progress_bar, terminal_file_link}, + term::progress_bar, }; #[derive(PartialEq, Eq)] @@ -184,7 +184,9 @@ impl<'a> WatchState<'a> { )?; stdout.write_all(b"\nCurrent exercise: ")?; - terminal_file_link(stdout, self.app_state.current_exercise().path, Color::Blue)?; + self.app_state + .current_exercise() + .terminal_file_link(stdout)?; stdout.write_all(b"\n\n")?; self.show_prompt(stdout)?; From 5eb3dee59c57e320f87b2101bbfb2e4aac1b153b Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 00:21:24 +0200 Subject: [PATCH 1252/1432] Create solution even if the solution's directory is missing --- src/embedded.rs | 88 ++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index 1dce46c5e4..51a14b6a8d 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Error, Result}; use std::{ - fs::{create_dir, OpenOptions}, - io::{self, Write}, + fs::{self, create_dir}, + io, }; use crate::info_file::ExerciseInfo; @@ -9,29 +9,6 @@ use crate::info_file::ExerciseInfo; /// Contains all embedded files. pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); -#[derive(Clone, Copy)] -pub enum WriteStrategy { - IfNotExists, - Overwrite, -} - -impl WriteStrategy { - fn write(self, path: &str, content: &[u8]) -> Result<()> { - let file = match self { - Self::IfNotExists => OpenOptions::new().create_new(true).write(true).open(path), - Self::Overwrite => OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(path), - }; - - file.with_context(|| format!("Failed to open the file `{path}` in write mode"))? - .write_all(content) - .with_context(|| format!("Failed to write the file {path}")) - } -} - // Files related to one exercise. struct ExerciseFiles { // The content of the exercise file. @@ -42,6 +19,16 @@ struct ExerciseFiles { dir_ind: usize, } +fn create_dir_if_not_exists(path: &str) -> Result<()> { + if let Err(e) = create_dir(path) { + if e.kind() != io::ErrorKind::AlreadyExists { + return Err(Error::from(e).context(format!("Failed to create the directory {path}"))); + } + } + + Ok(()) +} + // A directory in the `exercises/` directory. pub struct ExerciseDir { pub name: &'static str, @@ -55,21 +42,13 @@ impl ExerciseDir { let mut dir_path = String::with_capacity(20 + self.name.len()); dir_path.push_str("exercises/"); dir_path.push_str(self.name); - - if let Err(e) = create_dir(&dir_path) { - if e.kind() == io::ErrorKind::AlreadyExists { - return Ok(()); - } - - return Err( - Error::from(e).context(format!("Failed to create the directory {dir_path}")) - ); - } + create_dir_if_not_exists(&dir_path)?; let mut readme_path = dir_path; readme_path.push_str("/README.md"); - WriteStrategy::Overwrite.write(&readme_path, self.readme) + fs::write(&readme_path, self.readme) + .with_context(|| format!("Failed to write the file {readme_path}")) } } @@ -86,17 +65,31 @@ impl EmbeddedFiles { pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> Result<()> { create_dir("exercises").context("Failed to create the directory `exercises`")?; - WriteStrategy::IfNotExists.write( + fs::write( "exercises/README.md", include_bytes!("../exercises/README.md"), - )?; + ) + .context("Failed to write the file exercises/README.md")?; for dir in self.exercise_dirs { dir.init_on_disk()?; } + let mut exercise_path = String::with_capacity(64); + let prefix = "exercises/"; + exercise_path.push_str(prefix); + for (exercise_info, exercise_files) in exercise_infos.iter().zip(self.exercise_files) { - WriteStrategy::IfNotExists.write(&exercise_info.path(), exercise_files.exercise)?; + let dir = &self.exercise_dirs[exercise_files.dir_ind]; + + exercise_path.truncate(prefix.len()); + exercise_path.push_str(dir.name); + exercise_path.push('/'); + exercise_path.push_str(&exercise_info.name); + exercise_path.push_str(".rs"); + + fs::write(&exercise_path, exercise_files.exercise) + .with_context(|| format!("Failed to write the exercise file {exercise_path}"))?; } Ok(()) @@ -107,7 +100,8 @@ impl EmbeddedFiles { let dir = &self.exercise_dirs[exercise_files.dir_ind]; dir.init_on_disk()?; - WriteStrategy::Overwrite.write(path, exercise_files.exercise) + fs::write(path, exercise_files.exercise) + .with_context(|| format!("Failed to write the exercise file {path}")) } /// Write the solution file to disk and return its path. @@ -116,19 +110,25 @@ impl EmbeddedFiles { exercise_ind: usize, exercise_name: &str, ) -> Result { + create_dir_if_not_exists("solutions")?; + let exercise_files = &self.exercise_files[exercise_ind]; let dir = &self.exercise_dirs[exercise_files.dir_ind]; // 14 = 10 + 1 + 3 // solutions/ + / + .rs - let mut solution_path = String::with_capacity(14 + dir.name.len() + exercise_name.len()); - solution_path.push_str("solutions/"); - solution_path.push_str(dir.name); + let mut dir_path = String::with_capacity(14 + dir.name.len() + exercise_name.len()); + dir_path.push_str("solutions/"); + dir_path.push_str(dir.name); + create_dir_if_not_exists(&dir_path)?; + + let mut solution_path = dir_path; solution_path.push('/'); solution_path.push_str(exercise_name); solution_path.push_str(".rs"); - WriteStrategy::Overwrite.write(&solution_path, exercise_files.solution)?; + fs::write(&solution_path, exercise_files.solution) + .with_context(|| format!("Failed to write the solution file {solution_path}"))?; Ok(solution_path) } From 17877366b7d563077c1457d42cd500b5c2eefc7c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 01:55:31 +0200 Subject: [PATCH 1253/1432] Update deps --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1048d4f65..6f78f3ef73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.16" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -242,9 +242,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -549,9 +549,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -612,9 +612,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.76" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index d22aa60c1e..15e2d1c6fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,12 +48,12 @@ include = [ [dependencies] ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.86" -clap = { version = "4.5.16", features = ["derive"] } +clap = { version = "4.5.17", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } -serde_json = "1.0.127" +serde_json = "1.0.128" serde.workspace = true toml_edit.workspace = true From 2d0860fe1bd0aef512313617d8a26e9f118d2cd2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 02:11:19 +0200 Subject: [PATCH 1254/1432] Hide input and disable its line buffering --- Cargo.lock | 1 + Cargo.toml | 3 +++ src/main.rs | 12 ++---------- src/watch.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f78f3ef73..25949c4f63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,6 +490,7 @@ dependencies = [ "crossterm", "notify-debouncer-mini", "os_pipe", + "rustix", "rustlings-macros", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 15e2d1c6fd..da4fc4d4ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,9 @@ serde_json = "1.0.128" serde.workspace = true toml_edit.workspace = true +[target.'cfg(not(windows))'.dependencies] +rustix = { version = "0.38.35", default-features = false, features = ["std", "stdio", "termios"] } + [dev-dependencies] tempfile = "3.12.0" diff --git a/src/main.rs b/src/main.rs index e53cd5a7da..fe4b3dcda4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use std::{ }; use term::{clear_terminal, press_enter_prompt}; -use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; +use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile}; mod app_state; mod cargo_toml; @@ -130,15 +130,7 @@ fn main() -> Result<()> { ) }; - loop { - match watch::watch(&mut app_state, notify_exercise_names)? { - 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::list(&mut app_state)?, - } - } + watch::watch(&mut app_state, notify_exercise_names)?; } Some(Subcommands::Run { name }) => { if let Some(name) = name { diff --git a/src/watch.rs b/src/watch.rs index e14d3c5771..be8409f77a 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -11,7 +11,10 @@ use std::{ time::Duration, }; -use crate::app_state::{AppState, ExercisesProgress}; +use crate::{ + app_state::{AppState, ExercisesProgress}, + list, +}; use self::{ notify_event::NotifyEventHandler, @@ -33,15 +36,14 @@ enum WatchEvent { /// Returned by the watch mode to indicate what to do afterwards. #[must_use] -pub enum WatchExit { +enum WatchExit { /// Exit the program. Shutdown, /// Enter the list mode and restart the watch mode afterwards. List, } -/// `notify_exercise_names` as None activates the manual run mode. -pub fn watch( +fn run_watch( app_state: &mut AppState, notify_exercise_names: Option<&'static [&'static [u8]]>, ) -> Result { @@ -110,6 +112,48 @@ pub fn watch( Ok(WatchExit::Shutdown) } +fn watch_list_loop( + app_state: &mut AppState, + notify_exercise_names: Option<&'static [&'static [u8]]>, +) -> Result<()> { + loop { + match run_watch(app_state, notify_exercise_names)? { + WatchExit::Shutdown => break Ok(()), + // 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::list(app_state)?, + } + } +} + +/// `notify_exercise_names` as None activates the manual run mode. +pub fn watch( + app_state: &mut AppState, + notify_exercise_names: Option<&'static [&'static [u8]]>, +) -> Result<()> { + #[cfg(not(windows))] + { + let stdin_fd = rustix::stdio::stdin(); + let mut termios = rustix::termios::tcgetattr(stdin_fd)?; + let original_local_modes = termios.local_modes; + // Disable stdin line buffering and hide input. + termios.local_modes -= + rustix::termios::LocalModes::ICANON | rustix::termios::LocalModes::ECHO; + rustix::termios::tcsetattr(stdin_fd, rustix::termios::OptionalActions::Now, &termios)?; + + let res = watch_list_loop(app_state, notify_exercise_names); + + termios.local_modes = original_local_modes; + rustix::termios::tcsetattr(stdin_fd, rustix::termios::OptionalActions::Now, &termios)?; + + res + } + + #[cfg(windows)] + watch_list_loop(app_state, notify_exercise_names) +} + const QUIT_MSG: &[u8] = b" We hope you're enjoying learning Rust! If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. From aa3eda70e5727f9665a97fadcce081a96909e9b1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 17:12:26 +0200 Subject: [PATCH 1255/1432] Simplify handling terminal events for unbuffered stdin --- src/watch.rs | 1 - src/watch/terminal_event.rs | 42 ++++++------------------------------- 2 files changed, 6 insertions(+), 37 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index be8409f77a..ee5dd74de3 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -95,7 +95,6 @@ fn run_watch( break; } WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?, - WatchEvent::Input(InputEvent::Unrecognized) => watch_state.render(&mut stdout)?, WatchEvent::FileChange { exercise_ind } => { watch_state.handle_file_change(exercise_ind, &mut stdout)?; } diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 3e8c272a98..8e2e241792 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -9,13 +9,9 @@ pub enum InputEvent { Hint, List, Quit, - Unrecognized, } pub fn terminal_event_handler(tx: Sender, manual_run: bool) { - // Only send `Unrecognized` on ENTER if the last input wasn't valid. - let mut last_input_valid = false; - let last_input_event = loop { let terminal_event = match event::read() { Ok(v) => v, @@ -34,39 +30,13 @@ pub fn terminal_event_handler(tx: Sender, manual_run: bool) { KeyEventKind::Press => (), } - if key.modifiers != KeyModifiers::NONE { - last_input_valid = false; - continue; - } - let input_event = match key.code { - KeyCode::Enter => { - if last_input_valid { - continue; - } - - InputEvent::Unrecognized - } - KeyCode::Char(c) => { - let input_event = match c { - 'n' => InputEvent::Next, - 'h' => InputEvent::Hint, - 'l' => break InputEvent::List, - 'q' => break InputEvent::Quit, - 'r' if manual_run => InputEvent::Run, - _ => { - last_input_valid = false; - continue; - } - }; - - last_input_valid = true; - input_event - } - _ => { - last_input_valid = false; - continue; - } + KeyCode::Char('n') => InputEvent::Next, + KeyCode::Char('h') => InputEvent::Hint, + KeyCode::Char('l') => break InputEvent::List, + KeyCode::Char('q') => break InputEvent::Quit, + KeyCode::Char('r') if manual_run => InputEvent::Run, + _ => continue, }; if tx.send(WatchEvent::Input(input_event)).is_err() { From 51b8d2ab2542eb6115bbfdbe7a404993dfcd0749 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 17:23:56 +0200 Subject: [PATCH 1256/1432] Remove unused import --- src/watch/terminal_event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 8e2e241792..0762151975 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -1,4 +1,4 @@ -use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; +use crossterm::event::{self, Event, KeyCode, KeyEventKind}; use std::sync::mpsc::Sender; use super::WatchEvent; From dcad002057acfb1a41513fb421275116ea946ca3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 17:32:59 +0200 Subject: [PATCH 1257/1432] Only render when needed --- src/app_state.rs | 4 ++-- src/run.rs | 2 +- src/watch.rs | 10 +++------- src/watch/state.rs | 8 ++++++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 7123d11a39..ed723c22cd 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -24,10 +24,10 @@ const STATE_FILE_NAME: &str = ".rustlings-state.txt"; pub enum ExercisesProgress { // All exercises are done. AllDone, - // The current exercise failed and is still pending. - CurrentPending, // A new exercise is now pending. NewPending, + // The current exercise is still pending. + CurrentPending, } pub enum StateFileStatus { diff --git a/src/run.rs b/src/run.rs index f0faa69cba..a969164c40 100644 --- a/src/run.rs +++ b/src/run.rs @@ -45,7 +45,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { } match app_state.done_current_exercise(&mut stdout)? { - ExercisesProgress::CurrentPending | ExercisesProgress::NewPending => { + ExercisesProgress::NewPending | ExercisesProgress::CurrentPending => { stdout.write_all(b"Next exercise: ")?; app_state .current_exercise() diff --git a/src/watch.rs b/src/watch.rs index ee5dd74de3..bca3832158 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -83,13 +83,11 @@ fn run_watch( match event { WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise(&mut stdout)? { ExercisesProgress::AllDone => break, - ExercisesProgress::CurrentPending => watch_state.render(&mut stdout)?, ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?, + ExercisesProgress::CurrentPending => (), }, WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?, - WatchEvent::Input(InputEvent::List) => { - return Ok(WatchExit::List); - } + WatchEvent::Input(InputEvent::List) => return Ok(WatchExit::List), WatchEvent::Input(InputEvent::Quit) => { stdout.write_all(QUIT_MSG)?; break; @@ -99,9 +97,7 @@ fn run_watch( watch_state.handle_file_change(exercise_ind, &mut stdout)?; } WatchEvent::TerminalResize => watch_state.render(&mut stdout)?, - WatchEvent::NotifyErr(e) => { - return Err(Error::from(e).context(NOTIFY_ERR)); - } + WatchEvent::NotifyErr(e) => return Err(Error::from(e).context(NOTIFY_ERR)), WatchEvent::TerminalEventErr(e) => { return Err(Error::from(e).context("Terminal event listener failed")); } diff --git a/src/watch/state.rs b/src/watch/state.rs index fe9e27482b..75a0c9e15e 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -195,7 +195,11 @@ impl<'a> WatchState<'a> { } pub fn show_hint(&mut self, stdout: &mut StdoutLock) -> io::Result<()> { - self.show_hint = true; - self.render(stdout) + if !self.show_hint { + self.show_hint = true; + self.render(stdout)?; + } + + Ok(()) } } From bcc2a136c8b086a660b8e656c2cd9398f47435f4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 17:37:34 +0200 Subject: [PATCH 1258/1432] Add error message when unable to get terminal size --- src/list.rs | 2 +- src/list/state.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/list.rs b/src/list.rs index cfd3720ce9..9f243a1724 100644 --- a/src/list.rs +++ b/src/list.rs @@ -20,7 +20,7 @@ mod scroll_state; mod state; fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> { - let mut list_state = ListState::new(app_state, stdout)?; + let mut list_state = ListState::build(app_state, stdout)?; let mut is_searching = false; loop { diff --git a/src/list/state.rs b/src/list/state.rs index ed7c71f68f..5bdbca77eb 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -48,7 +48,7 @@ pub struct ListState<'a> { } impl<'a> ListState<'a> { - pub fn new(app_state: &'a mut AppState, stdout: &mut StdoutLock) -> io::Result { + pub fn build(app_state: &'a mut AppState, stdout: &mut StdoutLock) -> Result { stdout.queue(Clear(ClearType::All))?; let name_col_title_len = 4; @@ -64,7 +64,7 @@ impl<'a> ListState<'a> { let n_rows_with_filter = app_state.exercises().len(); let selected = app_state.current_exercise_ind(); - let (width, height) = terminal::size()?; + let (width, height) = terminal::size().context("Failed to get the terminal size")?; let scroll_state = ScrollState::new(n_rows_with_filter, Some(selected), 5); let mut slf = Self { From 9faa5d3aa48f7a94ed87e61ad6ea659579f1311a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 5 Sep 2024 17:45:27 +0200 Subject: [PATCH 1259/1432] Avoid asking for terminal size on each rendering --- src/watch.rs | 8 +++++--- src/watch/state.rs | 26 ++++++++++++++++++++------ src/watch/terminal_event.rs | 4 ++-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index bca3832158..900eba7c09 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -29,7 +29,7 @@ mod terminal_event; enum WatchEvent { Input(InputEvent), FileChange { exercise_ind: usize }, - TerminalResize, + TerminalResize { width: u16 }, NotifyErr(notify::Error), TerminalEventErr(io::Error), } @@ -72,7 +72,7 @@ fn run_watch( None }; - let mut watch_state = WatchState::new(app_state, manual_run); + let mut watch_state = WatchState::build(app_state, manual_run)?; let mut stdout = io::stdout().lock(); watch_state.run_current_exercise(&mut stdout)?; @@ -96,7 +96,9 @@ fn run_watch( WatchEvent::FileChange { exercise_ind } => { watch_state.handle_file_change(exercise_ind, &mut stdout)?; } - WatchEvent::TerminalResize => watch_state.render(&mut stdout)?, + WatchEvent::TerminalResize { width } => { + watch_state.update_term_width(width, &mut stdout)?; + } WatchEvent::NotifyErr(e) => return Err(Error::from(e).context(NOTIFY_ERR)), WatchEvent::TerminalEventErr(e) => { return Err(Error::from(e).context("Terminal event listener failed")); diff --git a/src/watch/state.rs b/src/watch/state.rs index 75a0c9e15e..e66cbeebe6 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use crossterm::{ style::{ Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor, @@ -27,17 +27,23 @@ pub struct WatchState<'a> { show_hint: bool, done_status: DoneStatus, manual_run: bool, + term_width: u16, } impl<'a> WatchState<'a> { - pub fn new(app_state: &'a mut AppState, manual_run: bool) -> Self { - Self { + pub fn build(app_state: &'a mut AppState, manual_run: bool) -> Result { + let term_width = terminal::size() + .context("Failed to get the terminal size")? + .0; + + Ok(Self { app_state, output: Vec::with_capacity(OUTPUT_CAPACITY), show_hint: false, done_status: DoneStatus::Pending, manual_run, - } + term_width, + }) } pub fn run_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> { @@ -175,12 +181,11 @@ impl<'a> WatchState<'a> { )?; } - let line_width = terminal::size()?.0; progress_bar( stdout, self.app_state.n_done(), self.app_state.exercises().len() as u16, - line_width, + self.term_width, )?; stdout.write_all(b"\nCurrent exercise: ")?; @@ -202,4 +207,13 @@ impl<'a> WatchState<'a> { Ok(()) } + + pub fn update_term_width(&mut self, width: u16, stdout: &mut StdoutLock) -> io::Result<()> { + if self.term_width != width { + self.term_width = width; + self.render(stdout)?; + } + + Ok(()) + } } diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 0762151975..ca3a846431 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -43,8 +43,8 @@ pub fn terminal_event_handler(tx: Sender, manual_run: bool) { return; } } - Event::Resize(_, _) => { - if tx.send(WatchEvent::TerminalResize).is_err() { + Event::Resize(width, _) => { + if tx.send(WatchEvent::TerminalResize { width }).is_err() { return; } } From 2d26358602fc1cd0a026f634b38c34e7b4618cc9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 6 Sep 2024 15:40:25 +0200 Subject: [PATCH 1260/1432] Use the thread builder and handle the spawn error --- clippy.toml | 3 +++ src/app_state.rs | 11 +++++++++-- src/dev/check.rs | 27 ++++++++++++++++++--------- src/watch.rs | 6 ++++-- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/clippy.toml b/clippy.toml index 81e372a747..4a5dd06a6c 100644 --- a/clippy.toml +++ b/clippy.toml @@ -10,4 +10,7 @@ disallowed-methods = [ "std::collections::HashSet::with_capacity", # Inefficient. Use `.queue(…)` instead. "crossterm::style::style", + # Use `thread::Builder::spawn` instead and handle the error. + "std::thread::spawn", + "std::thread::Scope::spawn", ] diff --git a/src/app_state.rs b/src/app_state.rs index ed723c22cd..ecb46898d9 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -388,13 +388,20 @@ impl AppState { let handles = self .exercises .iter() - .map(|exercise| s.spawn(|| exercise.run_exercise(None, &self.cmd_runner))) + .map(|exercise| { + thread::Builder::new() + .spawn_scoped(s, || exercise.run_exercise(None, &self.cmd_runner)) + }) .collect::>(); - for (exercise_ind, handle) in handles.into_iter().enumerate() { + for (exercise_ind, spawn_res) in handles.into_iter().enumerate() { write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; stdout.flush()?; + let Ok(handle) = spawn_res else { + return Ok(AllExercisesCheck::CheckedUntil(exercise_ind)); + }; + let Ok(success) = handle.join().unwrap() else { return Ok(AllExercisesCheck::CheckedUntil(exercise_ind)); }; diff --git a/src/dev/check.rs b/src/dev/check.rs index a6db3c21ae..b7e23dca88 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -185,12 +185,14 @@ fn check_exercises_unsolved( return None; } - Some(( - exercise_info.name.as_str(), - thread::spawn(|| exercise_info.run_exercise(None, cmd_runner)), - )) + Some( + thread::Builder::new() + .spawn(|| exercise_info.run_exercise(None, cmd_runner)) + .map(|handle| (exercise_info.name.as_str(), handle)), + ) }) - .collect::>(); + .collect::, _>>() + .context("Failed to spawn a thread to check if an exercise is already solved")?; let n_handles = handles.len(); write!(stdout, "Progress: 0/{n_handles}")?; @@ -226,7 +228,9 @@ fn check_exercises(info_file: &'static InfoFile, cmd_runner: &'static CmdRunner) Ordering::Equal => (), } - let handle = thread::spawn(move || check_exercises_unsolved(info_file, cmd_runner)); + let handle = thread::Builder::new() + .spawn(move || check_exercises_unsolved(info_file, cmd_runner)) + .context("Failed to spawn a thread to check if any exercise is already solved")?; let info_file_paths = check_info_file_exercises(info_file)?; check_unexpected_files("exercises", &info_file_paths)?; @@ -253,7 +257,7 @@ fn check_solutions( .exercises .iter() .map(|exercise_info| { - thread::spawn(move || { + thread::Builder::new().spawn(move || { let sol_path = exercise_info.sol_path(); if !Path::new(&sol_path).exists() { if require_solutions { @@ -274,7 +278,8 @@ fn check_solutions( } }) }) - .collect::>(); + .collect::, _>>() + .context("Failed to spawn a thread to check a solution")?; let mut sol_paths = hash_set_with_capacity(info_file.exercises.len()); let mut fmt_cmd = Command::new("rustfmt"); @@ -322,7 +327,11 @@ fn check_solutions( } stdout.write_all(b"\n")?; - let handle = thread::spawn(move || check_unexpected_files("solutions", &sol_paths)); + let handle = thread::Builder::new() + .spawn(move || check_unexpected_files("solutions", &sol_paths)) + .context( + "Failed to spawn a thread to check for unexpected files in the solutions directory", + )?; if !fmt_cmd .status() diff --git a/src/watch.rs b/src/watch.rs index 900eba7c09..a44b565676 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,4 +1,4 @@ -use anyhow::{Error, Result}; +use anyhow::{Context, Error, Result}; use notify_debouncer_mini::{ new_debouncer, notify::{self, RecursiveMode}, @@ -77,7 +77,9 @@ fn run_watch( let mut stdout = io::stdout().lock(); watch_state.run_current_exercise(&mut stdout)?; - thread::spawn(move || terminal_event_handler(tx, manual_run)); + thread::Builder::new() + .spawn(move || terminal_event_handler(tx, manual_run)) + .context("Failed to spawn a thread to handle terminal events")?; while let Ok(event) = rx.recv() { match event { From 938500fd2fddcec191ed896b42756e34dd29b9a3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 6 Sep 2024 16:35:12 +0200 Subject: [PATCH 1261/1432] Fix dev check in official repo --- dev/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 7bde359c79..2accf3a19f 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -217,3 +217,5 @@ empty_loop = "forbid" infinite_loop = "deny" # You shouldn't leak memory while still learning Rust mem_forget = "deny" +# Currently, there are no disallowed methods. This line avoids problems when developing Rustlings. +disallowed_methods = "allow" From 2b7caf6fcb7fc128bc5c0be93913d3482c87b35b Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 6 Sep 2024 16:36:36 +0200 Subject: [PATCH 1262/1432] Too polite :P --- src/dev/check.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index b7e23dca88..5a7aaed415 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -41,10 +41,10 @@ fn check_cargo_toml( if old_bins != new_bins { if cfg!(debug_assertions) { - bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it. Then run `cargo run -- dev check` again"); + bail!("The file `dev/Cargo.toml` is outdated. Run `cargo run -- dev update` to update it. Then run `cargo run -- dev check` again"); } - bail!("The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it. Then run `rustlings dev check` again"); + bail!("The file `Cargo.toml` is outdated. Run `rustlings dev update` to update it. Then run `rustlings dev check` again"); } Ok(()) From 9a25309c1c86b1e27938446b7c4b2de024514ea4 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Wed, 11 Sep 2024 16:47:27 +0200 Subject: [PATCH 1263/1432] Remove redundant enum definition task The exercise enums2.rs already contains a task where an identical enum has to be defined. --- exercises/08_enums/enums3.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index 66c4675f9b..cb05f657c2 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -4,7 +4,11 @@ struct Point { } enum Message { - // TODO: Implement the message variant types based on their usage below. + Resize { width: u64, height: u64 }, + Move(Point), + Echo(String), + ChangeColor(u8, u8, u8), + Quit, } struct State { From 1f624d4c2a4ee0441ffce842591a8e37e329d309 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 12 Sep 2024 15:26:40 +0200 Subject: [PATCH 1264/1432] Add rust-analyzer.toml file --- dev-rust-analyzer.toml | 4 ++++ src/init.rs | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 dev-rust-analyzer.toml diff --git a/dev-rust-analyzer.toml b/dev-rust-analyzer.toml new file mode 100644 index 0000000000..b0b88fae21 --- /dev/null +++ b/dev-rust-analyzer.toml @@ -0,0 +1,4 @@ +# rust-analyzer configuration file +# DO NOT edit what is already defined. +# You may add new configurations as needed. +check.extraArgs = ["--profile", "test"] diff --git a/src/init.rs b/src/init.rs index 332bf52e31..24dcfa6878 100644 --- a/src/init.rs +++ b/src/init.rs @@ -130,6 +130,10 @@ pub fn init() -> Result<()> { fs::write("Cargo.toml", updated_cargo_toml) .context("Failed to create the file `rustlings/Cargo.toml`")?; + let ra_toml = include_str!("../dev-rust-analyzer.toml"); + fs::write("rust-analyzer.toml", ra_toml) + .context("Failed to create the file `rustlings/rust-analyzer.toml`")?; + fs::write(".gitignore", GITIGNORE) .context("Failed to create the file `rustlings/.gitignore`")?; From 88e10a9e54eb06217a3c45d330b6a3ba66577b01 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 12 Sep 2024 15:46:09 +0200 Subject: [PATCH 1265/1432] hardcode ratoml in init.rs --- dev-rust-analyzer.toml | 4 ---- src/init.rs | 8 ++++++-- 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 dev-rust-analyzer.toml diff --git a/dev-rust-analyzer.toml b/dev-rust-analyzer.toml deleted file mode 100644 index b0b88fae21..0000000000 --- a/dev-rust-analyzer.toml +++ /dev/null @@ -1,4 +0,0 @@ -# rust-analyzer configuration file -# DO NOT edit what is already defined. -# You may add new configurations as needed. -check.extraArgs = ["--profile", "test"] diff --git a/src/init.rs b/src/init.rs index 24dcfa6878..632243254c 100644 --- a/src/init.rs +++ b/src/init.rs @@ -130,8 +130,7 @@ pub fn init() -> Result<()> { fs::write("Cargo.toml", updated_cargo_toml) .context("Failed to create the file `rustlings/Cargo.toml`")?; - let ra_toml = include_str!("../dev-rust-analyzer.toml"); - fs::write("rust-analyzer.toml", ra_toml) + fs::write("rust-analyzer.toml", RATOML) .context("Failed to create the file `rustlings/rust-analyzer.toml`")?; fs::write(".gitignore", GITIGNORE) @@ -173,6 +172,11 @@ const INIT_SOLUTION_FILE: &[u8] = b"fn main() { } "; +const RATOML: &[u8] = br#"# rust-analyzer configuration file +# DO NOT edit what is already defined. +# You may add new configurations as needed. +check.extraArgs = ["--profile", "test"]"#; + const GITIGNORE: &[u8] = b"Cargo.lock target/ .vscode/ From 83d1275d721303439ad436465995fb2bd699d4fd Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 12 Sep 2024 14:10:50 +0200 Subject: [PATCH 1266/1432] Add missing # in comment --- src/exercise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exercise.rs b/src/exercise.rs index 7fb2343cbf..849082847a 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -131,7 +131,7 @@ pub trait RunnableExercise { let mut clippy_cmd = cmd_runner.cargo("clippy", bin_name, output.as_deref_mut()); - // `--profile test` is required to also check code with `[cfg(test)]`. + // `--profile test` is required to also check code with `#[cfg(test)]`. if FORCE_STRICT_CLIPPY || self.strict_clippy() { clippy_cmd.args(["--profile", "test", "--", "-D", "warnings"]); } else { From 234a61a3eeb1c10b0bd6ac5bd1dfe3d09438c04f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 12 Sep 2024 15:40:21 +0200 Subject: [PATCH 1267/1432] Update deps --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25949c4f63..7d84cc5817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" [[package]] name = "autocfg" @@ -460,18 +460,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -530,18 +530,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -659,9 +659,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utf8parse" diff --git a/Cargo.toml b/Cargo.toml index da4fc4d4ab..5b554a6aa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.209", features = ["derive"] } +serde = { version = "1.0.210", features = ["derive"] } toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] } [package] @@ -47,7 +47,7 @@ include = [ [dependencies] ahash = { version = "0.8.11", default-features = false } -anyhow = "1.0.86" +anyhow = "1.0.88" clap = { version = "4.5.17", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } @@ -58,7 +58,7 @@ serde.workspace = true toml_edit.workspace = true [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.38.35", default-features = false, features = ["std", "stdio", "termios"] } +rustix = { version = "0.38.37", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] tempfile = "3.12.0" From 664228ef8b910640b353acd7445fa14b9d16ad9f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 12 Sep 2024 16:34:33 +0200 Subject: [PATCH 1268/1432] Improve quit message --- src/watch.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/watch.rs b/src/watch.rs index a44b565676..e910fb71b1 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -154,8 +154,9 @@ pub fn watch( } const QUIT_MSG: &[u8] = b" + We hope you're enjoying learning Rust! -If you want to continue working on the exercises at a later point, you can simply run `rustlings` again. +If you want to continue working on the exercises at a later point, you can simply run `rustlings` again in this directory. "; const NOTIFY_ERR: &str = " From 3947c4de284cb82945055a0fe802c2755e951bb9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 12 Sep 2024 17:45:42 +0200 Subject: [PATCH 1269/1432] Pause input while running an exercise --- src/watch.rs | 23 +++++--------- src/watch/notify_event.rs | 4 +-- src/watch/state.rs | 24 ++++++++++++-- src/watch/terminal_event.rs | 63 ++++++++++++++++++++++++------------- 4 files changed, 72 insertions(+), 42 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index e910fb71b1..c937bfbe7d 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Error, Result}; +use anyhow::{Error, Result}; use notify_debouncer_mini::{ new_debouncer, notify::{self, RecursiveMode}, @@ -7,7 +7,6 @@ use std::{ io::{self, Write}, path::Path, sync::mpsc::channel, - thread, time::Duration, }; @@ -16,11 +15,7 @@ use crate::{ list, }; -use self::{ - notify_event::NotifyEventHandler, - state::WatchState, - terminal_event::{terminal_event_handler, InputEvent}, -}; +use self::{notify_event::NotifyEventHandler, state::WatchState, terminal_event::InputEvent}; mod notify_event; mod state; @@ -47,7 +42,7 @@ fn run_watch( app_state: &mut AppState, notify_exercise_names: Option<&'static [&'static [u8]]>, ) -> Result { - let (tx, rx) = channel(); + let (watch_event_sender, watch_event_receiver) = channel(); let mut manual_run = false; // Prevent dropping the guard until the end of the function. @@ -56,7 +51,7 @@ fn run_watch( let mut debouncer = new_debouncer( Duration::from_millis(200), NotifyEventHandler { - tx: tx.clone(), + sender: watch_event_sender.clone(), exercise_names, }, ) @@ -72,16 +67,12 @@ fn run_watch( None }; - let mut watch_state = WatchState::build(app_state, manual_run)?; - + let mut watch_state = WatchState::build(app_state, watch_event_sender, manual_run)?; let mut stdout = io::stdout().lock(); - watch_state.run_current_exercise(&mut stdout)?; - thread::Builder::new() - .spawn(move || terminal_event_handler(tx, manual_run)) - .context("Failed to spawn a thread to handle terminal events")?; + watch_state.run_current_exercise(&mut stdout)?; - while let Ok(event) = rx.recv() { + while let Ok(event) = watch_event_receiver.recv() { match event { WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise(&mut stdout)? { ExercisesProgress::AllDone => break, diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index 74716409c2..9b23525907 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -4,7 +4,7 @@ use std::sync::mpsc::Sender; use super::WatchEvent; pub struct NotifyEventHandler { - pub tx: Sender, + pub sender: Sender, /// Used to report which exercise was modified. pub exercise_names: &'static [&'static [u8]], } @@ -47,6 +47,6 @@ impl notify_debouncer_mini::DebounceEventHandler for NotifyEventHandler { // An error occurs when the receiver is dropped. // After dropping the receiver, the debouncer guard should also be dropped. - let _ = self.tx.send(output_event); + let _ = self.sender.send(output_event); } } diff --git a/src/watch/state.rs b/src/watch/state.rs index e66cbeebe6..6e7600180c 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -5,7 +5,11 @@ use crossterm::{ }, terminal, QueueableCommand, }; -use std::io::{self, StdoutLock, Write}; +use std::{ + io::{self, StdoutLock, Write}, + sync::mpsc::Sender, + thread, +}; use crate::{ app_state::{AppState, ExercisesProgress}, @@ -14,6 +18,11 @@ use crate::{ term::progress_bar, }; +use super::{ + terminal_event::{terminal_event_handler, InputPauseGuard}, + WatchEvent, +}; + #[derive(PartialEq, Eq)] enum DoneStatus { DoneWithSolution(String), @@ -31,11 +40,19 @@ pub struct WatchState<'a> { } impl<'a> WatchState<'a> { - pub fn build(app_state: &'a mut AppState, manual_run: bool) -> Result { + pub fn build( + app_state: &'a mut AppState, + watch_event_sender: Sender, + manual_run: bool, + ) -> Result { let term_width = terminal::size() .context("Failed to get the terminal size")? .0; + thread::Builder::new() + .spawn(move || terminal_event_handler(watch_event_sender, manual_run)) + .context("Failed to spawn a thread to handle terminal events")?; + Ok(Self { app_state, output: Vec::with_capacity(OUTPUT_CAPACITY), @@ -47,6 +64,9 @@ impl<'a> WatchState<'a> { } pub fn run_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> { + // Ignore any input until running the exercise is done. + let _input_pause_guard = InputPauseGuard::scoped_pause(); + self.show_hint = false; writeln!( diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index ca3a846431..2a1dfdcf21 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -1,8 +1,32 @@ use crossterm::event::{self, Event, KeyCode, KeyEventKind}; -use std::sync::mpsc::Sender; +use std::sync::{ + atomic::{AtomicBool, Ordering::Relaxed}, + mpsc::Sender, +}; use super::WatchEvent; +static INPUT_PAUSED: AtomicBool = AtomicBool::new(false); + +// Private unit type to force using the constructor function. +#[must_use = "When the guard is dropped, the input is unpaused"] +pub struct InputPauseGuard(()); + +impl InputPauseGuard { + #[inline] + pub fn scoped_pause() -> Self { + INPUT_PAUSED.store(true, Relaxed); + Self(()) + } +} + +impl Drop for InputPauseGuard { + #[inline] + fn drop(&mut self) { + INPUT_PAUSED.store(false, Relaxed); + } +} + pub enum InputEvent { Run, Next, @@ -11,46 +35,41 @@ pub enum InputEvent { Quit, } -pub fn terminal_event_handler(tx: Sender, manual_run: bool) { - let last_input_event = loop { - let terminal_event = match event::read() { - Ok(v) => v, - Err(e) => { - // If `send` returns an error, then the receiver is dropped and - // a shutdown has been already initialized. - let _ = tx.send(WatchEvent::TerminalEventErr(e)); - return; - } - }; - - match terminal_event { - Event::Key(key) => { +pub fn terminal_event_handler(sender: Sender, manual_run: bool) { + let last_watch_event = loop { + match event::read() { + Ok(Event::Key(key)) => { match key.kind { KeyEventKind::Release | KeyEventKind::Repeat => continue, KeyEventKind::Press => (), } + if INPUT_PAUSED.load(Relaxed) { + continue; + } + let input_event = match key.code { KeyCode::Char('n') => InputEvent::Next, KeyCode::Char('h') => InputEvent::Hint, - KeyCode::Char('l') => break InputEvent::List, - KeyCode::Char('q') => break InputEvent::Quit, + KeyCode::Char('l') => break WatchEvent::Input(InputEvent::List), + KeyCode::Char('q') => break WatchEvent::Input(InputEvent::Quit), KeyCode::Char('r') if manual_run => InputEvent::Run, _ => continue, }; - if tx.send(WatchEvent::Input(input_event)).is_err() { + if sender.send(WatchEvent::Input(input_event)).is_err() { return; } } - Event::Resize(width, _) => { - if tx.send(WatchEvent::TerminalResize { width }).is_err() { + Ok(Event::Resize(width, _)) => { + if sender.send(WatchEvent::TerminalResize { width }).is_err() { return; } } - Event::FocusGained | Event::FocusLost | Event::Mouse(_) => continue, + Ok(Event::FocusGained | Event::FocusLost | Event::Mouse(_)) => continue, + Err(e) => break WatchEvent::TerminalEventErr(e), } }; - let _ = tx.send(WatchEvent::Input(last_input_event)); + let _ = sender.send(last_watch_event); } From 0513660b05e8dd45ba7bb25fff89b4fd089b14ea Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 13 Sep 2024 14:56:46 +0200 Subject: [PATCH 1270/1432] Allow dead code for all exercises and solutions --- dev/Cargo.toml | 14 ++++++++------ exercises/08_enums/enums2.rs | 2 -- exercises/10_modules/modules2.rs | 1 - exercises/15_traits/traits3.rs | 2 -- exercises/19_smart_pointers/rc1.rs | 1 - solutions/08_enums/enums2.rs | 2 -- solutions/10_modules/modules2.rs | 1 - solutions/15_traits/traits3.rs | 2 -- solutions/19_smart_pointers/rc1.rs | 1 - 9 files changed, 8 insertions(+), 18 deletions(-) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 2accf3a19f..fbb093d1a4 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -203,19 +203,21 @@ panic = "abort" panic = "abort" [lints.rust] -# You shouldn't write unsafe code in Rustlings +# You shouldn't write unsafe code in Rustlings! unsafe_code = "forbid" -# You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust +# You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust. unstable_features = "forbid" [lints.clippy] -# You forgot a `todo!()` +# You forgot a `todo!()`! todo = "forbid" -# This can only happen by mistake in Rustlings +# This can only happen by mistake in Rustlings. empty_loop = "forbid" -# No infinite loops are needed in Rustlings +# No infinite loops are needed in Rustlings. infinite_loop = "deny" -# You shouldn't leak memory while still learning Rust +# You shouldn't leak memory while still learning Rust! mem_forget = "deny" +# Dead code warnings can't be avoided in some exercises and might distract while learning. +dead_code = "allow" # Currently, there are no disallowed methods. This line avoids problems when developing Rustlings. disallowed_methods = "allow" diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index 29ed1b6f5b..d70f6398d1 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - #[derive(Debug)] struct Point { x: u64, diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 02eb80a957..782a70eace 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -1,7 +1,6 @@ // You can bring module paths into scopes and provide new names for them with // the `use` and `as` keywords. -#[allow(dead_code)] mod delicious_snacks { // TODO: Add the following two `use` statements after fixing them. // use self::fruits::PEAR as ???; diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 2e8969ebe3..c244650da6 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - trait Licensed { // TODO: Add a default implementation for `licensing_info` so that // implementors like the two structs below can share that default behavior diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index 48e19dc03c..ecd3438701 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -8,7 +8,6 @@ use std::rc::Rc; #[derive(Debug)] struct Sun; -#[allow(dead_code)] #[derive(Debug)] enum Planet { Mercury(Rc), diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs index 2ee0553a66..07aee262f7 100644 --- a/solutions/08_enums/enums2.rs +++ b/solutions/08_enums/enums2.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - #[derive(Debug)] struct Point { x: u64, diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs index 298d76eb1d..55c316d706 100644 --- a/solutions/10_modules/modules2.rs +++ b/solutions/10_modules/modules2.rs @@ -1,4 +1,3 @@ -#[allow(dead_code)] mod delicious_snacks { // Added `pub` and used the expected alias after `as`. pub use self::fruits::PEAR as fruit; diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs index 747d9190d2..3d8ec85ead 100644 --- a/solutions/15_traits/traits3.rs +++ b/solutions/15_traits/traits3.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - trait Licensed { fn licensing_info(&self) -> String { "Default license".to_string() diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs index 512eb9cea2..c0a41abfa8 100644 --- a/solutions/19_smart_pointers/rc1.rs +++ b/solutions/19_smart_pointers/rc1.rs @@ -8,7 +8,6 @@ use std::rc::Rc; #[derive(Debug)] struct Sun; -#[allow(dead_code)] #[derive(Debug)] enum Planet { Mercury(Rc), From 4ffce1c2971d127149e5530a94504664d9d5bc50 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 13 Sep 2024 14:59:34 +0200 Subject: [PATCH 1271/1432] Move lint to Rust lints --- dev/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/Cargo.toml b/dev/Cargo.toml index fbb093d1a4..29a557a019 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -207,6 +207,8 @@ panic = "abort" unsafe_code = "forbid" # You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust. unstable_features = "forbid" +# Dead code warnings can't be avoided in some exercises and might distract while learning. +dead_code = "allow" [lints.clippy] # You forgot a `todo!()`! @@ -217,7 +219,5 @@ empty_loop = "forbid" infinite_loop = "deny" # You shouldn't leak memory while still learning Rust! mem_forget = "deny" -# Dead code warnings can't be avoided in some exercises and might distract while learning. -dead_code = "allow" # Currently, there are no disallowed methods. This line avoids problems when developing Rustlings. disallowed_methods = "allow" From 5aaa8924a659051f474584c8b7065a5c3afd9b00 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 13 Sep 2024 15:07:53 +0200 Subject: [PATCH 1272/1432] earch isn't a typo --- .typos.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.typos.toml b/.typos.toml index 2de6d580e8..743c87416f 100644 --- a/.typos.toml +++ b/.typos.toml @@ -1,3 +1,6 @@ +[default.extend-words] +"earch" = "earch" # Because of earch in the list footer + [files] extend-exclude = [ "CHANGELOG.md", From 9459eef03214e05c4a50de3cffe250ecef095917 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 13 Sep 2024 16:38:53 +0200 Subject: [PATCH 1273/1432] Use Clippy with Rust-Analyzer --- src/init.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/init.rs b/src/init.rs index 632243254c..ce49bb65fa 100644 --- a/src/init.rs +++ b/src/init.rs @@ -130,7 +130,7 @@ pub fn init() -> Result<()> { fs::write("Cargo.toml", updated_cargo_toml) .context("Failed to create the file `rustlings/Cargo.toml`")?; - fs::write("rust-analyzer.toml", RATOML) + fs::write("rust-analyzer.toml", RUST_ANALYZER_TOML) .context("Failed to create the file `rustlings/rust-analyzer.toml`")?; fs::write(".gitignore", GITIGNORE) @@ -172,10 +172,9 @@ const INIT_SOLUTION_FILE: &[u8] = b"fn main() { } "; -const RATOML: &[u8] = br#"# rust-analyzer configuration file -# DO NOT edit what is already defined. -# You may add new configurations as needed. -check.extraArgs = ["--profile", "test"]"#; +pub const RUST_ANALYZER_TOML: &[u8] = br#"check.command = "clippy" +check.extraArgs = ["--profile", "test"] +"#; const GITIGNORE: &[u8] = b"Cargo.lock target/ From 47f8a0cbe502654ec09a6865cc82fe9330580ce1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 13 Sep 2024 16:39:28 +0200 Subject: [PATCH 1274/1432] Add rust-analyzer.toml on `dev new` --- src/dev/new.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dev/new.rs b/src/dev/new.rs index c7650465f1..154cd2247b 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -6,7 +6,7 @@ use std::{ process::Command, }; -use crate::CURRENT_FORMAT_VERSION; +use crate::{init::RUST_ANALYZER_TOML, CURRENT_FORMAT_VERSION}; // Create a directory relative to the current directory and print its path. fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> { @@ -62,6 +62,8 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { write_rel_file("README.md", &dir_path_str, README)?; + write_rel_file("rust-analyzer.toml", &dir_path_str, RUST_ANALYZER_TOML)?; + create_rel_dir(".vscode", &dir_path_str)?; write_rel_file( ".vscode/extensions.json", From 8b476e678a8b28bb1885671375a4a7a1b330b16b Mon Sep 17 00:00:00 2001 From: bri-rose Date: Fri, 13 Sep 2024 10:23:05 -0500 Subject: [PATCH 1275/1432] Update info.toml Fixed grammatical error, subject/verb agreement at line 124-125. --- rustlings-macros/info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 0fe8343953..2d420457df 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -122,7 +122,7 @@ dir = "01_variables" test = false hint = """ We know about variables and mutability, but there is another important type of -variables available: constants. +variable available: constants. Constants are always immutable. They are declared with the keyword `const` instead of `let`. From b540c6df253c1f528486bf4245da8eec66710684 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Sat, 14 Sep 2024 09:48:26 +0200 Subject: [PATCH 1276/1432] Make if2 less confusing Some people would get stuck on this exercise, trying to understand the meaning behind foo, fuzz, baz etc. Making the theme of the code make a little more sense to humans should hopefully prevent people from getting confused by abstract and non-sensical tests. --- exercises/03_if/if2.rs | 22 ++++++++++++---------- solutions/03_if/if2.rs | 26 ++++++++++++++------------ 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index 593a77a720..10037f262a 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -1,7 +1,7 @@ // TODO: Fix the compiler error on this function. -fn foo_if_fizz(fizzish: &str) -> &str { - if fizzish == "fizz" { - "foo" +fn picky_eater(food: &str) -> &str { + if food == "strawberry" { + "Yummy!" } else { 1 } @@ -18,18 +18,20 @@ mod tests { use super::*; #[test] - fn foo_for_fizz() { - // This means that calling `foo_if_fizz` with the argument "fizz" should return "foo". - assert_eq!(foo_if_fizz("fizz"), "foo"); + fn yummy_food() { + // This means that calling `picky_eater` with the argument "food" should return "Yummy!". + assert_eq!(picky_eater("strawberry"), "Yummy!"); } #[test] - fn bar_for_fuzz() { - assert_eq!(foo_if_fizz("fuzz"), "bar"); + fn neutral_food() { + assert_eq!(picky_eater("potato"), "I guess I can eat that."); } #[test] - fn default_to_baz() { - assert_eq!(foo_if_fizz("literally anything"), "baz"); + fn default_disliked_food() { + assert_eq!(picky_eater("broccoli"), "No thanks!"); + assert_eq!(picky_eater("gummy bears"), "No thanks!"); + assert_eq!(picky_eater("literally anything"), "No thanks!"); } } diff --git a/solutions/03_if/if2.rs b/solutions/03_if/if2.rs index 440bba055f..21c0dcd35c 100644 --- a/solutions/03_if/if2.rs +++ b/solutions/03_if/if2.rs @@ -1,10 +1,10 @@ -fn foo_if_fizz(fizzish: &str) -> &str { - if fizzish == "fizz" { - "foo" - } else if fizzish == "fuzz" { - "bar" +fn picky_eater(food: &str) -> &str { + if food == "strawberry" { + "Yummy!" + } else if food == "potato" { + "I guess I can eat that." } else { - "baz" + "No thanks!" } } @@ -17,17 +17,19 @@ mod tests { use super::*; #[test] - fn foo_for_fizz() { - assert_eq!(foo_if_fizz("fizz"), "foo"); + fn yummy_food() { + assert_eq!(picky_eater("strawberry"), "Yummy!"); } #[test] - fn bar_for_fuzz() { - assert_eq!(foo_if_fizz("fuzz"), "bar"); + fn neutral_food() { + assert_eq!(picky_eater("potato"), "I guess I can eat that."); } #[test] - fn default_to_baz() { - assert_eq!(foo_if_fizz("literally anything"), "baz"); + fn default_disliked_food() { + assert_eq!(picky_eater("broccoli"), "No thanks!"); + assert_eq!(picky_eater("gummy bears"), "No thanks!"); + assert_eq!(picky_eater("literally anything"), "No thanks!"); } } From e56ae6d65144d0a0bc8cc6759c89e59658a71497 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 17 Sep 2024 23:33:48 +0200 Subject: [PATCH 1277/1432] Update deps --- Cargo.lock | 8 ++++---- Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d84cc5817..bededeb246 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "autocfg" @@ -646,9 +646,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "serde", diff --git a/Cargo.toml b/Cargo.toml index 5b554a6aa6..23dc4f6536 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ rust-version = "1.80" [workspace.dependencies] serde = { version = "1.0.210", features = ["derive"] } -toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.21", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -47,7 +47,7 @@ include = [ [dependencies] ahash = { version = "0.8.11", default-features = false } -anyhow = "1.0.88" +anyhow = "1.0.89" clap = { version = "4.5.17", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify-debouncer-mini = { version = "0.4.1", default-features = false } From 89c40ba25672b2da17e2fcc5bc742462699d54dd Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 18 Sep 2024 01:43:48 +0200 Subject: [PATCH 1278/1432] Optimize the file watcher --- Cargo.lock | 28 +----------- Cargo.toml | 2 +- src/watch.rs | 43 +++++++++++++----- src/watch/notify_event.rs | 91 +++++++++++++++++++++---------------- src/watch/state.rs | 5 +- src/watch/terminal_event.rs | 30 ++---------- 6 files changed, 91 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bededeb246..adc3112927 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,21 +139,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - [[package]] name = "crossterm" version = "0.28.1" @@ -379,7 +364,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ "bitflags 2.6.0", - "crossbeam-channel", "filetime", "fsevent-sys", "inotify", @@ -391,16 +375,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "notify-debouncer-mini" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43" -dependencies = [ - "log", - "notify", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -488,7 +462,7 @@ dependencies = [ "anyhow", "clap", "crossterm", - "notify-debouncer-mini", + "notify", "os_pipe", "rustix", "rustlings-macros", diff --git a/Cargo.toml b/Cargo.toml index 23dc4f6536..f14b47a502 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.89" clap = { version = "4.5.17", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } -notify-debouncer-mini = { version = "0.4.1", default-features = false } +notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] } os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } serde_json = "1.0.128" diff --git a/src/watch.rs b/src/watch.rs index c937bfbe7d..fd89b29e3d 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,12 +1,12 @@ use anyhow::{Error, Result}; -use notify_debouncer_mini::{ - new_debouncer, - notify::{self, RecursiveMode}, -}; +use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; use std::{ io::{self, Write}, path::Path, - sync::mpsc::channel, + sync::{ + atomic::{AtomicBool, Ordering::Relaxed}, + mpsc::channel, + }, time::Duration, }; @@ -21,6 +21,27 @@ mod notify_event; mod state; mod terminal_event; +static EXERCISE_RUNNING: AtomicBool = AtomicBool::new(false); + +// Private unit type to force using the constructor function. +#[must_use = "When the guard is dropped, the input is unpaused"] +pub struct InputPauseGuard(()); + +impl InputPauseGuard { + #[inline] + pub fn scoped_pause() -> Self { + EXERCISE_RUNNING.store(true, Relaxed); + Self(()) + } +} + +impl Drop for InputPauseGuard { + #[inline] + fn drop(&mut self) { + EXERCISE_RUNNING.store(false, Relaxed); + } +} + enum WatchEvent { Input(InputEvent), FileChange { exercise_ind: usize }, @@ -47,21 +68,21 @@ fn run_watch( let mut manual_run = false; // Prevent dropping the guard until the end of the function. // Otherwise, the file watcher exits. - let _debouncer_guard = if let Some(exercise_names) = notify_exercise_names { - let mut debouncer = new_debouncer( - Duration::from_millis(200), + let _watcher_guard = if let Some(exercise_names) = notify_exercise_names { + let mut watcher = RecommendedWatcher::new( NotifyEventHandler { sender: watch_event_sender.clone(), exercise_names, }, + Config::default().with_poll_interval(Duration::from_secs(1)), ) .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; - debouncer - .watcher() + + watcher .watch(Path::new("exercises"), RecursiveMode::Recursive) .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; - Some(debouncer) + Some(watcher) } else { manual_run = true; None diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index 9b23525907..5ed8fd183f 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -1,7 +1,10 @@ -use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; -use std::sync::mpsc::Sender; +use notify::{ + event::{MetadataKind, ModifyKind}, + Event, EventKind, +}; +use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender}; -use super::WatchEvent; +use super::{WatchEvent, EXERCISE_RUNNING}; pub struct NotifyEventHandler { pub sender: Sender, @@ -9,44 +12,56 @@ pub struct NotifyEventHandler { pub exercise_names: &'static [&'static [u8]], } -impl notify_debouncer_mini::DebounceEventHandler for NotifyEventHandler { - fn handle_event(&mut self, input_event: DebounceEventResult) { - let output_event = match input_event { - Ok(input_event) => { - let Some(exercise_ind) = input_event - .iter() - .filter_map(|input_event| { - if input_event.kind != DebouncedEventKind::Any { - return None; - } - - let file_name = input_event.path.file_name()?.to_str()?.as_bytes(); - - if file_name.len() < 4 { - return None; - } - let (file_name_without_ext, ext) = file_name.split_at(file_name.len() - 3); - - if ext != b".rs" { - return None; - } - - self.exercise_names - .iter() - .position(|exercise_name| *exercise_name == file_name_without_ext) - }) - .min() - else { - return; - }; +impl notify::EventHandler for NotifyEventHandler { + fn handle_event(&mut self, input_event: notify::Result) { + if EXERCISE_RUNNING.load(Relaxed) { + return; + } - WatchEvent::FileChange { exercise_ind } + let input_event = match input_event { + Ok(v) => v, + Err(e) => { + // An error occurs when the receiver is dropped. + // After dropping the receiver, the debouncer guard should also be dropped. + let _ = self.sender.send(WatchEvent::NotifyErr(e)); + return; } - Err(e) => WatchEvent::NotifyErr(e), }; - // An error occurs when the receiver is dropped. - // After dropping the receiver, the debouncer guard should also be dropped. - let _ = self.sender.send(output_event); + match input_event.kind { + EventKind::Any => (), + EventKind::Modify(modify_kind) => match modify_kind { + ModifyKind::Any | ModifyKind::Data(_) => (), + ModifyKind::Metadata(metadata_kind) => match metadata_kind { + MetadataKind::Any | MetadataKind::WriteTime => (), + MetadataKind::AccessTime + | MetadataKind::Permissions + | MetadataKind::Ownership + | MetadataKind::Extended + | MetadataKind::Other => return, + }, + ModifyKind::Name(_) | ModifyKind::Other => return, + }, + EventKind::Access(_) + | EventKind::Create(_) + | EventKind::Remove(_) + | EventKind::Other => return, + } + + let _ = input_event + .paths + .into_iter() + .filter_map(|path| { + let file_name = path.file_name()?.to_str()?.as_bytes(); + + let [file_name_without_ext @ .., b'.', b'r', b's'] = file_name else { + return None; + }; + + self.exercise_names + .iter() + .position(|exercise_name| *exercise_name == file_name_without_ext) + }) + .try_for_each(|exercise_ind| self.sender.send(WatchEvent::FileChange { exercise_ind })); } } diff --git a/src/watch/state.rs b/src/watch/state.rs index 6e7600180c..8cccb40dfe 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -18,10 +18,7 @@ use crate::{ term::progress_bar, }; -use super::{ - terminal_event::{terminal_event_handler, InputPauseGuard}, - WatchEvent, -}; +use super::{terminal_event::terminal_event_handler, InputPauseGuard, WatchEvent}; #[derive(PartialEq, Eq)] enum DoneStatus { diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 2a1dfdcf21..050c4acb00 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -1,31 +1,7 @@ use crossterm::event::{self, Event, KeyCode, KeyEventKind}; -use std::sync::{ - atomic::{AtomicBool, Ordering::Relaxed}, - mpsc::Sender, -}; +use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender}; -use super::WatchEvent; - -static INPUT_PAUSED: AtomicBool = AtomicBool::new(false); - -// Private unit type to force using the constructor function. -#[must_use = "When the guard is dropped, the input is unpaused"] -pub struct InputPauseGuard(()); - -impl InputPauseGuard { - #[inline] - pub fn scoped_pause() -> Self { - INPUT_PAUSED.store(true, Relaxed); - Self(()) - } -} - -impl Drop for InputPauseGuard { - #[inline] - fn drop(&mut self) { - INPUT_PAUSED.store(false, Relaxed); - } -} +use super::{WatchEvent, EXERCISE_RUNNING}; pub enum InputEvent { Run, @@ -44,7 +20,7 @@ pub fn terminal_event_handler(sender: Sender, manual_run: bool) { KeyEventKind::Press => (), } - if INPUT_PAUSED.load(Relaxed) { + if EXERCISE_RUNNING.load(Relaxed) { continue; } From 4e4b65711a20ae3d02baa79d8295da2b30ec7dd2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 18 Sep 2024 01:44:13 +0200 Subject: [PATCH 1279/1432] Only handle file changes for the current exercise, no jumping back --- src/watch/state.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index 8cccb40dfe..cb79b356df 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -100,14 +100,10 @@ impl<'a> WatchState<'a> { exercise_ind: usize, stdout: &mut StdoutLock, ) -> Result<()> { - // Don't skip exercises on file changes to avoid confusion from missing exercises. - // Skipping exercises must be explicit in the interactive list. - // But going back to an earlier exercise on file change is fine. - if self.app_state.current_exercise_ind() < exercise_ind { + if self.app_state.current_exercise_ind() != exercise_ind { return Ok(()); } - self.app_state.set_current_exercise_ind(exercise_ind)?; self.run_current_exercise(stdout) } From 2653c3c4d448e4c3b0def82597796fa749f6f373 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 22 Sep 2024 10:48:14 +0200 Subject: [PATCH 1280/1432] Do not use `.as_bytes().len()` on strings --- exercises/23_conversions/as_ref_mut.rs | 5 +++-- solutions/23_conversions/as_ref_mut.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/exercises/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs index 54f0cd11df..d7892dd490 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -2,10 +2,11 @@ // 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. -// Obtain the number of bytes (not characters) in the given argument. +// Obtain the number of bytes (not characters) in the given argument +// (`.len()` returns the number of bytes in a string). // TODO: Add the `AsRef` trait appropriately as a trait bound. fn byte_counter(arg: T) -> usize { - arg.as_ref().as_bytes().len() + arg.as_ref().len() } // Obtain the number of characters (not bytes) in the given argument. diff --git a/solutions/23_conversions/as_ref_mut.rs b/solutions/23_conversions/as_ref_mut.rs index af62e2d8ce..a5d2d4fa57 100644 --- a/solutions/23_conversions/as_ref_mut.rs +++ b/solutions/23_conversions/as_ref_mut.rs @@ -2,9 +2,10 @@ // 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. -// Obtain the number of bytes (not characters) in the given argument. +// Obtain the number of bytes (not characters) in the given argument +// (`.len()` returns the number of bytes in a string). fn byte_counter>(arg: T) -> usize { - arg.as_ref().as_bytes().len() + arg.as_ref().len() } // Obtain the number of characters (not bytes) in the given argument. From e3ec0abca464f337515f79d256c26b208d08b4a5 Mon Sep 17 00:00:00 2001 From: "sota.n" Date: Tue, 24 Sep 2024 16:58:37 +0900 Subject: [PATCH 1281/1432] add Third-Party List about rustlings-jp on README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 91ca564cc6..38d330f66a 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,10 @@ Do you want to create your own set of Rustlings exercises to focus on some speci Or do you want to translate the original Rustlings exercises? Then follow the link to the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)! +### Third-Party List + +- [ζ—₯本θͺžη‰ˆ Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercise. + ## Uninstalling Rustlings If you want to remove Rustlings from your system, run the following command: From 554301b8e9a1c3f8eda05d616fbdb909e2ebf640 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 24 Sep 2024 16:12:44 +0200 Subject: [PATCH 1282/1432] Clear terminal before final check in watch mode --- src/app_state.rs | 16 ++++++++++++---- src/run.rs | 2 +- src/watch/state.rs | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index ecb46898d9..c879955f7e 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -381,7 +381,7 @@ impl AppState { // Return the exercise index of the first pending exercise found. fn check_all_exercises(&self, stdout: &mut StdoutLock) -> Result> { - stdout.write_all(RERUNNING_ALL_EXERCISES_MSG)?; + stdout.write_all(FINAL_CHECK_MSG)?; let n_exercises = self.exercises.len(); let status = thread::scope(|s| { @@ -441,7 +441,10 @@ impl AppState { /// Mark the current exercise as done and move on to the next pending exercise if one exists. /// If all exercises are marked as done, run all of them to make sure that they are actually /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. - pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result { + pub fn done_current_exercise( + &mut self, + stdout: &mut StdoutLock, + ) -> Result { let exercise = &mut self.exercises[self.current_exercise_ind]; if !exercise.done { exercise.done = true; @@ -453,6 +456,12 @@ impl AppState { return Ok(ExercisesProgress::NewPending); } + if CLEAR_BEFORE_FINAL_CHECK { + clear_terminal(stdout)?; + } else { + stdout.write_all(b"\n")?; + } + if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? { stdout.write_all(b"\n\n")?; @@ -482,8 +491,7 @@ impl AppState { const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n"; -const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" -All exercises seem to be done. +const FINAL_CHECK_MSG: &[u8] = b"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 = "+----------------------------------------------------+ diff --git a/src/run.rs b/src/run.rs index a969164c40..3fddcf225a 100644 --- a/src/run.rs +++ b/src/run.rs @@ -44,7 +44,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { stdout.write_all(b"\n")?; } - match app_state.done_current_exercise(&mut stdout)? { + match app_state.done_current_exercise::(&mut stdout)? { ExercisesProgress::NewPending | ExercisesProgress::CurrentPending => { stdout.write_all(b"Next exercise: ")?; app_state diff --git a/src/watch/state.rs b/src/watch/state.rs index cb79b356df..d6c3eb291c 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -113,7 +113,7 @@ impl<'a> WatchState<'a> { return Ok(ExercisesProgress::CurrentPending); } - self.app_state.done_current_exercise(stdout) + self.app_state.done_current_exercise::(stdout) } fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> { From d4fa61e4358bdc83173c346121c992836f1b7152 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 26 Sep 2024 12:26:24 +0200 Subject: [PATCH 1283/1432] Debounce file change events --- src/watch.rs | 8 ++-- src/watch/notify_event.rs | 91 +++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index fd89b29e3d..11450b49c8 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -69,11 +69,11 @@ fn run_watch( // Prevent dropping the guard until the end of the function. // Otherwise, the file watcher exits. let _watcher_guard = if let Some(exercise_names) = notify_exercise_names { + let notify_event_handler = + NotifyEventHandler::build(watch_event_sender.clone(), exercise_names)?; + let mut watcher = RecommendedWatcher::new( - NotifyEventHandler { - sender: watch_event_sender.clone(), - exercise_names, - }, + notify_event_handler, Config::default().with_poll_interval(Duration::from_secs(1)), ) .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index 5ed8fd183f..2051e544bf 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -1,15 +1,71 @@ +use anyhow::{Context, Result}; use notify::{ - event::{MetadataKind, ModifyKind}, + event::{AccessKind, AccessMode, MetadataKind, ModifyKind, RenameMode}, Event, EventKind, }; -use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender}; +use std::{ + sync::{ + atomic::Ordering::Relaxed, + mpsc::{sync_channel, RecvTimeoutError, Sender, SyncSender}, + }, + thread, + time::Duration, +}; use super::{WatchEvent, EXERCISE_RUNNING}; +const DEBOUNCE_DURATION: Duration = Duration::from_millis(200); + pub struct NotifyEventHandler { - pub sender: Sender, - /// Used to report which exercise was modified. - pub exercise_names: &'static [&'static [u8]], + error_sender: Sender, + // Sends the index of the updated exercise. + update_sender: SyncSender, + // Used to report which exercise was modified. + exercise_names: &'static [&'static [u8]], +} + +impl NotifyEventHandler { + pub fn build( + watch_event_sender: Sender, + exercise_names: &'static [&'static [u8]], + ) -> Result { + let (update_sender, update_receiver) = sync_channel(0); + let error_sender = watch_event_sender.clone(); + + // Debouncer + thread::Builder::new() + .spawn(move || { + let mut exercise_updated = vec![false; exercise_names.len()]; + + loop { + match update_receiver.recv_timeout(DEBOUNCE_DURATION) { + Ok(exercise_ind) => exercise_updated[exercise_ind] = true, + Err(RecvTimeoutError::Timeout) => { + for (exercise_ind, updated) in exercise_updated.iter_mut().enumerate() { + if *updated { + if watch_event_sender + .send(WatchEvent::FileChange { exercise_ind }) + .is_err() + { + break; + } + + *updated = false; + } + } + } + Err(RecvTimeoutError::Disconnected) => break, + } + } + }) + .context("Failed to spawn a thread to debounce file changes")?; + + Ok(Self { + error_sender, + update_sender, + exercise_names, + }) + } } impl notify::EventHandler for NotifyEventHandler { @@ -22,8 +78,8 @@ impl notify::EventHandler for NotifyEventHandler { Ok(v) => v, Err(e) => { // An error occurs when the receiver is dropped. - // After dropping the receiver, the debouncer guard should also be dropped. - let _ = self.sender.send(WatchEvent::NotifyErr(e)); + // After dropping the receiver, the watcher guard should also be dropped. + let _ = self.error_sender.send(WatchEvent::NotifyErr(e)); return; } }; @@ -32,6 +88,10 @@ impl notify::EventHandler for NotifyEventHandler { EventKind::Any => (), EventKind::Modify(modify_kind) => match modify_kind { ModifyKind::Any | ModifyKind::Data(_) => (), + ModifyKind::Name(rename_mode) => match rename_mode { + RenameMode::Any | RenameMode::To => (), + RenameMode::From | RenameMode::Both | RenameMode::Other => return, + }, ModifyKind::Metadata(metadata_kind) => match metadata_kind { MetadataKind::Any | MetadataKind::WriteTime => (), MetadataKind::AccessTime @@ -40,12 +100,17 @@ impl notify::EventHandler for NotifyEventHandler { | MetadataKind::Extended | MetadataKind::Other => return, }, - ModifyKind::Name(_) | ModifyKind::Other => return, + ModifyKind::Other => return, + }, + EventKind::Access(access_kind) => match access_kind { + AccessKind::Any => (), + AccessKind::Close(access_mode) => match access_mode { + AccessMode::Any | AccessMode::Write => (), + AccessMode::Execute | AccessMode::Read | AccessMode::Other => return, + }, + AccessKind::Read | AccessKind::Open(_) | AccessKind::Other => return, }, - EventKind::Access(_) - | EventKind::Create(_) - | EventKind::Remove(_) - | EventKind::Other => return, + EventKind::Create(_) | EventKind::Remove(_) | EventKind::Other => return, } let _ = input_event @@ -62,6 +127,6 @@ impl notify::EventHandler for NotifyEventHandler { .iter() .position(|exercise_name| *exercise_name == file_name_without_ext) }) - .try_for_each(|exercise_ind| self.sender.send(WatchEvent::FileChange { exercise_ind })); + .try_for_each(|exercise_ind| self.update_sender.send(exercise_ind)); } } From 0d258b9e9691468fd1c9a46378446467a14be765 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 26 Sep 2024 12:28:48 +0200 Subject: [PATCH 1284/1432] Update deps --- Cargo.lock | 32 ++++++++++++++++---------------- Cargo.toml | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index adc3112927..1a541c67d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -117,9 +117,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libredox" @@ -434,9 +434,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" dependencies = [ "bitflags 2.6.0", ] @@ -536,9 +536,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -620,9 +620,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -846,9 +846,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index f14b47a502..d4134fd8a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ rust-version = "1.80" [workspace.dependencies] serde = { version = "1.0.210", features = ["derive"] } -toml_edit = { version = "0.22.21", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -48,7 +48,7 @@ include = [ [dependencies] ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.89" -clap = { version = "4.5.17", features = ["derive"] } +clap = { version = "4.5.18", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] } os_pipe = "1.2.1" From 0e9eb9e87e21b1f95d2fbd8ad24ba8a197172318 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 26 Sep 2024 18:05:05 +0200 Subject: [PATCH 1285/1432] Replace three dots with dot in hint --- rustlings-macros/info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 2d420457df..c1342d68ec 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1144,7 +1144,7 @@ constants, but clippy recognizes those imprecise mathematical constants as a source of potential error. See the suggestions of the Clippy warning in the compile output and use the -appropriate replacement constant from `std::f32::consts`...""" +appropriate replacement constant from `std::f32::consts`.""" [[exercises]] name = "clippy2" From 0c79f2ea3e1fd2db54ebe8fba8ed17369a15b365 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 26 Sep 2024 18:15:45 +0200 Subject: [PATCH 1286/1432] Reset in prompt with confirmation --- src/watch.rs | 3 +- src/watch/state.rs | 73 ++++++++++++++++++++++++++++++++----- src/watch/terminal_event.rs | 28 ++++++++++++-- 3 files changed, 89 insertions(+), 15 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 11450b49c8..35533b027d 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -100,13 +100,14 @@ fn run_watch( ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?, ExercisesProgress::CurrentPending => (), }, + WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?, WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?, WatchEvent::Input(InputEvent::List) => return Ok(WatchExit::List), + WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?, WatchEvent::Input(InputEvent::Quit) => { stdout.write_all(QUIT_MSG)?; break; } - WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?, WatchEvent::FileChange { exercise_ind } => { watch_state.handle_file_change(exercise_ind, &mut stdout)?; } diff --git a/src/watch/state.rs b/src/watch/state.rs index d6c3eb291c..19910f0375 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -6,8 +6,8 @@ use crossterm::{ terminal, QueueableCommand, }; use std::{ - io::{self, StdoutLock, Write}, - sync::mpsc::Sender, + io::{self, Read, StdoutLock, Write}, + sync::mpsc::{sync_channel, Sender, SyncSender}, thread, }; @@ -34,6 +34,7 @@ pub struct WatchState<'a> { done_status: DoneStatus, manual_run: bool, term_width: u16, + terminal_event_unpause_sender: SyncSender<()>, } impl<'a> WatchState<'a> { @@ -46,8 +47,16 @@ impl<'a> WatchState<'a> { .context("Failed to get the terminal size")? .0; + let (terminal_event_unpause_sender, terminal_event_unpause_receiver) = sync_channel(0); + thread::Builder::new() - .spawn(move || terminal_event_handler(watch_event_sender, manual_run)) + .spawn(move || { + terminal_event_handler( + watch_event_sender, + terminal_event_unpause_receiver, + manual_run, + ) + }) .context("Failed to spawn a thread to handle terminal events")?; Ok(Self { @@ -57,6 +66,7 @@ impl<'a> WatchState<'a> { done_status: DoneStatus::Pending, manual_run, term_width, + terminal_event_unpause_sender, }) } @@ -95,6 +105,44 @@ impl<'a> WatchState<'a> { Ok(()) } + pub fn reset_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> { + clear_terminal(stdout)?; + + stdout.write_all(b"Resetting will undo all your changes to the file ")?; + stdout.write_all(self.app_state.current_exercise().path.as_bytes())?; + stdout.write_all(b"\nReset (y/n)? ")?; + stdout.flush()?; + + { + let mut stdin = io::stdin().lock(); + let mut answer = [0]; + loop { + stdin + .read_exact(&mut answer) + .context("Failed to read the user's input")?; + + match answer[0] { + b'y' | b'Y' => { + self.app_state.reset_current_exercise()?; + + // The file watcher reruns the exercise otherwise. + if self.manual_run { + self.run_current_exercise(stdout)?; + } + } + b'n' | b'N' => self.render(stdout)?, + _ => continue, + } + + break; + } + } + + self.terminal_event_unpause_sender.send(())?; + + Ok(()) + } + pub fn handle_file_change( &mut self, exercise_ind: usize, @@ -117,13 +165,6 @@ impl<'a> WatchState<'a> { } fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> { - if self.manual_run { - stdout.queue(SetAttribute(Attribute::Bold))?; - stdout.write_all(b"r")?; - stdout.queue(ResetColor)?; - stdout.write_all(b":run / ")?; - } - if self.done_status != DoneStatus::Pending { stdout.queue(SetAttribute(Attribute::Bold))?; stdout.write_all(b"n")?; @@ -135,6 +176,13 @@ impl<'a> WatchState<'a> { stdout.write_all(b" / ")?; } + if self.manual_run { + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"r")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":run / ")?; + } + if !self.show_hint { stdout.queue(SetAttribute(Attribute::Bold))?; stdout.write_all(b"h")?; @@ -147,6 +195,11 @@ impl<'a> WatchState<'a> { stdout.queue(ResetColor)?; stdout.write_all(b":list / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"x")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":reset / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; stdout.write_all(b"q")?; stdout.queue(ResetColor)?; diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 050c4acb00..1ed681dc35 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -1,17 +1,25 @@ use crossterm::event::{self, Event, KeyCode, KeyEventKind}; -use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender}; +use std::sync::{ + atomic::Ordering::Relaxed, + mpsc::{Receiver, Sender}, +}; use super::{WatchEvent, EXERCISE_RUNNING}; pub enum InputEvent { - Run, Next, + Run, Hint, List, + Reset, Quit, } -pub fn terminal_event_handler(sender: Sender, manual_run: bool) { +pub fn terminal_event_handler( + sender: Sender, + unpause_receiver: Receiver<()>, + manual_run: bool, +) { let last_watch_event = loop { match event::read() { Ok(Event::Key(key)) => { @@ -26,10 +34,22 @@ pub fn terminal_event_handler(sender: Sender, manual_run: bool) { let input_event = match key.code { KeyCode::Char('n') => InputEvent::Next, + KeyCode::Char('r') if manual_run => InputEvent::Run, KeyCode::Char('h') => InputEvent::Hint, KeyCode::Char('l') => break WatchEvent::Input(InputEvent::List), + KeyCode::Char('x') => { + if sender.send(WatchEvent::Input(InputEvent::Reset)).is_err() { + return; + } + + // Pause input until quitting the confirmation prompt. + if unpause_receiver.recv().is_err() { + return; + }; + + continue; + } KeyCode::Char('q') => break WatchEvent::Input(InputEvent::Quit), - KeyCode::Char('r') if manual_run => InputEvent::Run, _ => continue, }; From 26fd97a209d936755aa653ee0110d17d27e47306 Mon Sep 17 00:00:00 2001 From: Nahor Date: Wed, 2 Oct 2024 11:45:55 -0700 Subject: [PATCH 1287/1432] Update all exercises during the final check The previous code run the check on all exercises but only updates one exercise (the first that failed) even if multiple failed. The user won't be able to see all the failed exercises when viewing the list, and will have to run check_all after each fixed exercise. This change will update all the exercises so the user can see all that failed, fix them all, and only then need run check_all again. --- src/app_state.rs | 143 +++++++++++++++++++++++++++++++---------------- 1 file changed, 96 insertions(+), 47 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index c879955f7e..de5a3828da 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -35,10 +35,12 @@ pub enum StateFileStatus { NotRead, } -enum AllExercisesCheck { - Pending(usize), - AllDone, - CheckedUntil(usize), +#[derive(Clone, Copy, PartialEq)] +enum AllExercisesResult { + Pending, + Success, + Failed, + Error, } pub struct AppState { @@ -270,18 +272,32 @@ impl AppState { self.write() } - pub fn set_pending(&mut self, exercise_ind: usize) -> Result<()> { + // Set the status of an exercise without saving. Returns `true` if the + // status actually changed (and thus needs saving later) + pub fn set_status(&mut self, exercise_ind: usize, done: bool) -> Result { let exercise = self .exercises .get_mut(exercise_ind) .context(BAD_INDEX_ERR)?; - if exercise.done { - exercise.done = false; - self.n_done -= 1; - self.write()?; + if exercise.done == done { + Ok(false) + } else { + exercise.done = done; + if done { + self.n_done += 1; + } else { + self.n_done -= 1; + } + Ok(true) } + } + // Set the status of an exercise to "pending" and save + pub fn set_pending(&mut self, exercise_ind: usize) -> Result<()> { + if self.set_status(exercise_ind, false)? { + self.write()?; + } Ok(()) } @@ -380,11 +396,11 @@ impl AppState { } // Return the exercise index of the first pending exercise found. - fn check_all_exercises(&self, stdout: &mut StdoutLock) -> Result> { + fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result> { stdout.write_all(FINAL_CHECK_MSG)?; let n_exercises = self.exercises.len(); - let status = thread::scope(|s| { + let (mut checked_count, mut results) = thread::scope(|s| { let handles = self .exercises .iter() @@ -394,48 +410,83 @@ impl AppState { }) .collect::>(); + let mut results = vec![AllExercisesResult::Pending; n_exercises]; + let mut checked_count = 0; for (exercise_ind, spawn_res) in handles.into_iter().enumerate() { - write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; + write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?; stdout.flush()?; - let Ok(handle) = spawn_res else { - return Ok(AllExercisesCheck::CheckedUntil(exercise_ind)); - }; - - let Ok(success) = handle.join().unwrap() else { - return Ok(AllExercisesCheck::CheckedUntil(exercise_ind)); - }; - - if !success { - return Ok(AllExercisesCheck::Pending(exercise_ind)); - } + results[exercise_ind] = spawn_res + .context("Spawn error") + .and_then(|handle| handle.join().unwrap()) + .map_or_else( + |_| AllExercisesResult::Error, + |success| { + checked_count += 1; + if success { + AllExercisesResult::Success + } else { + AllExercisesResult::Failed + } + }, + ); } - Ok::<_, io::Error>(AllExercisesCheck::AllDone) + Ok::<_, io::Error>((checked_count, results)) })?; - let mut exercise_ind = match status { - AllExercisesCheck::Pending(exercise_ind) => return Ok(Some(exercise_ind)), - AllExercisesCheck::AllDone => return Ok(None), - AllExercisesCheck::CheckedUntil(ind) => ind, - }; - - // We got an error while checking all exercises in parallel. - // This could be because we exceeded the limit of open file descriptors. - // Therefore, try to continue the check sequentially. - for exercise in &self.exercises[exercise_ind..] { - write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?; - stdout.flush()?; - - let success = exercise.run_exercise(None, &self.cmd_runner)?; - if !success { - return Ok(Some(exercise_ind)); - } + // If we got an error while checking all exercises in parallel, + // it could be because we exceeded the limit of open file descriptors. + // Therefore, re-try those one at a time (i.e. sequentially). + results + .iter_mut() + .enumerate() + .filter(|(_, result)| { + **result == AllExercisesResult::Pending || **result == AllExercisesResult::Error + }) + .try_for_each(|(exercise_ind, result)| { + let exercise = self.exercises.get(exercise_ind).context(BAD_INDEX_ERR)?; + *result = match exercise + .run_exercise(None, &self.cmd_runner) + .context("Sequential retry") + { + Ok(true) => AllExercisesResult::Success, + Ok(false) => AllExercisesResult::Failed, + Err(err) => bail!(err), + }; + checked_count += 1; + write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?; + stdout.flush()?; + Ok(()) + })?; - exercise_ind += 1; - } + // Update the state of each exercise and return the first that failed + let first_fail = results + .iter() + .enumerate() + .filter_map(|(exercise_ind, result)| { + match result { + AllExercisesResult::Success => self + .set_status(exercise_ind, true) + .map_or_else(|err| Some(Err(err)), |_| None), + AllExercisesResult::Failed => self + .set_status(exercise_ind, false) + .map_or_else(|err| Some(Err(err)), |_| Some(Ok(exercise_ind))), + // The sequential check done earlier will have converted all + // exercises to Success/Failed, or bailed, so those are unreachable + AllExercisesResult::Pending | AllExercisesResult::Error => unreachable!(), + } + }) + .try_fold(None::, |current_min, index| { + match (current_min, index) { + (_, Err(err)) => Err(err), + (None, Ok(index)) => Ok(Some(index)), + (Some(current_min), Ok(index)) => Ok(Some(current_min.min(index))), + } + })?; + self.write()?; - Ok(None) + Ok(first_fail) } /// Mark the current exercise as done and move on to the next pending exercise if one exists. @@ -467,9 +518,7 @@ impl AppState { self.current_exercise_ind = pending_exercise_ind; self.exercises[pending_exercise_ind].done = false; - // All exercises were marked as done. - self.n_done -= 1; - self.write()?; + return Ok(ExercisesProgress::NewPending); } From c52867eb8bf69f67e702b87dd2bf12125aa7ab12 Mon Sep 17 00:00:00 2001 From: Nahor Date: Wed, 2 Oct 2024 13:40:32 -0700 Subject: [PATCH 1288/1432] Add command to check all the exercises This allows for skipping repeating "next" when multiple exercises are done at once, or when earlier exercises have been updated/changed (and thus must be redone) while still working of the whole set (i.e. the final check_all is not yet available to flag those undone exercises) --- src/app_state.rs | 24 ++++++++++++++++++++---- src/watch.rs | 7 +++++++ src/watch/state.rs | 22 ++++++++++++++++++++++ src/watch/terminal_event.rs | 2 ++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index de5a3828da..99772b7b5c 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -396,8 +396,16 @@ impl AppState { } // Return the exercise index of the first pending exercise found. - fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result> { - stdout.write_all(FINAL_CHECK_MSG)?; + pub fn check_all_exercises( + &mut self, + stdout: &mut StdoutLock, + final_check: bool, + ) -> Result> { + if !final_check { + stdout.write_all(INTERMEDIATE_CHECK_MSG)?; + } else { + stdout.write_all(FINAL_CHECK_MSG)?; + } let n_exercises = self.exercises.len(); let (mut checked_count, mut results) = thread::scope(|s| { @@ -513,7 +521,7 @@ impl AppState { stdout.write_all(b"\n")?; } - if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? { + if let Some(pending_exercise_ind) = self.check_all_exercises(stdout, true)? { stdout.write_all(b"\n\n")?; self.current_exercise_ind = pending_exercise_ind; @@ -525,6 +533,12 @@ impl AppState { // Write that the last exercise is done. self.write()?; + self.render_final_message(stdout)?; + + Ok(ExercisesProgress::AllDone) + } + + pub fn render_final_message(&self, stdout: &mut StdoutLock) -> Result<()> { clear_terminal(stdout)?; stdout.write_all(FENISH_LINE.as_bytes())?; @@ -534,12 +548,14 @@ impl AppState { stdout.write_all(b"\n")?; } - Ok(ExercisesProgress::AllDone) + Ok(()) } } const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n"; +const INTERMEDIATE_CHECK_MSG: &[u8] = b"Checking all exercises +"; const FINAL_CHECK_MSG: &[u8] = b"All exercises seem to be done. Recompiling and running all exercises to make sure that all of them are actually done. "; diff --git a/src/watch.rs b/src/watch.rs index 35533b027d..b984675786 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -103,6 +103,13 @@ fn run_watch( WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?, WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?, WatchEvent::Input(InputEvent::List) => return Ok(WatchExit::List), + WatchEvent::Input(InputEvent::CheckAll) => match watch_state + .check_all_exercises(&mut stdout)? + { + ExercisesProgress::AllDone => break, + ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?, + ExercisesProgress::CurrentPending => (), + }, WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?, WatchEvent::Input(InputEvent::Quit) => { stdout.write_all(QUIT_MSG)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 19910f0375..67a63579d8 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -195,6 +195,11 @@ impl<'a> WatchState<'a> { stdout.queue(ResetColor)?; stdout.write_all(b":list / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; + stdout.write_all(b"c")?; + stdout.queue(ResetColor)?; + stdout.write_all(b":check all / ")?; + stdout.queue(SetAttribute(Attribute::Bold))?; stdout.write_all(b"x")?; stdout.queue(ResetColor)?; @@ -274,6 +279,23 @@ impl<'a> WatchState<'a> { Ok(()) } + pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result { + stdout.write_all(b"\n")?; + + if let Some(first_fail) = self.app_state.check_all_exercises(stdout, false)? { + // Only change exercise if the current one is done... + if self.app_state.current_exercise().done { + self.app_state.set_current_exercise_ind(first_fail)?; + } + // ...but always pretend it's a "new" anyway because that refreshes + // the display + Ok(ExercisesProgress::NewPending) + } else { + self.app_state.render_final_message(stdout)?; + Ok(ExercisesProgress::AllDone) + } + } + pub fn update_term_width(&mut self, width: u16, stdout: &mut StdoutLock) -> io::Result<()> { if self.term_width != width { self.term_width = width; diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 1ed681dc35..48411db0f7 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -11,6 +11,7 @@ pub enum InputEvent { Run, Hint, List, + CheckAll, Reset, Quit, } @@ -37,6 +38,7 @@ pub fn terminal_event_handler( KeyCode::Char('r') if manual_run => InputEvent::Run, KeyCode::Char('h') => InputEvent::Hint, KeyCode::Char('l') => break WatchEvent::Input(InputEvent::List), + KeyCode::Char('c') => InputEvent::CheckAll, KeyCode::Char('x') => { if sender.send(WatchEvent::Input(InputEvent::Reset)).is_err() { return; From 5c17abd1bf52d1222f6574d38a56a29ca0e7696f Mon Sep 17 00:00:00 2001 From: Nahor Date: Wed, 2 Oct 2024 14:10:26 -0700 Subject: [PATCH 1289/1432] Use a channel to update the check_all progress The previous code was checking the threads in the order they were created. So the progress update would be blocked on an earlier thread even if later thread were already done. Add to that that multiple instances of `cargo build` cannot run in parallel, they will be serialized instead. So if the exercises needs to be recompiled, depending on the order those `cargo build` are run, the first update can be a long time coming. So instead of relying on the thread terminating, use a channel to get notified when an exercise check is done, regardless of the order they finish in. --- src/app_state.rs | 55 ++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 99772b7b5c..28226d64c5 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -5,6 +5,7 @@ use std::{ io::{self, Read, Seek, StdoutLock, Write}, path::{Path, MAIN_SEPARATOR_STR}, process::{Command, Stdio}, + sync::mpsc, thread, }; @@ -409,35 +410,43 @@ impl AppState { let n_exercises = self.exercises.len(); let (mut checked_count, mut results) = thread::scope(|s| { - let handles = self - .exercises + let (tx, rx) = mpsc::channel(); + + self.exercises .iter() - .map(|exercise| { - thread::Builder::new() - .spawn_scoped(s, || exercise.run_exercise(None, &self.cmd_runner)) - }) - .collect::>(); + .enumerate() + .for_each(|(index, exercise)| { + let tx = tx.clone(); + let cmd_runner = &self.cmd_runner; + let _ = thread::Builder::new().spawn_scoped(s, move || { + tx.send((index, exercise.run_exercise(None, cmd_runner))) + }); + }); + + // Drop this `tx`, since the `rx` loop will not stop while there is + // at least one tx alive (i.e. we want the loop to block only while + // there are `tx` clones, i.e. threads) + drop(tx); let mut results = vec![AllExercisesResult::Pending; n_exercises]; let mut checked_count = 0; - for (exercise_ind, spawn_res) in handles.into_iter().enumerate() { + write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?; + stdout.flush()?; + while let Ok((exercise_ind, result)) = rx.recv() { + results[exercise_ind] = result.map_or_else( + |_| AllExercisesResult::Error, + |success| { + checked_count += 1; + if success { + AllExercisesResult::Success + } else { + AllExercisesResult::Failed + } + }, + ); + write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?; stdout.flush()?; - - results[exercise_ind] = spawn_res - .context("Spawn error") - .and_then(|handle| handle.join().unwrap()) - .map_or_else( - |_| AllExercisesResult::Error, - |success| { - checked_count += 1; - if success { - AllExercisesResult::Success - } else { - AllExercisesResult::Failed - } - }, - ); } Ok::<_, io::Error>((checked_count, results)) From e2f7734f37394097f330c4073bf7784500afdc9d Mon Sep 17 00:00:00 2001 From: Nahor Date: Wed, 2 Oct 2024 14:42:50 -0700 Subject: [PATCH 1290/1432] Limit the amount of parallelism in check_all Don't create more threads than there are CPU cores. --- src/app_state.rs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 28226d64c5..ec79188329 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -5,7 +5,7 @@ use std::{ io::{self, Read, Seek, StdoutLock, Write}, path::{Path, MAIN_SEPARATOR_STR}, process::{Command, Stdio}, - sync::mpsc, + sync::{atomic::AtomicUsize, mpsc, Arc}, thread, }; @@ -20,6 +20,7 @@ use crate::{ }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; +const DEFAULT_CHECK_PARALLELISM: usize = 8; #[must_use] pub enum ExercisesProgress { @@ -411,17 +412,31 @@ impl AppState { let (mut checked_count, mut results) = thread::scope(|s| { let (tx, rx) = mpsc::channel(); - - self.exercises - .iter() - .enumerate() - .for_each(|(index, exercise)| { - let tx = tx.clone(); - let cmd_runner = &self.cmd_runner; - let _ = thread::Builder::new().spawn_scoped(s, move || { - tx.send((index, exercise.run_exercise(None, cmd_runner))) - }); + let exercise_ind = Arc::new(AtomicUsize::default()); + + let num_core = thread::available_parallelism() + .map_or(DEFAULT_CHECK_PARALLELISM, |count| count.get()); + (0..num_core).for_each(|_| { + let tx = tx.clone(); + let exercise_ind = exercise_ind.clone(); + let this = &self; + let _ = thread::Builder::new().spawn_scoped(s, move || { + loop { + let exercise_ind = + exercise_ind.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + let Some(exercise) = this.exercises.get(exercise_ind) else { + // No more exercises + break; + }; + if tx + .send((exercise_ind, exercise.run_exercise(None, &this.cmd_runner))) + .is_err() + { + break; + } + } }); + }); // Drop this `tx`, since the `rx` loop will not stop while there is // at least one tx alive (i.e. we want the loop to block only while From aa83fd6bc46167477447ee9b95d21e551e163411 Mon Sep 17 00:00:00 2001 From: Nahor Date: Wed, 2 Oct 2024 15:28:42 -0700 Subject: [PATCH 1291/1432] Show a progress bar when running check_all Replace the "Progress: xxx/yyy" with a progress bar when checking all the exercises --- src/app_state.rs | 93 +++++++++++++++++++++++++++++++++++++----------- src/term.rs | 75 +++++++++++++++++++++++++++++++++----- 2 files changed, 138 insertions(+), 30 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index ec79188329..f4cc1804e9 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,4 +1,9 @@ use anyhow::{bail, Context, Result}; +use crossterm::{ + queue, + style::{Print, ResetColor, SetForegroundColor}, + terminal, +}; use std::{ env, fs::{File, OpenOptions}, @@ -16,7 +21,7 @@ use crate::{ embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, - term, + term::{self, progress_bar_with_success}, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; @@ -428,10 +433,16 @@ impl AppState { // No more exercises break; }; - if tx - .send((exercise_ind, exercise.run_exercise(None, &this.cmd_runner))) - .is_err() - { + + // Notify the progress bar that this exercise is pending + if tx.send((exercise_ind, None)).is_err() { + break; + }; + + let result = exercise.run_exercise(None, &this.cmd_runner); + + // Notify the progress bar that this exercise is done + if tx.send((exercise_ind, Some(result))).is_err() { break; } } @@ -443,28 +454,68 @@ impl AppState { // there are `tx` clones, i.e. threads) drop(tx); + // Print the legend + queue!( + stdout, + Print("Color legend: "), + SetForegroundColor(term::PROGRESS_FAILED_COLOR), + Print("Failure"), + ResetColor, + Print(" - "), + SetForegroundColor(term::PROGRESS_SUCCESS_COLOR), + Print("Success"), + ResetColor, + Print(" - "), + SetForegroundColor(term::PROGRESS_PENDING_COLOR), + Print("Checking"), + ResetColor, + Print("\n"), + ) + .unwrap(); + // We expect at least a few "pending" notifications shortly, so don't + // bother printing the initial state of the progress bar and flushing + // stdout + + let line_width = terminal::size().unwrap().0; let mut results = vec![AllExercisesResult::Pending; n_exercises]; - let mut checked_count = 0; - write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?; - stdout.flush()?; + let mut pending = 0; + let mut success = 0; + let mut failed = 0; + while let Ok((exercise_ind, result)) = rx.recv() { - results[exercise_ind] = result.map_or_else( - |_| AllExercisesResult::Error, - |success| { - checked_count += 1; - if success { - AllExercisesResult::Success - } else { - AllExercisesResult::Failed - } - }, - ); + match result { + None => { + pending += 1; + } + Some(Err(_)) => { + results[exercise_ind] = AllExercisesResult::Error; + } + Some(Ok(true)) => { + results[exercise_ind] = AllExercisesResult::Success; + pending -= 1; + success += 1; + } + Some(Ok(false)) => { + results[exercise_ind] = AllExercisesResult::Failed; + pending -= 1; + failed += 1; + } + } - write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?; + write!(stdout, "\r").unwrap(); + progress_bar_with_success( + stdout, + pending, + failed, + success, + n_exercises as u16, + line_width, + ) + .unwrap(); stdout.flush()?; } - Ok::<_, io::Error>((checked_count, results)) + Ok::<_, io::Error>((success, results)) })?; // If we got an error while checking all exercises in parallel, diff --git a/src/term.rs b/src/term.rs index 5b557ecf40..67ace4b846 100644 --- a/src/term.rs +++ b/src/term.rs @@ -9,6 +9,10 @@ use std::{ io::{self, BufRead, StdoutLock, Write}, }; +pub const PROGRESS_FAILED_COLOR: Color = Color::Red; +pub const PROGRESS_SUCCESS_COLOR: Color = Color::Green; +pub const PROGRESS_PENDING_COLOR: Color = Color::Blue; + pub struct MaxLenWriter<'a, 'b> { pub stdout: &'a mut StdoutLock<'b>, len: usize, @@ -85,15 +89,26 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> { } } -/// Terminal progress bar to be used when not using Ratataui. +/// Simple terminal progress bar pub fn progress_bar<'a>( writer: &mut impl CountedWrite<'a>, progress: u16, total: u16, line_width: u16, +) -> io::Result<()> { + progress_bar_with_success(writer, 0, 0, progress, total, line_width) +} +/// Terminal progress bar with three states (pending + failed + success) +pub fn progress_bar_with_success<'a>( + writer: &mut impl CountedWrite<'a>, + pending: u16, + failed: u16, + success: u16, + total: u16, + line_width: u16, ) -> io::Result<()> { debug_assert!(total < 1000); - debug_assert!(progress <= total); + debug_assert!((pending + failed + success) <= total); const PREFIX: &[u8] = b"Progress: ["; const PREFIX_WIDTH: u16 = PREFIX.len() as u16; @@ -104,25 +119,67 @@ pub fn progress_bar<'a>( if line_width < MIN_LINE_WIDTH { writer.write_ascii(b"Progress: ")?; // Integers are in ASCII. - return writer.write_ascii(format!("{progress}/{total}").as_bytes()); + return writer.write_ascii(format!("{}/{total}", failed + success).as_bytes()); } let stdout = writer.stdout(); stdout.write_all(PREFIX)?; let width = line_width - WRAPPER_WIDTH; - let filled = (width * progress) / total; + let mut failed_end = (width * failed) / total; + let mut success_end = (width * (failed + success)) / total; + let mut pending_end = (width * (failed + success + pending)) / total; + + // In case the range boundaries overlap, "pending" has priority over both + // "failed" and "success" (don't show the bar as "complete" when we are + // still checking some things). + // "Failed" has priority over "success" (don't show 100% success if we + // have some failures, at the risk of showing 100% failures even with + // a few successes). + // + // "Failed" already has priority over "success" because it's displayed + // first. But "pending" is last so we need to fix "success"/"failed". + if pending > 0 { + pending_end = pending_end.max(1); + if pending_end == success_end { + success_end -= 1; + } + if pending_end == failed_end { + failed_end -= 1; + } - stdout.queue(SetForegroundColor(Color::Green))?; - for _ in 0..filled { + // This will replace the last character of the "pending" range with + // the arrow char ('>'). This ensures that even if the progress bar + // is filled (everything either done or pending), we'll still see + // the '>' as long as we are not fully done. + pending_end -= 1; + } + + if failed > 0 { + stdout.queue(SetForegroundColor(PROGRESS_FAILED_COLOR))?; + for _ in 0..failed_end { + stdout.write_all(b"#")?; + } + } + + stdout.queue(SetForegroundColor(PROGRESS_SUCCESS_COLOR))?; + for _ in failed_end..success_end { stdout.write_all(b"#")?; } - if filled < width { + if pending > 0 { + stdout.queue(SetForegroundColor(PROGRESS_PENDING_COLOR))?; + + for _ in success_end..pending_end { + stdout.write_all(b"#")?; + } + } + + if pending_end < width { stdout.write_all(b">")?; } - let width_minus_filled = width - filled; + let width_minus_filled = width - pending_end; if width_minus_filled > 1 { let red_part_width = width_minus_filled - 1; stdout.queue(SetForegroundColor(Color::Red))?; @@ -133,7 +190,7 @@ pub fn progress_bar<'a>( stdout.queue(SetForegroundColor(Color::Reset))?; - write!(stdout, "] {progress:>3}/{total}") + write!(stdout, "] {:>3}/{}", failed + success, total) } pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { From d3f819f86f0fd7e67e9b995034947a65961cab34 Mon Sep 17 00:00:00 2001 From: Nahor Date: Fri, 4 Oct 2024 14:36:36 -0700 Subject: [PATCH 1292/1432] Add command line command to check all exercises --- src/main.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/main.rs b/src/main.rs index fe4b3dcda4..d257b40842 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,10 @@ use anyhow::{bail, Context, Result}; use app_state::StateFileStatus; use clap::{Parser, Subcommand}; +use crossterm::{ + style::{Color, Print, ResetColor, SetForegroundColor}, + QueueableCommand, +}; use std::{ io::{self, IsTerminal, Write}, path::Path, @@ -47,6 +51,8 @@ enum Subcommands { /// The name of the exercise name: Option, }, + /// Run all the exercises, marking them as done or pending accordingly. + RunAll, /// Reset a single exercise Reset { /// The name of the exercise @@ -138,6 +144,36 @@ fn main() -> Result<()> { } run::run(&mut app_state)?; } + Some(Subcommands::RunAll) => { + let mut stdout = io::stdout().lock(); + if let Some(first_fail) = app_state.check_all_exercises(&mut stdout, false)? { + let pending = app_state + .exercises() + .iter() + .filter(|exercise| !exercise.done) + .count(); + if app_state.current_exercise().done { + app_state.set_current_exercise_ind(first_fail)?; + } + stdout + .queue(Print("\n"))? + .queue(SetForegroundColor(Color::Red))? + .queue(Print(format!("{pending}")))? + .queue(ResetColor)?; + if pending == 1 { + stdout.queue(Print(" exercise has some errors: "))?; + } else { + stdout.queue(Print(" exercises have errors, including "))?; + } + app_state + .current_exercise() + .terminal_file_link(&mut stdout)?; + stdout.write_all(b".\n")?; + exit(1); + } else { + app_state.render_final_message(&mut stdout)?; + } + } Some(Subcommands::Reset { name }) => { app_state.set_current_exercise_by_name(&name)?; let exercise_path = app_state.reset_current_exercise()?; From bf7d171915d2a720f10ccacb305993b917798419 Mon Sep 17 00:00:00 2001 From: Polycarbohydrate Date: Sat, 5 Oct 2024 16:05:35 -0400 Subject: [PATCH 1293/1432] Update from_str.rs --- exercises/23_conversions/from_str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index 4b1aaa28f9..ec6d3fd129 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -25,7 +25,7 @@ enum ParsePersonError { ParseInt(ParseIntError), } -// TODO: Complete this `From` implementation to be able to parse a `Person` +// TODO: Complete this `FromStr` implementation to be able to parse a `Person` // out of a string in the form of "Mark,20". // Note that you'll need to parse the age component into a `u8` with something // like `"4".parse::()`. From f516da4138111aa6eff0970471b8a37182c09fad Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 9 Oct 2024 15:27:36 +0200 Subject: [PATCH 1294/1432] Avoid single char variables --- solutions/08_enums/enums3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs index 4bc26b7c63..94cf250f0a 100644 --- a/solutions/08_enums/enums3.rs +++ b/solutions/08_enums/enums3.rs @@ -46,8 +46,8 @@ impl State { match message { Message::Resize { width, height } => self.resize(width, height), Message::Move(point) => self.move_position(point), - Message::Echo(s) => self.echo(s), - Message::ChangeColor(r, g, b) => self.change_color(r, g, b), + Message::Echo(string) => self.echo(string), + Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue), Message::Quit => self.quit(), } } From 84a42a2b24687ed11f4d2a5c9b624d00b74de916 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 9 Oct 2024 15:42:16 +0200 Subject: [PATCH 1295/1432] Update third-party exercises section --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 38d330f66a..b65920f239 100644 --- a/README.md +++ b/README.md @@ -135,13 +135,14 @@ Continue practicing your Rust skills by building your own projects, contributing ## Third-Party Exercises -Do you want to create your own set of Rustlings exercises to focus on some specific topic? -Or do you want to translate the original Rustlings exercises? -Then follow the link to the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)! +Third-party exercises are a set of exercises maintained by the community. +You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them: -### Third-Party List +- [ζ—₯本θͺžη‰ˆ Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. -- [ζ—₯本θͺžη‰ˆ Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercise. +Do you want to create your own set of Rustlings exercises to focus on some specific topic? +Or do you want to translate the original Rustlings exercises? +Then follow the the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)! ## Uninstalling Rustlings From 685e069c58ef02dae65381974722315ee8c84e8b Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 10 Oct 2024 19:43:35 +0200 Subject: [PATCH 1296/1432] First PR review changes --- src/app_state.rs | 326 +++++++++++++++++++++------------------------ src/main.rs | 3 +- src/term.rs | 19 +-- src/watch.rs | 2 +- src/watch/state.rs | 18 +-- 5 files changed, 175 insertions(+), 193 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index f4cc1804e9..7540181c5f 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,16 +1,18 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Context, Error, Result}; use crossterm::{ - queue, - style::{Print, ResetColor, SetForegroundColor}, - terminal, + style::{ResetColor, SetForegroundColor}, + terminal, QueueableCommand, }; use std::{ env, fs::{File, OpenOptions}, - io::{self, Read, Seek, StdoutLock, Write}, + io::{Read, Seek, StdoutLock, Write}, path::{Path, MAIN_SEPARATOR_STR}, process::{Command, Stdio}, - sync::{atomic::AtomicUsize, mpsc, Arc}, + sync::{ + atomic::{AtomicUsize, Ordering::Relaxed}, + mpsc, + }, thread, }; @@ -42,11 +44,17 @@ pub enum StateFileStatus { NotRead, } -#[derive(Clone, Copy, PartialEq)] -enum AllExercisesResult { +enum ExerciseCheckProgress { + Checking, + Done, + Pending, + Error, +} + +#[derive(Clone, Copy)] +enum ExerciseCheckResult { + Done, Pending, - Success, - Failed, Error, } @@ -280,7 +288,7 @@ impl AppState { } // Set the status of an exercise without saving. Returns `true` if the - // status actually changed (and thus needs saving later) + // status actually changed (and thus needs saving later). pub fn set_status(&mut self, exercise_ind: usize, done: bool) -> Result { let exercise = self .exercises @@ -288,23 +296,25 @@ impl AppState { .context(BAD_INDEX_ERR)?; if exercise.done == done { - Ok(false) + return Ok(false); + } + + exercise.done = done; + if done { + self.n_done += 1; } else { - exercise.done = done; - if done { - self.n_done += 1; - } else { - self.n_done -= 1; - } - Ok(true) + self.n_done -= 1; } + + Ok(true) } - // Set the status of an exercise to "pending" and save + // Set the status of an exercise to "pending" and save. pub fn set_pending(&mut self, exercise_ind: usize) -> Result<()> { if self.set_status(exercise_ind, false)? { self.write()?; } + Ok(()) } @@ -403,173 +413,154 @@ impl AppState { } // Return the exercise index of the first pending exercise found. - pub fn check_all_exercises( - &mut self, - stdout: &mut StdoutLock, - final_check: bool, - ) -> Result> { - if !final_check { - stdout.write_all(INTERMEDIATE_CHECK_MSG)?; - } else { - stdout.write_all(FINAL_CHECK_MSG)?; - } - let n_exercises = self.exercises.len(); - - let (mut checked_count, mut results) = thread::scope(|s| { - let (tx, rx) = mpsc::channel(); - let exercise_ind = Arc::new(AtomicUsize::default()); - - let num_core = thread::available_parallelism() + pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result> { + stdout.write_all("Checking all exercises…\n".as_bytes())?; + let n_exercises = self.exercises.len() as u16; + let next_exercise_ind = AtomicUsize::new(0); + let term_width = terminal::size() + .context("Failed to get the terminal size")? + .0; + + let mut results = vec![ExerciseCheckResult::Error; self.exercises.len()]; + let mut done = 0; + let mut pending = 0; + + thread::scope(|s| { + let mut checking = 0; + let (exercise_result_sender, exercise_result_receiver) = mpsc::channel(); + let n_threads = thread::available_parallelism() .map_or(DEFAULT_CHECK_PARALLELISM, |count| count.get()); - (0..num_core).for_each(|_| { - let tx = tx.clone(); - let exercise_ind = exercise_ind.clone(); - let this = &self; - let _ = thread::Builder::new().spawn_scoped(s, move || { - loop { - let exercise_ind = - exercise_ind.fetch_add(1, std::sync::atomic::Ordering::AcqRel); - let Some(exercise) = this.exercises.get(exercise_ind) else { - // No more exercises + + for _ in 0..n_threads { + let exercise_result_sender = exercise_result_sender.clone(); + let next_exercise_ind = &next_exercise_ind; + let slf = &self; + thread::Builder::new() + .spawn_scoped(s, move || loop { + let exercise_ind = next_exercise_ind.fetch_add(1, Relaxed); + let Some(exercise) = slf.exercises.get(exercise_ind) else { + // No more exercises. break; }; - // Notify the progress bar that this exercise is pending - if tx.send((exercise_ind, None)).is_err() { + // Notify the progress bar that this exercise is pending. + if exercise_result_sender + .send((exercise_ind, ExerciseCheckProgress::Checking)) + .is_err() + { break; }; - let result = exercise.run_exercise(None, &this.cmd_runner); + let success = exercise.run_exercise(None, &slf.cmd_runner); + let result = match success { + Ok(true) => ExerciseCheckProgress::Done, + Ok(false) => ExerciseCheckProgress::Pending, + Err(_) => ExerciseCheckProgress::Error, + }; - // Notify the progress bar that this exercise is done - if tx.send((exercise_ind, Some(result))).is_err() { + // Notify the progress bar that this exercise is done. + if exercise_result_sender.send((exercise_ind, result)).is_err() { break; } - } - }); - }); - - // Drop this `tx`, since the `rx` loop will not stop while there is - // at least one tx alive (i.e. we want the loop to block only while - // there are `tx` clones, i.e. threads) - drop(tx); - - // Print the legend - queue!( - stdout, - Print("Color legend: "), - SetForegroundColor(term::PROGRESS_FAILED_COLOR), - Print("Failure"), - ResetColor, - Print(" - "), - SetForegroundColor(term::PROGRESS_SUCCESS_COLOR), - Print("Success"), - ResetColor, - Print(" - "), - SetForegroundColor(term::PROGRESS_PENDING_COLOR), - Print("Checking"), - ResetColor, - Print("\n"), - ) - .unwrap(); - // We expect at least a few "pending" notifications shortly, so don't - // bother printing the initial state of the progress bar and flushing - // stdout - - let line_width = terminal::size().unwrap().0; - let mut results = vec![AllExercisesResult::Pending; n_exercises]; - let mut pending = 0; - let mut success = 0; - let mut failed = 0; - - while let Ok((exercise_ind, result)) = rx.recv() { + }) + .context("Failed to spawn a thread to check all exercises")?; + } + + // Drop this sender to detect when the last thread is done. + drop(exercise_result_sender); + + // Print the legend. + stdout.write_all(b"Color legend: ")?; + stdout.queue(SetForegroundColor(term::PROGRESS_FAILED_COLOR))?; + stdout.write_all(b"Pending")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" - ")?; + stdout.queue(SetForegroundColor(term::PROGRESS_SUCCESS_COLOR))?; + stdout.write_all(b"Done")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" - ")?; + stdout.queue(SetForegroundColor(term::PROGRESS_PENDING_COLOR))?; + stdout.write_all(b"Checking")?; + stdout.queue(ResetColor)?; + stdout.write_all(b"\n")?; + + while let Ok((exercise_ind, result)) = exercise_result_receiver.recv() { match result { - None => { - pending += 1; + ExerciseCheckProgress::Checking => checking += 1, + ExerciseCheckProgress::Done => { + results[exercise_ind] = ExerciseCheckResult::Done; + checking -= 1; + done += 1; } - Some(Err(_)) => { - results[exercise_ind] = AllExercisesResult::Error; - } - Some(Ok(true)) => { - results[exercise_ind] = AllExercisesResult::Success; - pending -= 1; - success += 1; - } - Some(Ok(false)) => { - results[exercise_ind] = AllExercisesResult::Failed; - pending -= 1; - failed += 1; + ExerciseCheckProgress::Pending => { + results[exercise_ind] = ExerciseCheckResult::Pending; + checking -= 1; + pending += 1; } + ExerciseCheckProgress::Error => checking -= 1, } - write!(stdout, "\r").unwrap(); + stdout.write_all(b"\r")?; progress_bar_with_success( stdout, + checking, pending, - failed, - success, - n_exercises as u16, - line_width, - ) - .unwrap(); + done, + n_exercises, + term_width, + )?; stdout.flush()?; } - Ok::<_, io::Error>((success, results)) + Ok::<_, Error>(()) })?; - // If we got an error while checking all exercises in parallel, - // it could be because we exceeded the limit of open file descriptors. - // Therefore, re-try those one at a time (i.e. sequentially). - results - .iter_mut() - .enumerate() - .filter(|(_, result)| { - **result == AllExercisesResult::Pending || **result == AllExercisesResult::Error - }) - .try_for_each(|(exercise_ind, result)| { - let exercise = self.exercises.get(exercise_ind).context(BAD_INDEX_ERR)?; - *result = match exercise - .run_exercise(None, &self.cmd_runner) - .context("Sequential retry") - { - Ok(true) => AllExercisesResult::Success, - Ok(false) => AllExercisesResult::Failed, - Err(err) => bail!(err), - }; - checked_count += 1; - write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?; - stdout.flush()?; - Ok(()) - })?; - - // Update the state of each exercise and return the first that failed - let first_fail = results - .iter() - .enumerate() - .filter_map(|(exercise_ind, result)| { - match result { - AllExercisesResult::Success => self - .set_status(exercise_ind, true) - .map_or_else(|err| Some(Err(err)), |_| None), - AllExercisesResult::Failed => self - .set_status(exercise_ind, false) - .map_or_else(|err| Some(Err(err)), |_| Some(Ok(exercise_ind))), - // The sequential check done earlier will have converted all - // exercises to Success/Failed, or bailed, so those are unreachable - AllExercisesResult::Pending | AllExercisesResult::Error => unreachable!(), + let mut first_pending_exercise_ind = None; + for (exercise_ind, result) in results.into_iter().enumerate() { + match result { + ExerciseCheckResult::Done => { + self.set_status(exercise_ind, true)?; } - }) - .try_fold(None::, |current_min, index| { - match (current_min, index) { - (_, Err(err)) => Err(err), - (None, Ok(index)) => Ok(Some(index)), - (Some(current_min), Ok(index)) => Ok(Some(current_min.min(index))), + ExerciseCheckResult::Pending => { + self.set_status(exercise_ind, false)?; + if first_pending_exercise_ind.is_none() { + first_pending_exercise_ind = Some(exercise_ind); + } } - })?; + ExerciseCheckResult::Error => { + // If we got an error while checking all exercises in parallel, + // it could be because we exceeded the limit of open file descriptors. + // Therefore, try running exercises with errors sequentially. + let exercise = &self.exercises[exercise_ind]; + let success = exercise.run_exercise(None, &self.cmd_runner)?; + if success { + done += 1; + } else { + pending += 1; + if first_pending_exercise_ind.is_none() { + first_pending_exercise_ind = Some(exercise_ind); + } + } + self.set_status(exercise_ind, success)?; + + stdout.write_all(b"\r")?; + progress_bar_with_success( + stdout, + u16::from(pending + done < n_exercises), + pending, + done, + n_exercises, + term_width, + )?; + stdout.flush()?; + } + } + } + self.write()?; + stdout.write_all(b"\n\n")?; - Ok(first_fail) + Ok(first_pending_exercise_ind) } /// Mark the current exercise as done and move on to the next pending exercise if one exists. @@ -596,18 +587,12 @@ impl AppState { stdout.write_all(b"\n")?; } - if let Some(pending_exercise_ind) = self.check_all_exercises(stdout, true)? { - stdout.write_all(b"\n\n")?; - - self.current_exercise_ind = pending_exercise_ind; - self.exercises[pending_exercise_ind].done = false; + if let Some(first_pending_exercise_ind) = self.check_all_exercises(stdout)? { + self.set_current_exercise_ind(first_pending_exercise_ind)?; return Ok(ExercisesProgress::NewPending); } - // Write that the last exercise is done. - self.write()?; - self.render_final_message(stdout)?; Ok(ExercisesProgress::AllDone) @@ -629,11 +614,6 @@ impl AppState { const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n"; -const INTERMEDIATE_CHECK_MSG: &[u8] = b"Checking all exercises -"; -const FINAL_CHECK_MSG: &[u8] = b"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! | +-------------------------- ------------------------+ diff --git a/src/main.rs b/src/main.rs index d257b40842..64b72bde9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -146,7 +146,7 @@ fn main() -> Result<()> { } Some(Subcommands::RunAll) => { let mut stdout = io::stdout().lock(); - if let Some(first_fail) = app_state.check_all_exercises(&mut stdout, false)? { + if let Some(first_fail) = app_state.check_all_exercises(&mut stdout)? { let pending = app_state .exercises() .iter() @@ -156,7 +156,6 @@ fn main() -> Result<()> { app_state.set_current_exercise_ind(first_fail)?; } stdout - .queue(Print("\n"))? .queue(SetForegroundColor(Color::Red))? .queue(Print(format!("{pending}")))? .queue(ResetColor)?; diff --git a/src/term.rs b/src/term.rs index 67ace4b846..31a951dbf9 100644 --- a/src/term.rs +++ b/src/term.rs @@ -89,34 +89,35 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> { } } -/// Simple terminal progress bar +/// Simple terminal progress bar. pub fn progress_bar<'a>( writer: &mut impl CountedWrite<'a>, progress: u16, total: u16, - line_width: u16, + term_width: u16, ) -> io::Result<()> { - progress_bar_with_success(writer, 0, 0, progress, total, line_width) + progress_bar_with_success(writer, 0, 0, progress, total, term_width) } -/// Terminal progress bar with three states (pending + failed + success) + +/// Terminal progress bar with three states (pending + failed + success). pub fn progress_bar_with_success<'a>( writer: &mut impl CountedWrite<'a>, pending: u16, failed: u16, success: u16, total: u16, - line_width: u16, + term_width: u16, ) -> io::Result<()> { debug_assert!(total < 1000); - debug_assert!((pending + failed + success) <= total); + debug_assert!(pending + failed + success <= total); const PREFIX: &[u8] = b"Progress: ["; const PREFIX_WIDTH: u16 = PREFIX.len() as u16; const POSTFIX_WIDTH: u16 = "] xxx/xxx".len() as u16; const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH; - const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; + const MIN_TERM_WIDTH: u16 = WRAPPER_WIDTH + 4; - if line_width < MIN_LINE_WIDTH { + if term_width < MIN_TERM_WIDTH { writer.write_ascii(b"Progress: ")?; // Integers are in ASCII. return writer.write_ascii(format!("{}/{total}", failed + success).as_bytes()); @@ -125,7 +126,7 @@ pub fn progress_bar_with_success<'a>( let stdout = writer.stdout(); stdout.write_all(PREFIX)?; - let width = line_width - WRAPPER_WIDTH; + let width = term_width - WRAPPER_WIDTH; let mut failed_end = (width * failed) / total; let mut success_end = (width * (failed + success)) / total; let mut pending_end = (width * (failed + success + pending)) / total; diff --git a/src/watch.rs b/src/watch.rs index b984675786..6259c9df38 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -108,7 +108,7 @@ fn run_watch( { ExercisesProgress::AllDone => break, ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?, - ExercisesProgress::CurrentPending => (), + ExercisesProgress::CurrentPending => watch_state.render(&mut stdout)?, }, WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?, WatchEvent::Input(InputEvent::Quit) => { diff --git a/src/watch/state.rs b/src/watch/state.rs index 67a63579d8..8b58e3117a 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -157,8 +157,9 @@ impl<'a> WatchState<'a> { /// Move on to the next exercise if the current one is done. pub fn next_exercise(&mut self, stdout: &mut StdoutLock) -> Result { - if self.done_status == DoneStatus::Pending { - return Ok(ExercisesProgress::CurrentPending); + match self.done_status { + DoneStatus::DoneWithSolution(_) | DoneStatus::DoneWithoutSolution => (), + DoneStatus::Pending => return Ok(ExercisesProgress::CurrentPending), } self.app_state.done_current_exercise::(stdout) @@ -282,14 +283,15 @@ impl<'a> WatchState<'a> { pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result { stdout.write_all(b"\n")?; - if let Some(first_fail) = self.app_state.check_all_exercises(stdout, false)? { - // Only change exercise if the current one is done... + if let Some(first_pending_exercise_ind) = self.app_state.check_all_exercises(stdout)? { + // Only change exercise if the current one is done. if self.app_state.current_exercise().done { - self.app_state.set_current_exercise_ind(first_fail)?; + self.app_state + .set_current_exercise_ind(first_pending_exercise_ind)?; + Ok(ExercisesProgress::NewPending) + } else { + Ok(ExercisesProgress::CurrentPending) } - // ...but always pretend it's a "new" anyway because that refreshes - // the display - Ok(ExercisesProgress::NewPending) } else { self.app_state.render_final_message(stdout)?; Ok(ExercisesProgress::AllDone) From 326169a7fabacda9a21377b110371f91b32e8fd3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 13 Oct 2024 22:02:41 +0200 Subject: [PATCH 1297/1432] Improve check-all command --- clippy.toml | 2 ++ src/app_state.rs | 5 +++++ src/main.rs | 54 ++++++++++++++++++++++++------------------------ src/run.rs | 8 +++---- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/clippy.toml b/clippy.toml index 4a5dd06a6c..11ec6cc345 100644 --- a/clippy.toml +++ b/clippy.toml @@ -13,4 +13,6 @@ disallowed-methods = [ # Use `thread::Builder::spawn` instead and handle the error. "std::thread::spawn", "std::thread::Scope::spawn", + # Return `ExitCode` instead. + "std::process::exit", ] diff --git a/src/app_state.rs b/src/app_state.rs index 7540181c5f..c3998422a3 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -211,6 +211,11 @@ impl AppState { self.n_done } + #[inline] + pub fn n_pending(&self) -> u16 { + self.exercises.len() as u16 - self.n_done + } + #[inline] pub fn current_exercise(&self) -> &Exercise { &self.exercises[self.current_exercise_ind] diff --git a/src/main.rs b/src/main.rs index 64b72bde9e..f40bb89a18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use crossterm::{ use std::{ io::{self, IsTerminal, Write}, path::Path, - process::exit, + process::ExitCode, }; use term::{clear_terminal, press_enter_prompt}; @@ -51,8 +51,8 @@ enum Subcommands { /// The name of the exercise name: Option, }, - /// Run all the exercises, marking them as done or pending accordingly. - RunAll, + /// Check all the exercises, marking them as done or pending accordingly. + CheckAll, /// Reset a single exercise Reset { /// The name of the exercise @@ -68,22 +68,26 @@ enum Subcommands { Dev(DevCommands), } -fn main() -> Result<()> { +fn main() -> Result { let args = Args::parse(); if cfg!(not(debug_assertions)) && Path::new("dev/rustlings-repo.txt").exists() { bail!("{OLD_METHOD_ERR}"); } - match args.command { - Some(Subcommands::Init) => return init::init().context("Initialization failed"), - Some(Subcommands::Dev(dev_command)) => return dev_command.run(), - _ => (), + 'priority_cmd: { + match args.command { + Some(Subcommands::Init) => init::init().context("Initialization failed")?, + Some(Subcommands::Dev(dev_command)) => dev_command.run()?, + _ => break 'priority_cmd, + } + + return Ok(ExitCode::SUCCESS); } if !Path::new("exercises").is_dir() { println!("{PRE_INIT_MSG}"); - exit(1); + return Ok(ExitCode::FAILURE); } let info_file = InfoFile::parse()?; @@ -142,33 +146,29 @@ fn main() -> Result<()> { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; } - run::run(&mut app_state)?; + return run::run(&mut app_state); } - Some(Subcommands::RunAll) => { + Some(Subcommands::CheckAll) => { let mut stdout = io::stdout().lock(); - if let Some(first_fail) = app_state.check_all_exercises(&mut stdout)? { - let pending = app_state - .exercises() - .iter() - .filter(|exercise| !exercise.done) - .count(); + if let Some(first_pending_exercise_ind) = app_state.check_all_exercises(&mut stdout)? { if app_state.current_exercise().done { - app_state.set_current_exercise_ind(first_fail)?; + app_state.set_current_exercise_ind(first_pending_exercise_ind)?; } - stdout - .queue(SetForegroundColor(Color::Red))? - .queue(Print(format!("{pending}")))? - .queue(ResetColor)?; + + let pending = app_state.n_pending(); if pending == 1 { - stdout.queue(Print(" exercise has some errors: "))?; + stdout.queue(Print("One exercise pending: "))?; } else { - stdout.queue(Print(" exercises have errors, including "))?; + stdout.queue(SetForegroundColor(Color::Red))?; + write!(stdout, "{pending}")?; + stdout.queue(ResetColor)?; + stdout.queue(Print(" exercises are pending. The first: "))?; } app_state .current_exercise() .terminal_file_link(&mut stdout)?; - stdout.write_all(b".\n")?; - exit(1); + stdout.write_all(b"\n")?; + return Ok(ExitCode::FAILURE); } else { app_state.render_final_message(&mut stdout)?; } @@ -188,7 +188,7 @@ fn main() -> Result<()> { Some(Subcommands::Init | Subcommands::Dev(_)) => (), } - Ok(()) + Ok(ExitCode::SUCCESS) } const OLD_METHOD_ERR: &str = diff --git a/src/run.rs b/src/run.rs index 3fddcf225a..f259f52c80 100644 --- a/src/run.rs +++ b/src/run.rs @@ -5,7 +5,7 @@ use crossterm::{ }; use std::{ io::{self, Write}, - process::exit, + process::ExitCode, }; use crate::{ @@ -13,7 +13,7 @@ use crate::{ exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, }; -pub fn run(app_state: &mut AppState) -> Result<()> { +pub fn run(app_state: &mut AppState) -> Result { let exercise = app_state.current_exercise(); let mut output = Vec::with_capacity(OUTPUT_CAPACITY); let success = exercise.run_exercise(Some(&mut output), app_state.cmd_runner())?; @@ -29,7 +29,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { .current_exercise() .terminal_file_link(&mut stdout)?; stdout.write_all(b" with errors\n")?; - exit(1); + return Ok(ExitCode::FAILURE); } stdout.queue(SetForegroundColor(Color::Green))?; @@ -55,5 +55,5 @@ pub fn run(app_state: &mut AppState) -> Result<()> { ExercisesProgress::AllDone => (), } - Ok(()) + Ok(ExitCode::SUCCESS) } From 396ee4d618bc5e1cd5c84495f571f9d3f79774c8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 13 Oct 2024 23:28:17 +0200 Subject: [PATCH 1298/1432] Show progress with exercise numbers --- src/app_state.rs | 128 +++++++++++++++++----------------------------- src/main.rs | 15 +++--- src/term.rs | 130 ++++++++++++++++++++++------------------------- 3 files changed, 114 insertions(+), 159 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index c3998422a3..db9d1f10c2 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,8 +1,5 @@ use anyhow::{bail, Context, Error, Result}; -use crossterm::{ - style::{ResetColor, SetForegroundColor}, - terminal, QueueableCommand, -}; +use crossterm::{cursor, terminal, QueueableCommand}; use std::{ env, fs::{File, OpenOptions}, @@ -23,7 +20,7 @@ use crate::{ embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, - term::{self, progress_bar_with_success}, + term::{self, show_exercises_check_progress}, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; @@ -44,18 +41,12 @@ pub enum StateFileStatus { NotRead, } -enum ExerciseCheckProgress { - Checking, - Done, - Pending, - Error, -} - #[derive(Clone, Copy)] -enum ExerciseCheckResult { +pub enum ExerciseCheckProgress { + None, + Checking, Done, Pending, - Error, } pub struct AppState { @@ -417,27 +408,25 @@ impl AppState { } } - // Return the exercise index of the first pending exercise found. - pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result> { + fn check_all_exercises_impl(&mut self, stdout: &mut StdoutLock) -> Result> { stdout.write_all("Checking all exercises…\n".as_bytes())?; - let n_exercises = self.exercises.len() as u16; let next_exercise_ind = AtomicUsize::new(0); let term_width = terminal::size() .context("Failed to get the terminal size")? .0; + clear_terminal(stdout)?; - let mut results = vec![ExerciseCheckResult::Error; self.exercises.len()]; + let mut progresses = vec![ExerciseCheckProgress::None; self.exercises.len()]; let mut done = 0; let mut pending = 0; thread::scope(|s| { - let mut checking = 0; - let (exercise_result_sender, exercise_result_receiver) = mpsc::channel(); + let (exercise_progress_sender, exercise_progress_receiver) = mpsc::channel(); let n_threads = thread::available_parallelism() .map_or(DEFAULT_CHECK_PARALLELISM, |count| count.get()); for _ in 0..n_threads { - let exercise_result_sender = exercise_result_sender.clone(); + let exercise_progress_sender = exercise_progress_sender.clone(); let next_exercise_ind = &next_exercise_ind; let slf = &self; thread::Builder::new() @@ -449,7 +438,7 @@ impl AppState { }; // Notify the progress bar that this exercise is pending. - if exercise_result_sender + if exercise_progress_sender .send((exercise_ind, ExerciseCheckProgress::Checking)) .is_err() { @@ -457,14 +446,17 @@ impl AppState { }; let success = exercise.run_exercise(None, &slf.cmd_runner); - let result = match success { + let progress = match success { Ok(true) => ExerciseCheckProgress::Done, Ok(false) => ExerciseCheckProgress::Pending, - Err(_) => ExerciseCheckProgress::Error, + Err(_) => ExerciseCheckProgress::None, }; // Notify the progress bar that this exercise is done. - if exercise_result_sender.send((exercise_ind, result)).is_err() { + if exercise_progress_sender + .send((exercise_ind, progress)) + .is_err() + { break; } }) @@ -472,102 +464,76 @@ impl AppState { } // Drop this sender to detect when the last thread is done. - drop(exercise_result_sender); - - // Print the legend. - stdout.write_all(b"Color legend: ")?; - stdout.queue(SetForegroundColor(term::PROGRESS_FAILED_COLOR))?; - stdout.write_all(b"Pending")?; - stdout.queue(ResetColor)?; - stdout.write_all(b" - ")?; - stdout.queue(SetForegroundColor(term::PROGRESS_SUCCESS_COLOR))?; - stdout.write_all(b"Done")?; - stdout.queue(ResetColor)?; - stdout.write_all(b" - ")?; - stdout.queue(SetForegroundColor(term::PROGRESS_PENDING_COLOR))?; - stdout.write_all(b"Checking")?; - stdout.queue(ResetColor)?; - stdout.write_all(b"\n")?; + drop(exercise_progress_sender); - while let Ok((exercise_ind, result)) = exercise_result_receiver.recv() { - match result { - ExerciseCheckProgress::Checking => checking += 1, - ExerciseCheckProgress::Done => { - results[exercise_ind] = ExerciseCheckResult::Done; - checking -= 1; - done += 1; - } - ExerciseCheckProgress::Pending => { - results[exercise_ind] = ExerciseCheckResult::Pending; - checking -= 1; - pending += 1; - } - ExerciseCheckProgress::Error => checking -= 1, + while let Ok((exercise_ind, progress)) = exercise_progress_receiver.recv() { + progresses[exercise_ind] = progress; + + match progress { + ExerciseCheckProgress::None | ExerciseCheckProgress::Checking => (), + ExerciseCheckProgress::Done => done += 1, + ExerciseCheckProgress::Pending => pending += 1, } - stdout.write_all(b"\r")?; - progress_bar_with_success( - stdout, - checking, - pending, - done, - n_exercises, - term_width, - )?; - stdout.flush()?; + show_exercises_check_progress(stdout, &progresses, term_width)?; } Ok::<_, Error>(()) })?; let mut first_pending_exercise_ind = None; - for (exercise_ind, result) in results.into_iter().enumerate() { - match result { - ExerciseCheckResult::Done => { + for exercise_ind in 0..progresses.len() { + match progresses[exercise_ind] { + ExerciseCheckProgress::Done => { self.set_status(exercise_ind, true)?; } - ExerciseCheckResult::Pending => { + ExerciseCheckProgress::Pending => { self.set_status(exercise_ind, false)?; if first_pending_exercise_ind.is_none() { first_pending_exercise_ind = Some(exercise_ind); } } - ExerciseCheckResult::Error => { + ExerciseCheckProgress::None | ExerciseCheckProgress::Checking => { // If we got an error while checking all exercises in parallel, // it could be because we exceeded the limit of open file descriptors. // Therefore, try running exercises with errors sequentially. + progresses[exercise_ind] = ExerciseCheckProgress::Checking; + show_exercises_check_progress(stdout, &progresses, term_width)?; + let exercise = &self.exercises[exercise_ind]; let success = exercise.run_exercise(None, &self.cmd_runner)?; if success { done += 1; + progresses[exercise_ind] = ExerciseCheckProgress::Done; } else { pending += 1; if first_pending_exercise_ind.is_none() { first_pending_exercise_ind = Some(exercise_ind); } + progresses[exercise_ind] = ExerciseCheckProgress::Pending; } self.set_status(exercise_ind, success)?; - stdout.write_all(b"\r")?; - progress_bar_with_success( - stdout, - u16::from(pending + done < n_exercises), - pending, - done, - n_exercises, - term_width, - )?; - stdout.flush()?; + show_exercises_check_progress(stdout, &progresses, term_width)?; } } } self.write()?; - stdout.write_all(b"\n\n")?; + stdout.write_all(b"\n")?; Ok(first_pending_exercise_ind) } + // Return the exercise index of the first pending exercise found. + pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result> { + stdout.queue(cursor::Hide)?; + let res = self.check_all_exercises_impl(stdout); + stdout.queue(cursor::Show)?; + + res + } + /// Mark the current exercise as done and move on to the next pending exercise if one exists. /// If all exercises are marked as done, run all of them to make sure that they are actually /// done. If an exercise which is marked as done fails, mark it as pending and continue on it. diff --git a/src/main.rs b/src/main.rs index f40bb89a18..075e726519 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,6 @@ use anyhow::{bail, Context, Result}; use app_state::StateFileStatus; use clap::{Parser, Subcommand}; -use crossterm::{ - style::{Color, Print, ResetColor, SetForegroundColor}, - QueueableCommand, -}; use std::{ io::{self, IsTerminal, Write}, path::Path, @@ -157,12 +153,13 @@ fn main() -> Result { let pending = app_state.n_pending(); if pending == 1 { - stdout.queue(Print("One exercise pending: "))?; + stdout.write_all(b"One exercise pending: ")?; } else { - stdout.queue(SetForegroundColor(Color::Red))?; - write!(stdout, "{pending}")?; - stdout.queue(ResetColor)?; - stdout.queue(Print(" exercises are pending. The first: "))?; + write!( + stdout, + "{pending}/{} exercises are pending. The first: ", + app_state.exercises().len(), + )?; } app_state .current_exercise() diff --git a/src/term.rs b/src/term.rs index 31a951dbf9..0294017b37 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,6 +1,6 @@ use crossterm::{ cursor::MoveTo, - style::{Attribute, Color, SetAttribute, SetForegroundColor}, + style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, terminal::{Clear, ClearType}, Command, QueueableCommand, }; @@ -9,9 +9,7 @@ use std::{ io::{self, BufRead, StdoutLock, Write}, }; -pub const PROGRESS_FAILED_COLOR: Color = Color::Red; -pub const PROGRESS_SUCCESS_COLOR: Color = Color::Green; -pub const PROGRESS_PENDING_COLOR: Color = Color::Blue; +use crate::app_state::ExerciseCheckProgress; pub struct MaxLenWriter<'a, 'b> { pub stdout: &'a mut StdoutLock<'b>, @@ -89,98 +87,43 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> { } } -/// Simple terminal progress bar. pub fn progress_bar<'a>( writer: &mut impl CountedWrite<'a>, progress: u16, total: u16, term_width: u16, -) -> io::Result<()> { - progress_bar_with_success(writer, 0, 0, progress, total, term_width) -} - -/// Terminal progress bar with three states (pending + failed + success). -pub fn progress_bar_with_success<'a>( - writer: &mut impl CountedWrite<'a>, - pending: u16, - failed: u16, - success: u16, - total: u16, - term_width: u16, ) -> io::Result<()> { debug_assert!(total < 1000); - debug_assert!(pending + failed + success <= total); + debug_assert!(progress <= total); const PREFIX: &[u8] = b"Progress: ["; const PREFIX_WIDTH: u16 = PREFIX.len() as u16; const POSTFIX_WIDTH: u16 = "] xxx/xxx".len() as u16; const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH; - const MIN_TERM_WIDTH: u16 = WRAPPER_WIDTH + 4; + const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4; - if term_width < MIN_TERM_WIDTH { + if term_width < MIN_LINE_WIDTH { writer.write_ascii(b"Progress: ")?; // Integers are in ASCII. - return writer.write_ascii(format!("{}/{total}", failed + success).as_bytes()); + return writer.write_ascii(format!("{progress}/{total}").as_bytes()); } let stdout = writer.stdout(); stdout.write_all(PREFIX)?; let width = term_width - WRAPPER_WIDTH; - let mut failed_end = (width * failed) / total; - let mut success_end = (width * (failed + success)) / total; - let mut pending_end = (width * (failed + success + pending)) / total; - - // In case the range boundaries overlap, "pending" has priority over both - // "failed" and "success" (don't show the bar as "complete" when we are - // still checking some things). - // "Failed" has priority over "success" (don't show 100% success if we - // have some failures, at the risk of showing 100% failures even with - // a few successes). - // - // "Failed" already has priority over "success" because it's displayed - // first. But "pending" is last so we need to fix "success"/"failed". - if pending > 0 { - pending_end = pending_end.max(1); - if pending_end == success_end { - success_end -= 1; - } - if pending_end == failed_end { - failed_end -= 1; - } - - // This will replace the last character of the "pending" range with - // the arrow char ('>'). This ensures that even if the progress bar - // is filled (everything either done or pending), we'll still see - // the '>' as long as we are not fully done. - pending_end -= 1; - } - - if failed > 0 { - stdout.queue(SetForegroundColor(PROGRESS_FAILED_COLOR))?; - for _ in 0..failed_end { - stdout.write_all(b"#")?; - } - } + let filled = (width * progress) / total; - stdout.queue(SetForegroundColor(PROGRESS_SUCCESS_COLOR))?; - for _ in failed_end..success_end { + stdout.queue(SetForegroundColor(Color::Green))?; + for _ in 0..filled { stdout.write_all(b"#")?; } - if pending > 0 { - stdout.queue(SetForegroundColor(PROGRESS_PENDING_COLOR))?; - - for _ in success_end..pending_end { - stdout.write_all(b"#")?; - } - } - - if pending_end < width { + if filled < width { stdout.write_all(b">")?; } - let width_minus_filled = width - pending_end; + let width_minus_filled = width - filled; if width_minus_filled > 1 { let red_part_width = width_minus_filled - 1; stdout.queue(SetForegroundColor(Color::Red))?; @@ -191,7 +134,56 @@ pub fn progress_bar_with_success<'a>( stdout.queue(SetForegroundColor(Color::Reset))?; - write!(stdout, "] {:>3}/{}", failed + success, total) + write!(stdout, "] {progress:>3}/{total}") +} + +pub fn show_exercises_check_progress( + stdout: &mut StdoutLock, + progresses: &[ExerciseCheckProgress], + term_width: u16, +) -> io::Result<()> { + stdout.queue(MoveTo(0, 0))?; + + // Legend + stdout.write_all(b"Color of exercise number: ")?; + stdout.queue(SetForegroundColor(Color::Blue))?; + stdout.write_all(b"Checking")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" - ")?; + stdout.queue(SetForegroundColor(Color::Green))?; + stdout.write_all(b"Done")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" - ")?; + stdout.queue(SetForegroundColor(Color::Red))?; + stdout.write_all(b"Pending")?; + stdout.queue(ResetColor)?; + stdout.write_all(b"\n")?; + + // Exercise numbers with up to 3 digits. + let n_cols = usize::from(term_width + 1) / 4; + + let mut exercise_num = 1; + for exercise_progress in progresses { + let color = match exercise_progress { + ExerciseCheckProgress::None => Color::Reset, + ExerciseCheckProgress::Checking => Color::Blue, + ExerciseCheckProgress::Done => Color::Green, + ExerciseCheckProgress::Pending => Color::Red, + }; + + stdout.queue(SetForegroundColor(color))?; + write!(stdout, "{exercise_num:<3}")?; + + if exercise_num % n_cols == 0 { + stdout.write_all(b"\n")?; + } else { + stdout.write_all(b" ")?; + } + + exercise_num += 1; + } + + stdout.queue(ResetColor)?.flush() } pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { From 8cac21511cbcc148ea7a4c8c6d196c9c0bf17255 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 00:42:49 +0200 Subject: [PATCH 1299/1432] Small improvements to showing progress --- src/app_state.rs | 1 - src/main.rs | 4 +++- src/term.rs | 38 +++++++++++++++++++++++--------------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index db9d1f10c2..41231ef9dc 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -520,7 +520,6 @@ impl AppState { } self.write()?; - stdout.write_all(b"\n")?; Ok(first_pending_exercise_ind) } diff --git a/src/main.rs b/src/main.rs index 075e726519..5616d264e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -151,13 +151,15 @@ fn main() -> Result { app_state.set_current_exercise_ind(first_pending_exercise_ind)?; } + stdout.write_all(b"\n\n")?; + let pending = app_state.n_pending(); if pending == 1 { stdout.write_all(b"One exercise pending: ")?; } else { write!( stdout, - "{pending}/{} exercises are pending. The first: ", + "{pending}/{} exercises pending. The first: ", app_state.exercises().len(), )?; } diff --git a/src/term.rs b/src/term.rs index 0294017b37..8a2f8c597b 100644 --- a/src/term.rs +++ b/src/term.rs @@ -164,26 +164,34 @@ pub fn show_exercises_check_progress( let mut exercise_num = 1; for exercise_progress in progresses { - let color = match exercise_progress { - ExerciseCheckProgress::None => Color::Reset, - ExerciseCheckProgress::Checking => Color::Blue, - ExerciseCheckProgress::Done => Color::Green, - ExerciseCheckProgress::Pending => Color::Red, - }; - - stdout.queue(SetForegroundColor(color))?; + match exercise_progress { + ExerciseCheckProgress::None => (), + ExerciseCheckProgress::Checking => { + stdout.queue(SetForegroundColor(Color::Blue))?; + } + ExerciseCheckProgress::Done => { + stdout.queue(SetForegroundColor(Color::Green))?; + } + ExerciseCheckProgress::Pending => { + stdout.queue(SetForegroundColor(Color::Red))?; + } + } + write!(stdout, "{exercise_num:<3}")?; + stdout.queue(ResetColor)?; - if exercise_num % n_cols == 0 { - stdout.write_all(b"\n")?; - } else { - stdout.write_all(b" ")?; - } + if exercise_num != progresses.len() { + if exercise_num % n_cols == 0 { + stdout.write_all(b"\n")?; + } else { + stdout.write_all(b" ")?; + } - exercise_num += 1; + exercise_num += 1; + } } - stdout.queue(ResetColor)?.flush() + stdout.flush() } pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { From 9705c161b4d9b7fc8b071978f57b35a1b0c69819 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 00:45:41 +0200 Subject: [PATCH 1300/1432] Remove the tracking of done and pending --- src/app_state.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 41231ef9dc..d2dd87bedc 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -417,8 +417,6 @@ impl AppState { clear_terminal(stdout)?; let mut progresses = vec![ExerciseCheckProgress::None; self.exercises.len()]; - let mut done = 0; - let mut pending = 0; thread::scope(|s| { let (exercise_progress_sender, exercise_progress_receiver) = mpsc::channel(); @@ -468,13 +466,6 @@ impl AppState { while let Ok((exercise_ind, progress)) = exercise_progress_receiver.recv() { progresses[exercise_ind] = progress; - - match progress { - ExerciseCheckProgress::None | ExerciseCheckProgress::Checking => (), - ExerciseCheckProgress::Done => done += 1, - ExerciseCheckProgress::Pending => pending += 1, - } - show_exercises_check_progress(stdout, &progresses, term_width)?; } @@ -503,10 +494,8 @@ impl AppState { let exercise = &self.exercises[exercise_ind]; let success = exercise.run_exercise(None, &self.cmd_runner)?; if success { - done += 1; progresses[exercise_ind] = ExerciseCheckProgress::Done; } else { - pending += 1; if first_pending_exercise_ind.is_none() { first_pending_exercise_ind = Some(exercise_ind); } From fc5fc0920f3590d1b1e8c8186309ac1c5ec6fba5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 00:48:12 +0200 Subject: [PATCH 1301/1432] Remove outdated comments --- src/app_state.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index d2dd87bedc..76a4c454e3 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -435,7 +435,6 @@ impl AppState { break; }; - // Notify the progress bar that this exercise is pending. if exercise_progress_sender .send((exercise_ind, ExerciseCheckProgress::Checking)) .is_err() @@ -450,7 +449,6 @@ impl AppState { Err(_) => ExerciseCheckProgress::None, }; - // Notify the progress bar that this exercise is done. if exercise_progress_sender .send((exercise_ind, progress)) .is_err() From ea73af9ba37bc1f6155a910c72f2ded8a0b64805 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 01:06:11 +0200 Subject: [PATCH 1302/1432] Separate initialization with a struct --- src/app_state.rs | 13 +++-- src/term.rs | 125 ++++++++++++++++++++++++++--------------------- 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 76a4c454e3..57ffea8514 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -20,7 +20,7 @@ use crate::{ embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, - term::{self, show_exercises_check_progress}, + term::{self, ExercisesCheckProgressVisualizer}, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; @@ -409,13 +409,12 @@ impl AppState { } fn check_all_exercises_impl(&mut self, stdout: &mut StdoutLock) -> Result> { - stdout.write_all("Checking all exercises…\n".as_bytes())?; - let next_exercise_ind = AtomicUsize::new(0); let term_width = terminal::size() .context("Failed to get the terminal size")? .0; - clear_terminal(stdout)?; + let mut progress_visualizer = ExercisesCheckProgressVisualizer::build(stdout, term_width)?; + let next_exercise_ind = AtomicUsize::new(0); let mut progresses = vec![ExerciseCheckProgress::None; self.exercises.len()]; thread::scope(|s| { @@ -464,7 +463,7 @@ impl AppState { while let Ok((exercise_ind, progress)) = exercise_progress_receiver.recv() { progresses[exercise_ind] = progress; - show_exercises_check_progress(stdout, &progresses, term_width)?; + progress_visualizer.update(&progresses)?; } Ok::<_, Error>(()) @@ -487,7 +486,7 @@ impl AppState { // it could be because we exceeded the limit of open file descriptors. // Therefore, try running exercises with errors sequentially. progresses[exercise_ind] = ExerciseCheckProgress::Checking; - show_exercises_check_progress(stdout, &progresses, term_width)?; + progress_visualizer.update(&progresses)?; let exercise = &self.exercises[exercise_ind]; let success = exercise.run_exercise(None, &self.cmd_runner)?; @@ -501,7 +500,7 @@ impl AppState { } self.set_status(exercise_ind, success)?; - show_exercises_check_progress(stdout, &progresses, term_width)?; + progress_visualizer.update(&progresses)?; } } } diff --git a/src/term.rs b/src/term.rs index 8a2f8c597b..13d5657b9f 100644 --- a/src/term.rs +++ b/src/term.rs @@ -87,6 +87,74 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> { } } +pub struct ExercisesCheckProgressVisualizer<'a, 'b> { + stdout: &'a mut StdoutLock<'b>, + n_cols: usize, +} + +impl<'a, 'b> ExercisesCheckProgressVisualizer<'a, 'b> { + pub fn build(stdout: &'a mut StdoutLock<'b>, term_width: u16) -> io::Result { + clear_terminal(stdout)?; + stdout.write_all("Checking all exercises…\n".as_bytes())?; + + // Legend + stdout.write_all(b"Color of exercise number: ")?; + stdout.queue(SetForegroundColor(Color::Blue))?; + stdout.write_all(b"Checking")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" - ")?; + stdout.queue(SetForegroundColor(Color::Green))?; + stdout.write_all(b"Done")?; + stdout.queue(ResetColor)?; + stdout.write_all(b" - ")?; + stdout.queue(SetForegroundColor(Color::Red))?; + stdout.write_all(b"Pending")?; + stdout.queue(ResetColor)?; + stdout.write_all(b"\n")?; + + // Exercise numbers with up to 3 digits. + // +1 because the last column doesn't end with a whitespace. + let n_cols = usize::from(term_width + 1) / 4; + + Ok(Self { stdout, n_cols }) + } + + pub fn update(&mut self, progresses: &[ExerciseCheckProgress]) -> io::Result<()> { + self.stdout.queue(MoveTo(0, 2))?; + + let mut exercise_num = 1; + for exercise_progress in progresses { + match exercise_progress { + ExerciseCheckProgress::None => (), + ExerciseCheckProgress::Checking => { + self.stdout.queue(SetForegroundColor(Color::Blue))?; + } + ExerciseCheckProgress::Done => { + self.stdout.queue(SetForegroundColor(Color::Green))?; + } + ExerciseCheckProgress::Pending => { + self.stdout.queue(SetForegroundColor(Color::Red))?; + } + } + + write!(self.stdout, "{exercise_num:<3}")?; + self.stdout.queue(ResetColor)?; + + if exercise_num != progresses.len() { + if exercise_num % self.n_cols == 0 { + self.stdout.write_all(b"\n")?; + } else { + self.stdout.write_all(b" ")?; + } + + exercise_num += 1; + } + } + + self.stdout.flush() + } +} + pub fn progress_bar<'a>( writer: &mut impl CountedWrite<'a>, progress: u16, @@ -137,63 +205,6 @@ pub fn progress_bar<'a>( write!(stdout, "] {progress:>3}/{total}") } -pub fn show_exercises_check_progress( - stdout: &mut StdoutLock, - progresses: &[ExerciseCheckProgress], - term_width: u16, -) -> io::Result<()> { - stdout.queue(MoveTo(0, 0))?; - - // Legend - stdout.write_all(b"Color of exercise number: ")?; - stdout.queue(SetForegroundColor(Color::Blue))?; - stdout.write_all(b"Checking")?; - stdout.queue(ResetColor)?; - stdout.write_all(b" - ")?; - stdout.queue(SetForegroundColor(Color::Green))?; - stdout.write_all(b"Done")?; - stdout.queue(ResetColor)?; - stdout.write_all(b" - ")?; - stdout.queue(SetForegroundColor(Color::Red))?; - stdout.write_all(b"Pending")?; - stdout.queue(ResetColor)?; - stdout.write_all(b"\n")?; - - // Exercise numbers with up to 3 digits. - let n_cols = usize::from(term_width + 1) / 4; - - let mut exercise_num = 1; - for exercise_progress in progresses { - match exercise_progress { - ExerciseCheckProgress::None => (), - ExerciseCheckProgress::Checking => { - stdout.queue(SetForegroundColor(Color::Blue))?; - } - ExerciseCheckProgress::Done => { - stdout.queue(SetForegroundColor(Color::Green))?; - } - ExerciseCheckProgress::Pending => { - stdout.queue(SetForegroundColor(Color::Red))?; - } - } - - write!(stdout, "{exercise_num:<3}")?; - stdout.queue(ResetColor)?; - - if exercise_num != progresses.len() { - if exercise_num % n_cols == 0 { - stdout.write_all(b"\n")?; - } else { - stdout.write_all(b" ")?; - } - - exercise_num += 1; - } - } - - stdout.flush() -} - pub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> { stdout .queue(MoveTo(0, 0))? From bdc6dad8de2d3b47e33098fd55956ba03b131b27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 01:28:12 +0200 Subject: [PATCH 1303/1432] Update names --- src/app_state.rs | 29 ++++++++++++++--------------- src/term.rs | 33 +++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 57ffea8514..5f84d35dda 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -20,7 +20,7 @@ use crate::{ embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, - term::{self, ExercisesCheckProgressVisualizer}, + term::{self, CheckProgressVisualizer}, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; @@ -42,7 +42,7 @@ pub enum StateFileStatus { } #[derive(Clone, Copy)] -pub enum ExerciseCheckProgress { +pub enum CheckProgress { None, Checking, Done, @@ -412,10 +412,10 @@ impl AppState { let term_width = terminal::size() .context("Failed to get the terminal size")? .0; - let mut progress_visualizer = ExercisesCheckProgressVisualizer::build(stdout, term_width)?; + let mut progress_visualizer = CheckProgressVisualizer::build(stdout, term_width)?; let next_exercise_ind = AtomicUsize::new(0); - let mut progresses = vec![ExerciseCheckProgress::None; self.exercises.len()]; + let mut progresses = vec![CheckProgress::None; self.exercises.len()]; thread::scope(|s| { let (exercise_progress_sender, exercise_progress_receiver) = mpsc::channel(); @@ -435,7 +435,7 @@ impl AppState { }; if exercise_progress_sender - .send((exercise_ind, ExerciseCheckProgress::Checking)) + .send((exercise_ind, CheckProgress::Checking)) .is_err() { break; @@ -443,9 +443,9 @@ impl AppState { let success = exercise.run_exercise(None, &slf.cmd_runner); let progress = match success { - Ok(true) => ExerciseCheckProgress::Done, - Ok(false) => ExerciseCheckProgress::Pending, - Err(_) => ExerciseCheckProgress::None, + Ok(true) => CheckProgress::Done, + Ok(false) => CheckProgress::Pending, + Err(_) => CheckProgress::None, }; if exercise_progress_sender @@ -472,34 +472,33 @@ impl AppState { let mut first_pending_exercise_ind = None; for exercise_ind in 0..progresses.len() { match progresses[exercise_ind] { - ExerciseCheckProgress::Done => { + CheckProgress::Done => { self.set_status(exercise_ind, true)?; } - ExerciseCheckProgress::Pending => { + CheckProgress::Pending => { self.set_status(exercise_ind, false)?; if first_pending_exercise_ind.is_none() { first_pending_exercise_ind = Some(exercise_ind); } } - ExerciseCheckProgress::None | ExerciseCheckProgress::Checking => { + CheckProgress::None | CheckProgress::Checking => { // If we got an error while checking all exercises in parallel, // it could be because we exceeded the limit of open file descriptors. // Therefore, try running exercises with errors sequentially. - progresses[exercise_ind] = ExerciseCheckProgress::Checking; + progresses[exercise_ind] = CheckProgress::Checking; progress_visualizer.update(&progresses)?; let exercise = &self.exercises[exercise_ind]; let success = exercise.run_exercise(None, &self.cmd_runner)?; if success { - progresses[exercise_ind] = ExerciseCheckProgress::Done; + progresses[exercise_ind] = CheckProgress::Done; } else { + progresses[exercise_ind] = CheckProgress::Pending; if first_pending_exercise_ind.is_none() { first_pending_exercise_ind = Some(exercise_ind); } - progresses[exercise_ind] = ExerciseCheckProgress::Pending; } self.set_status(exercise_ind, success)?; - progress_visualizer.update(&progresses)?; } } diff --git a/src/term.rs b/src/term.rs index 13d5657b9f..86909f08fb 100644 --- a/src/term.rs +++ b/src/term.rs @@ -9,7 +9,7 @@ use std::{ io::{self, BufRead, StdoutLock, Write}, }; -use crate::app_state::ExerciseCheckProgress; +use crate::app_state::CheckProgress; pub struct MaxLenWriter<'a, 'b> { pub stdout: &'a mut StdoutLock<'b>, @@ -87,27 +87,31 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> { } } -pub struct ExercisesCheckProgressVisualizer<'a, 'b> { +pub struct CheckProgressVisualizer<'a, 'b> { stdout: &'a mut StdoutLock<'b>, n_cols: usize, } -impl<'a, 'b> ExercisesCheckProgressVisualizer<'a, 'b> { +impl<'a, 'b> CheckProgressVisualizer<'a, 'b> { + const CHECKING_COLOR: Color = Color::Blue; + const DONE_COLOR: Color = Color::Green; + const PENDING_COLOR: Color = Color::Red; + pub fn build(stdout: &'a mut StdoutLock<'b>, term_width: u16) -> io::Result { clear_terminal(stdout)?; stdout.write_all("Checking all exercises…\n".as_bytes())?; // Legend stdout.write_all(b"Color of exercise number: ")?; - stdout.queue(SetForegroundColor(Color::Blue))?; + stdout.queue(SetForegroundColor(Self::CHECKING_COLOR))?; stdout.write_all(b"Checking")?; stdout.queue(ResetColor)?; stdout.write_all(b" - ")?; - stdout.queue(SetForegroundColor(Color::Green))?; + stdout.queue(SetForegroundColor(Self::DONE_COLOR))?; stdout.write_all(b"Done")?; stdout.queue(ResetColor)?; stdout.write_all(b" - ")?; - stdout.queue(SetForegroundColor(Color::Red))?; + stdout.queue(SetForegroundColor(Self::PENDING_COLOR))?; stdout.write_all(b"Pending")?; stdout.queue(ResetColor)?; stdout.write_all(b"\n")?; @@ -119,21 +123,22 @@ impl<'a, 'b> ExercisesCheckProgressVisualizer<'a, 'b> { Ok(Self { stdout, n_cols }) } - pub fn update(&mut self, progresses: &[ExerciseCheckProgress]) -> io::Result<()> { + pub fn update(&mut self, progresses: &[CheckProgress]) -> io::Result<()> { self.stdout.queue(MoveTo(0, 2))?; let mut exercise_num = 1; for exercise_progress in progresses { match exercise_progress { - ExerciseCheckProgress::None => (), - ExerciseCheckProgress::Checking => { - self.stdout.queue(SetForegroundColor(Color::Blue))?; + CheckProgress::None => (), + CheckProgress::Checking => { + self.stdout + .queue(SetForegroundColor(Self::CHECKING_COLOR))?; } - ExerciseCheckProgress::Done => { - self.stdout.queue(SetForegroundColor(Color::Green))?; + CheckProgress::Done => { + self.stdout.queue(SetForegroundColor(Self::DONE_COLOR))?; } - ExerciseCheckProgress::Pending => { - self.stdout.queue(SetForegroundColor(Color::Red))?; + CheckProgress::Pending => { + self.stdout.queue(SetForegroundColor(Self::PENDING_COLOR))?; } } From 932bc25d8824e18debc91e5f25f022e8d066bcf8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 01:28:34 +0200 Subject: [PATCH 1304/1432] Remove unneeded line --- src/main.rs | 2 +- src/run.rs | 1 + src/watch/state.rs | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5616d264e6..c8bcd2e563 100644 --- a/src/main.rs +++ b/src/main.rs @@ -152,7 +152,6 @@ fn main() -> Result { } stdout.write_all(b"\n\n")?; - let pending = app_state.n_pending(); if pending == 1 { stdout.write_all(b"One exercise pending: ")?; @@ -167,6 +166,7 @@ fn main() -> Result { .current_exercise() .terminal_file_link(&mut stdout)?; stdout.write_all(b"\n")?; + return Ok(ExitCode::FAILURE); } else { app_state.render_final_message(&mut stdout)?; diff --git a/src/run.rs b/src/run.rs index f259f52c80..ac8b26ad76 100644 --- a/src/run.rs +++ b/src/run.rs @@ -29,6 +29,7 @@ pub fn run(app_state: &mut AppState) -> Result { .current_exercise() .terminal_file_link(&mut stdout)?; stdout.write_all(b" with errors\n")?; + return Ok(ExitCode::FAILURE); } diff --git a/src/watch/state.rs b/src/watch/state.rs index 8b58e3117a..0ac758cef9 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -281,8 +281,6 @@ impl<'a> WatchState<'a> { } pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result { - stdout.write_all(b"\n")?; - if let Some(first_pending_exercise_ind) = self.app_state.check_all_exercises(stdout)? { // Only change exercise if the current one is done. if self.app_state.current_exercise().done { From a675cb5754309ba9997fd2344ab0a364688de430 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 15:24:42 +0200 Subject: [PATCH 1305/1432] Replace ahash with foldhash --- Cargo.lock | 86 +++++++++++++++------------------------------- Cargo.toml | 6 ++-- clippy.toml | 2 +- src/collections.rs | 9 +++-- 4 files changed, 35 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a541c67d3..f89c139f86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "anstream" version = "0.6.15" @@ -71,9 +59,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" @@ -95,9 +83,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -105,9 +93,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -198,6 +186,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -209,9 +203,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" @@ -227,9 +221,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -377,9 +371,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "os_pipe" @@ -416,9 +410,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -434,9 +428,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -458,10 +452,10 @@ dependencies = [ name = "rustlings" version = "6.3.0" dependencies = [ - "ahash", "anyhow", "clap", "crossterm", + "foldhash", "notify", "os_pipe", "rustix", @@ -587,9 +581,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -598,9 +592,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -643,12 +637,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "walkdir" version = "2.5.0" @@ -852,23 +840,3 @@ checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/Cargo.toml b/Cargo.toml index d4134fd8a7..eb22cfee41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,10 +46,10 @@ include = [ ] [dependencies] -ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.89" -clap = { version = "4.5.18", features = ["derive"] } +clap = { version = "4.5.20", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } +foldhash = "0.1.3" notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] } os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } @@ -61,7 +61,7 @@ toml_edit.workspace = true rustix = { version = "0.38.37", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.12.0" +tempfile = "3.13.0" [profile.release] panic = "abort" diff --git a/clippy.toml b/clippy.toml index 11ec6cc345..2a981849bb 100644 --- a/clippy.toml +++ b/clippy.toml @@ -5,7 +5,7 @@ disallowed-types = [ ] disallowed-methods = [ - # We use `ahash` instead of the default hasher. + # We use `foldhash` instead of the default hasher. "std::collections::HashSet::new", "std::collections::HashSet::with_capacity", # Inefficient. Use `.queue(…)` instead. diff --git a/src/collections.rs b/src/collections.rs index fa9e3fa7c9..3f2841e038 100644 --- a/src/collections.rs +++ b/src/collections.rs @@ -1,10 +1,9 @@ -use ahash::AHasher; -use std::hash::BuildHasherDefault; +use foldhash::fast::FixedState; -/// DOS attacks aren't a concern for Rustlings. Therefore, we use `ahash` with fixed seeds. -pub type HashSet = std::collections::HashSet>; +/// DOS attacks aren't a concern for Rustlings. Therefore, we use `foldhash` with a fixed state. +pub type HashSet = std::collections::HashSet; #[inline] pub fn hash_set_with_capacity(capacity: usize) -> HashSet { - HashSet::with_capacity_and_hasher(capacity, BuildHasherDefault::::default()) + HashSet::with_capacity_and_hasher(capacity, FixedState::default()) } From 990a722852ab22b55db342f93ebe03e6ed122f7f Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 14 Oct 2024 15:57:44 +0200 Subject: [PATCH 1306/1432] Limit the maximum number of exercises to 999 --- src/dev/check.rs | 5 +++++ src/term.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 5a7aaed415..bd73ec8cf4 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -17,6 +17,7 @@ use crate::{ CURRENT_FORMAT_VERSION, }; +const MAX_N_EXERCISES: usize = 999; const MAX_EXERCISE_NAME_LEN: usize = 32; // Find a char that isn't allowed in the exercise's `name` or `dir`. @@ -347,6 +348,10 @@ fn check_solutions( pub fn check(require_solutions: bool) -> Result<()> { let info_file = InfoFile::parse()?; + if info_file.exercises.len() > MAX_N_EXERCISES { + bail!("The maximum number of exercises is {MAX_N_EXERCISES}"); + } + if cfg!(debug_assertions) { // A hack to make `cargo run -- dev check` work when developing Rustlings. check_cargo_toml(&info_file.exercises, "dev/Cargo.toml", b"../")?; diff --git a/src/term.rs b/src/term.rs index 86909f08fb..e3bfd7bb19 100644 --- a/src/term.rs +++ b/src/term.rs @@ -166,7 +166,7 @@ pub fn progress_bar<'a>( total: u16, term_width: u16, ) -> io::Result<()> { - debug_assert!(total < 1000); + debug_assert!(total <= 999); debug_assert!(progress <= total); const PREFIX: &[u8] = b"Progress: ["; From f33ba139b460ee433c6738ab050534b951436d6d Mon Sep 17 00:00:00 2001 From: Nahor Date: Mon, 14 Oct 2024 10:13:30 -0700 Subject: [PATCH 1307/1432] Fix typos --- src/app_state.rs | 6 +++--- src/dev/check.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 5f84d35dda..4007fbc3f6 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -148,11 +148,11 @@ impl AppState { let mut done_exercises = hash_set_with_capacity(exercises.len()); - for done_exerise_name in lines { - if done_exerise_name.is_empty() { + for done_exercise_name in lines { + if done_exercise_name.is_empty() { break; } - done_exercises.insert(done_exerise_name); + done_exercises.insert(done_exercise_name); } for (ind, exercise) in exercises.iter_mut().enumerate() { diff --git a/src/dev/check.rs b/src/dev/check.rs index bd73ec8cf4..119fed5fda 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -202,7 +202,7 @@ fn check_exercises_unsolved( for (exercise_name, handle) in handles { let Ok(result) = handle.join() else { - bail!("Panic while trying to run the exericse {exercise_name}"); + bail!("Panic while trying to run the exercise {exercise_name}"); }; match result { @@ -300,7 +300,7 @@ fn check_solutions( for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { let Ok(check_result) = handle.join() else { bail!( - "Panic while trying to run the solution of the exericse {}", + "Panic while trying to run the solution of the exercise {}", exercise_info.name, ); }; From f146553dead78357cd44736dfca97b1349418fa2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 17 Oct 2024 14:37:47 +0200 Subject: [PATCH 1308/1432] hashmap3: Use `or_default` --- rustlings-macros/info.toml | 8 ++------ solutions/11_hashmaps/hashmaps3.rs | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index c1342d68ec..e705598187 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -575,12 +575,8 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if- name = "hashmaps3" dir = "11_hashmaps" hint = """ -Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of - `HashMap` to insert the default value of `TeamScores` if a team doesn't - exist in the table yet. - -Learn more in The Book: -https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +Hint 1: Use the `entry()` and `or_default()` methods of `HashMap` to insert the + default value of `TeamScores` if a team doesn't exist in the table yet. Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value. diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 8a5d30b669..41da784b26 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -28,17 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap(); // Insert the default with zeros if a team doesn't exist yet. - let team_1 = scores - .entry(team_1_name) - .or_insert_with(TeamScores::default); + let team_1 = scores.entry(team_1_name).or_default(); // Update the values. team_1.goals_scored += team_1_score; team_1.goals_conceded += team_2_score; // Similarly for the second team. - let team_2 = scores - .entry(team_2_name) - .or_insert_with(TeamScores::default); + let team_2 = scores.entry(team_2_name).or_default(); team_2.goals_scored += team_2_score; team_2.goals_conceded += team_1_score; } From 99496706c5041affdc252649bfc74d50a2187271 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 17 Oct 2024 14:44:48 +0200 Subject: [PATCH 1309/1432] Apply new Clippy lints --- src/cmd.rs | 2 +- src/term.rs | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cmd.rs b/src/cmd.rs index 4a93312adb..30f988a6de 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -125,7 +125,7 @@ pub struct CargoSubcommand<'out> { output: Option<&'out mut Vec>, } -impl<'out> CargoSubcommand<'out> { +impl CargoSubcommand<'_> { #[inline] pub fn args<'arg, I>(&mut self, args: I) -> &mut Self where diff --git a/src/term.rs b/src/term.rs index e3bfd7bb19..cb0a07ceaf 100644 --- a/src/term.rs +++ b/src/term.rs @@ -11,15 +11,15 @@ use std::{ use crate::app_state::CheckProgress; -pub struct MaxLenWriter<'a, 'b> { - pub stdout: &'a mut StdoutLock<'b>, +pub struct MaxLenWriter<'a, 'lock> { + pub stdout: &'a mut StdoutLock<'lock>, len: usize, max_len: usize, } -impl<'a, 'b> MaxLenWriter<'a, 'b> { +impl<'a, 'lock> MaxLenWriter<'a, 'lock> { #[inline] - pub fn new(stdout: &'a mut StdoutLock<'b>, max_len: usize) -> Self { + pub fn new(stdout: &'a mut StdoutLock<'lock>, max_len: usize) -> Self { Self { stdout, len: 0, @@ -34,13 +34,13 @@ impl<'a, 'b> MaxLenWriter<'a, 'b> { } } -pub trait CountedWrite<'a> { +pub trait CountedWrite<'lock> { fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()>; fn write_str(&mut self, unicode: &str) -> io::Result<()>; - fn stdout(&mut self) -> &mut StdoutLock<'a>; + fn stdout(&mut self) -> &mut StdoutLock<'lock>; } -impl<'a, 'b> CountedWrite<'b> for MaxLenWriter<'a, 'b> { +impl<'lock> CountedWrite<'lock> for MaxLenWriter<'_, 'lock> { fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> { let n = ascii.len().min(self.max_len.saturating_sub(self.len)); if n > 0 { @@ -65,7 +65,7 @@ impl<'a, 'b> CountedWrite<'b> for MaxLenWriter<'a, 'b> { } #[inline] - fn stdout(&mut self) -> &mut StdoutLock<'b> { + fn stdout(&mut self) -> &mut StdoutLock<'lock> { self.stdout } } @@ -87,17 +87,17 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> { } } -pub struct CheckProgressVisualizer<'a, 'b> { - stdout: &'a mut StdoutLock<'b>, +pub struct CheckProgressVisualizer<'a, 'lock> { + stdout: &'a mut StdoutLock<'lock>, n_cols: usize, } -impl<'a, 'b> CheckProgressVisualizer<'a, 'b> { +impl<'a, 'lock> CheckProgressVisualizer<'a, 'lock> { const CHECKING_COLOR: Color = Color::Blue; const DONE_COLOR: Color = Color::Green; const PENDING_COLOR: Color = Color::Red; - pub fn build(stdout: &'a mut StdoutLock<'b>, term_width: u16) -> io::Result { + pub fn build(stdout: &'a mut StdoutLock<'lock>, term_width: u16) -> io::Result { clear_terminal(stdout)?; stdout.write_all("Checking all exercises…\n".as_bytes())?; From 0e090ae11244ee8e0cdb50501b34c36a7112fd0c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 17 Oct 2024 14:48:56 +0200 Subject: [PATCH 1310/1432] Add required type annotation --- exercises/11_hashmaps/hashmaps3.rs | 2 +- solutions/11_hashmaps/hashmaps3.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 7e9584d1ad..5b390ab9b8 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -17,7 +17,7 @@ struct TeamScores { fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { // The name of the team is the key and its associated struct is the value. - let mut scores = HashMap::new(); + let mut scores = HashMap::<&str, TeamScores>::new(); for line in results.lines() { let mut split_iterator = line.split(','); diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 41da784b26..433b16c331 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -17,7 +17,7 @@ struct TeamScores { fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> { // The name of the team is the key and its associated struct is the value. - let mut scores = HashMap::new(); + let mut scores = HashMap::<&str, TeamScores>::new(); for line in results.lines() { let mut split_iterator = line.split(','); From e90f5f03f3da639bf3157aec12ebf0cec62ac7ae Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 17 Oct 2024 14:59:37 +0200 Subject: [PATCH 1311/1432] Mention the Q&A category --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b65920f239..9a223670cf 100644 --- a/README.md +++ b/README.md @@ -124,14 +124,13 @@ The list allows you to… - See the status of all exercises (done or pending) - `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one) -- `r`: Reset status and file of an exercise (you need to _reload/reopen_ its file in your editor afterwards) +- `r`: Reset status and file of the selected exercise (you need to _reload/reopen_ its file in your editor afterwards) See the footer of the list for all possible keys. -## Continuing On +## Questions? -Once you've completed Rustlings, put your new knowledge to good use! -Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. +If you need any help while doing the exercises and the builtin-hints aren't helpful, feel free to ask in the [_Q&A_ category of the discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question wasn't asked yet πŸ’‘ ## Third-Party Exercises @@ -144,6 +143,11 @@ Do you want to create your own set of Rustlings exercises to focus on some speci Or do you want to translate the original Rustlings exercises? Then follow the the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)! +## Continuing On + +Once you've completed Rustlings, put your new knowledge to good use! +Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. + ## Uninstalling Rustlings If you want to remove Rustlings from your system, run the following command: From 7e2f56f41a89213d3ae60a069402a25b570f0cca Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 17 Oct 2024 15:03:43 +0200 Subject: [PATCH 1312/1432] Use the default hasher --- Cargo.lock | 15 ++++----------- Cargo.toml | 1 - clippy.toml | 3 --- src/app_state.rs | 4 ++-- src/collections.rs | 9 --------- src/dev/check.rs | 8 ++++---- src/main.rs | 1 - 7 files changed, 10 insertions(+), 31 deletions(-) delete mode 100644 src/collections.rs diff --git a/Cargo.lock b/Cargo.lock index f89c139f86..1ac56b40e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,12 +186,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "foldhash" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -283,9 +277,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" [[package]] name = "libredox" @@ -410,9 +404,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -455,7 +449,6 @@ dependencies = [ "anyhow", "clap", "crossterm", - "foldhash", "notify", "os_pipe", "rustix", diff --git a/Cargo.toml b/Cargo.toml index eb22cfee41..4dbcb5fbfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ include = [ anyhow = "1.0.89" clap = { version = "4.5.20", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } -foldhash = "0.1.3" notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] } os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } diff --git a/clippy.toml b/clippy.toml index 2a981849bb..afc9253a51 100644 --- a/clippy.toml +++ b/clippy.toml @@ -5,9 +5,6 @@ disallowed-types = [ ] disallowed-methods = [ - # We use `foldhash` instead of the default hasher. - "std::collections::HashSet::new", - "std::collections::HashSet::with_capacity", # Inefficient. Use `.queue(…)` instead. "crossterm::style::style", # Use `thread::Builder::spawn` instead and handle the error. diff --git a/src/app_state.rs b/src/app_state.rs index 4007fbc3f6..5979150faa 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,6 +1,7 @@ use anyhow::{bail, Context, Error, Result}; use crossterm::{cursor, terminal, QueueableCommand}; use std::{ + collections::HashSet, env, fs::{File, OpenOptions}, io::{Read, Seek, StdoutLock, Write}, @@ -16,7 +17,6 @@ use std::{ use crate::{ clear_terminal, cmd::CmdRunner, - collections::hash_set_with_capacity, embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, @@ -146,7 +146,7 @@ impl AppState { break 'block StateFileStatus::NotRead; } - let mut done_exercises = hash_set_with_capacity(exercises.len()); + let mut done_exercises = HashSet::with_capacity(exercises.len()); for done_exercise_name in lines { if done_exercise_name.is_empty() { diff --git a/src/collections.rs b/src/collections.rs deleted file mode 100644 index 3f2841e038..0000000000 --- a/src/collections.rs +++ /dev/null @@ -1,9 +0,0 @@ -use foldhash::fast::FixedState; - -/// DOS attacks aren't a concern for Rustlings. Therefore, we use `foldhash` with a fixed state. -pub type HashSet = std::collections::HashSet; - -#[inline] -pub fn hash_set_with_capacity(capacity: usize) -> HashSet { - HashSet::with_capacity_and_hasher(capacity, FixedState::default()) -} diff --git a/src/dev/check.rs b/src/dev/check.rs index 119fed5fda..956c2be2de 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, bail, Context, Error, Result}; use std::{ cmp::Ordering, + collections::HashSet, fs::{self, read_dir, OpenOptions}, io::{self, Read, Write}, path::{Path, PathBuf}, @@ -11,7 +12,6 @@ use std::{ use crate::{ cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY}, cmd::CmdRunner, - collections::{hash_set_with_capacity, HashSet}, exercise::{RunnableExercise, OUTPUT_CAPACITY}, info_file::{ExerciseInfo, InfoFile}, CURRENT_FORMAT_VERSION, @@ -53,8 +53,8 @@ fn check_cargo_toml( // Check the info of all exercises and return their paths in a set. fn check_info_file_exercises(info_file: &InfoFile) -> Result> { - let mut names = hash_set_with_capacity(info_file.exercises.len()); - let mut paths = hash_set_with_capacity(info_file.exercises.len()); + let mut names = HashSet::with_capacity(info_file.exercises.len()); + let mut paths = HashSet::with_capacity(info_file.exercises.len()); let mut file_buf = String::with_capacity(1 << 14); for exercise_info in &info_file.exercises { @@ -282,7 +282,7 @@ fn check_solutions( .collect::, _>>() .context("Failed to spawn a thread to check a solution")?; - let mut sol_paths = hash_set_with_capacity(info_file.exercises.len()); + let mut sol_paths = HashSet::with_capacity(info_file.exercises.len()); let mut fmt_cmd = Command::new("rustfmt"); fmt_cmd .arg("--check") diff --git a/src/main.rs b/src/main.rs index c8bcd2e563..eeb1883edd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,6 @@ use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile}; mod app_state; mod cargo_toml; mod cmd; -mod collections; mod dev; mod embedded; mod exercise; From 930a0ea73b74921d687f3389f8dfb99f8fda8cea Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 17 Oct 2024 16:00:10 +0200 Subject: [PATCH 1313/1432] list: Highlight search match in exercise names --- src/list/state.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 5bdbca77eb..53fe07c418 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -105,6 +105,28 @@ impl<'a> ListState<'a> { ); } + fn draw_exericse_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> { + if !self.search_query.is_empty() { + if let Some((pre_highlight, highlight, post_highlight)) = exercise + .name + .find(&self.search_query) + .and_then(|ind| exercise.name.split_at_checked(ind)) + .and_then(|(pre_highlight, rest)| { + rest.split_at_checked(self.search_query.len()) + .map(|x| (pre_highlight, x.0, x.1)) + }) + { + writer.write_str(pre_highlight)?; + writer.stdout.queue(SetForegroundColor(Color::Magenta))?; + writer.write_str(highlight)?; + writer.stdout.queue(ResetColor)?; + return writer.write_str(post_highlight); + } + } + + writer.write_str(exercise.name) + } + fn draw_rows( &self, stdout: &mut StdoutLock, @@ -147,10 +169,10 @@ impl<'a> ListState<'a> { writer.stdout.queue(SetForegroundColor(Color::Yellow))?; writer.write_ascii(b"PENDING ")?; } - writer.stdout.queue(SetForegroundColor(Color::Reset))?; - writer.write_str(exercise.name)?; + self.draw_exericse_name(&mut writer, exercise)?; + writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?; // The list links aren't shown correctly in VS Code on Windows. From 6bec6f92c4f0fe14ed56ad646514e89f6d0ee7cc Mon Sep 17 00:00:00 2001 From: Vincent Ging Ho Yim Date: Tue, 22 Oct 2024 16:49:44 +1100 Subject: [PATCH 1314/1432] threads1: Fix typos in description --- exercises/20_threads/threads1.rs | 4 ++-- solutions/20_threads/threads1.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index 01f9ff44cf..dbc64b1644 100644 --- a/exercises/20_threads/threads1.rs +++ b/exercises/20_threads/threads1.rs @@ -1,5 +1,5 @@ -// 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 +// This program spawns multiple threads that each runs for at least 250ms, and +// each thread returns how much time it took to complete. The program should // wait until all the spawned threads have finished and should collect their // return values into a vector. diff --git a/solutions/20_threads/threads1.rs b/solutions/20_threads/threads1.rs index 7f3dd29a1a..1fc5bc9c06 100644 --- a/solutions/20_threads/threads1.rs +++ b/solutions/20_threads/threads1.rs @@ -1,5 +1,5 @@ -// 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 +// This program spawns multiple threads that each runs for at least 250ms, and +// each thread returns how much time it took to complete. The program should // wait until all the spawned threads have finished and should collect their // return values into a vector. From e8c2a79516192761283f41acc68744e91a34d6b4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 26 Oct 2024 16:53:07 +0200 Subject: [PATCH 1315/1432] Deduplicate code for printing keys --- src/watch/state.rs | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/watch/state.rs b/src/watch/state.rs index 0ac758cef9..47978aba34 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -177,39 +177,25 @@ impl<'a> WatchState<'a> { stdout.write_all(b" / ")?; } - if self.manual_run { + let mut show_key = |key, postfix| { stdout.queue(SetAttribute(Attribute::Bold))?; - stdout.write_all(b"r")?; + stdout.write_all(&[key])?; stdout.queue(ResetColor)?; - stdout.write_all(b":run / ")?; + stdout.write_all(postfix) + }; + + if self.manual_run { + show_key(b'r', b":run / ")?; } if !self.show_hint { - stdout.queue(SetAttribute(Attribute::Bold))?; - stdout.write_all(b"h")?; - stdout.queue(ResetColor)?; - stdout.write_all(b":hint / ")?; + show_key(b'h', b":hint / ")?; } - stdout.queue(SetAttribute(Attribute::Bold))?; - stdout.write_all(b"l")?; - stdout.queue(ResetColor)?; - stdout.write_all(b":list / ")?; - - stdout.queue(SetAttribute(Attribute::Bold))?; - stdout.write_all(b"c")?; - stdout.queue(ResetColor)?; - stdout.write_all(b":check all / ")?; - - stdout.queue(SetAttribute(Attribute::Bold))?; - stdout.write_all(b"x")?; - stdout.queue(ResetColor)?; - stdout.write_all(b":reset / ")?; - - stdout.queue(SetAttribute(Attribute::Bold))?; - stdout.write_all(b"q")?; - stdout.queue(ResetColor)?; - stdout.write_all(b":quit ? ")?; + show_key(b'l', b":list / ")?; + show_key(b'c', b":check all / ")?; + show_key(b'x', b":reset / ")?; + show_key(b'q', b":quit ? ")?; stdout.flush() } From 449858655d5302efda18288a3d0f138b6e83c463 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 26 Oct 2024 16:54:54 +0200 Subject: [PATCH 1316/1432] Update deps --- Cargo.lock | 56 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 6 +++--- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ac56b40e0..a44f271721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -19,43 +19,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "autocfg" @@ -123,9 +123,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "crossterm" @@ -277,9 +277,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.160" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libredox" @@ -404,9 +404,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -491,18 +491,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ "proc-macro2", "quote", @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -574,9 +574,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4dbcb5fbfe..f05e5250ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.210", features = ["derive"] } +serde = { version = "1.0.213", features = ["derive"] } toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] } [package] @@ -46,13 +46,13 @@ include = [ ] [dependencies] -anyhow = "1.0.89" +anyhow = "1.0.91" clap = { version = "4.5.20", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] } os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } -serde_json = "1.0.128" +serde_json = "1.0.132" serde.workspace = true toml_edit.workspace = true From 2a725fb13719de0597a34c633cb59da7c7244534 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 29 Oct 2024 14:25:44 +0100 Subject: [PATCH 1317/1432] Upgrade notify --- Cargo.lock | 145 ++++++++++++++++------------------------------------- Cargo.toml | 6 +-- 2 files changed, 46 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a44f271721..beb01df3b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,7 +135,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.6.0", "crossterm_winapi", - "mio 1.0.2", + "mio", "parking_lot", "rustix", "signal-hook", @@ -225,9 +225,9 @@ dependencies = [ [[package]] name = "inotify" -version = "0.9.6" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ "bitflags 1.3.2", "inotify-sys", @@ -243,6 +243,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -320,18 +329,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.2" @@ -347,9 +344,9 @@ dependencies = [ [[package]] name = "notify" -version = "6.1.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" dependencies = [ "bitflags 2.6.0", "filetime", @@ -358,9 +355,19 @@ dependencies = [ "kqueue", "libc", "log", - "mio 0.8.11", + "mio", + "notify-types", "walkdir", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" +dependencies = [ + "instant", ] [[package]] @@ -399,7 +406,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -431,9 +438,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags 2.6.0", "errno", @@ -491,18 +498,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -547,7 +554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio 1.0.2", + "mio", "signal-hook", ] @@ -677,22 +684,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -701,22 +699,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -725,46 +708,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -777,48 +742,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index f05e5250ee..e9fe07902b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.213", features = ["derive"] } +serde = { version = "1.0.214", features = ["derive"] } toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] } [package] @@ -49,7 +49,7 @@ include = [ anyhow = "1.0.91" clap = { version = "4.5.20", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } -notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] } +notify = "7.0.0" os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } serde_json = "1.0.132" @@ -57,7 +57,7 @@ serde.workspace = true toml_edit.workspace = true [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.38.37", default-features = false, features = ["std", "stdio", "termios"] } +rustix = { version = "0.38.38", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] tempfile = "3.13.0" From 46ad25f9257836d4d1303874f0a618b6c319b263 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 14:34:33 +0100 Subject: [PATCH 1318/1432] Fix contrast in terminals with a light theme --- src/list/state.rs | 43 ++++++++++++++++++++++++++++--------------- src/watch/state.rs | 8 +++++--- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 53fe07c418..76392d1c5c 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -1,7 +1,9 @@ use anyhow::{Context, Result}; use crossterm::{ cursor::{MoveTo, MoveToNextLine}, - style::{Attribute, Color, ResetColor, SetAttribute, SetBackgroundColor, SetForegroundColor}, + style::{ + Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor, + }, terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate}, QueueableCommand, }; @@ -19,6 +21,9 @@ use crate::{ use super::scroll_state::ScrollState; const COL_SPACING: usize = 2; +const SELECTED_ROW_ATTRIBUTES: Attributes = Attributes::none() + .with(Attribute::Reverse) + .with(Attribute::Bold); fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> { stdout @@ -41,6 +46,7 @@ pub struct ListState<'a> { app_state: &'a mut AppState, scroll_state: ScrollState, name_col_padding: Vec, + path_col_padding: Vec, filter: Filter, term_width: u16, term_height: u16, @@ -52,13 +58,18 @@ impl<'a> ListState<'a> { stdout.queue(Clear(ClearType::All))?; let name_col_title_len = 4; - let name_col_width = app_state - .exercises() - .iter() - .map(|exercise| exercise.name.len()) - .max() - .map_or(name_col_title_len, |max| max.max(name_col_title_len)); + let path_col_title_len = 4; + let (name_col_width, path_col_width) = app_state.exercises().iter().fold( + (name_col_title_len, path_col_title_len), + |(name_col_width, path_col_width), exercise| { + ( + name_col_width.max(exercise.name.len()), + path_col_width.max(exercise.path.len()), + ) + }, + ); let name_col_padding = vec![b' '; name_col_width + COL_SPACING]; + let path_col_padding = vec![b' '; path_col_width]; let filter = Filter::None; let n_rows_with_filter = app_state.exercises().len(); @@ -73,6 +84,7 @@ impl<'a> ListState<'a> { app_state, scroll_state, name_col_padding, + path_col_padding, filter, // Set by `set_term_size` term_width: 0, @@ -119,7 +131,7 @@ impl<'a> ListState<'a> { writer.write_str(pre_highlight)?; writer.stdout.queue(SetForegroundColor(Color::Magenta))?; writer.write_str(highlight)?; - writer.stdout.queue(ResetColor)?; + writer.stdout.queue(SetForegroundColor(Color::Reset))?; return writer.write_str(post_highlight); } } @@ -143,14 +155,12 @@ impl<'a> ListState<'a> { let mut writer = MaxLenWriter::new(stdout, self.term_width as usize); if self.scroll_state.selected() == Some(row_offset + n_displayed_rows) { - writer.stdout.queue(SetBackgroundColor(Color::Rgb { - r: 40, - g: 40, - b: 40, - }))?; // The crab emoji has the width of two ascii chars. writer.add_to_len(2); writer.stdout.write_all("πŸ¦€".as_bytes())?; + writer + .stdout + .queue(SetAttributes(SELECTED_ROW_ATTRIBUTES))?; } else { writer.write_ascii(b" ")?; } @@ -164,12 +174,13 @@ impl<'a> ListState<'a> { if exercise.done { writer.stdout.queue(SetForegroundColor(Color::Green))?; - writer.write_ascii(b"DONE ")?; + writer.write_ascii(b"DONE ")?; } else { writer.stdout.queue(SetForegroundColor(Color::Yellow))?; - writer.write_ascii(b"PENDING ")?; + writer.write_ascii(b"PENDING")?; } writer.stdout.queue(SetForegroundColor(Color::Reset))?; + writer.write_ascii(b" ")?; self.draw_exericse_name(&mut writer, exercise)?; @@ -183,6 +194,8 @@ impl<'a> ListState<'a> { exercise.terminal_file_link(&mut writer)?; } + writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?; + next_ln(stdout)?; stdout.queue(ResetColor)?; n_displayed_rows += 1; diff --git a/src/watch/state.rs b/src/watch/state.rs index 47978aba34..c27dedf397 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -20,6 +20,10 @@ use crate::{ use super::{terminal_event::terminal_event_handler, InputPauseGuard, WatchEvent}; +const HEADING_ATTRIBUTES: Attributes = Attributes::none() + .with(Attribute::Bold) + .with(Attribute::Underlined); + #[derive(PartialEq, Eq)] enum DoneStatus { DoneWithSolution(String), @@ -209,9 +213,7 @@ impl<'a> WatchState<'a> { if self.show_hint { stdout - .queue(SetAttributes( - Attributes::from(Attribute::Bold).with(Attribute::Underlined), - ))? + .queue(SetAttributes(HEADING_ATTRIBUTES))? .queue(SetForegroundColor(Color::Cyan))?; stdout.write_all(b"Hint")?; stdout.queue(ResetColor)?; From 9bc7bbe4b43d26d3646f249eda11562f90e54cee Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 14:35:22 +0100 Subject: [PATCH 1319/1432] Update deps --- Cargo.lock | 36 ++++++++++++++++++------------------ Cargo.toml | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index beb01df3b9..23c416ff04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -53,9 +53,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "autocfg" @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "filetime" @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libredox" @@ -438,9 +438,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.38" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -581,9 +581,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -592,9 +592,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", diff --git a/Cargo.toml b/Cargo.toml index e9fe07902b..5be6d70650 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ include = [ ] [dependencies] -anyhow = "1.0.91" +anyhow = "1.0.93" clap = { version = "4.5.20", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = "7.0.0" @@ -60,7 +60,7 @@ toml_edit.workspace = true rustix = { version = "0.38.38", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.13.0" +tempfile = "3.14.0" [profile.release] panic = "abort" From f49164e69b3be368b51aa71e2f2d40f5a7c6a319 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 14:43:38 +0100 Subject: [PATCH 1320/1432] Fix typo --- src/list/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/list/state.rs b/src/list/state.rs index 76392d1c5c..0670fa46a8 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -117,7 +117,7 @@ impl<'a> ListState<'a> { ); } - fn draw_exericse_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> { + fn draw_exercise_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> { if !self.search_query.is_empty() { if let Some((pre_highlight, highlight, post_highlight)) = exercise .name @@ -182,7 +182,7 @@ impl<'a> ListState<'a> { writer.stdout.queue(SetForegroundColor(Color::Reset))?; writer.write_ascii(b" ")?; - self.draw_exericse_name(&mut writer, exercise)?; + self.draw_exercise_name(&mut writer, exercise)?; writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?; From fd33c29b262e249de720dc8b04e878cd92a83d13 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 14:43:51 +0100 Subject: [PATCH 1321/1432] Test with MSRV before release --- release-hook.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release-hook.sh b/release-hook.sh index d5954ca81f..8da5636dea 100755 --- a/release-hook.sh +++ b/release-hook.sh @@ -11,3 +11,6 @@ cargo clippy -- --deny warnings cargo fmt --all --check cargo test --workspace --all-targets cargo run -- dev check --require-solutions + +# MSRV +cargo +1.80 run -- dev check --require-solutions From eff2ce8a23bcd8f979dff917bcdb83dccbaa4170 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 14:55:58 +0100 Subject: [PATCH 1322/1432] Ignore input while checking all exercises in watch mode --- src/watch/state.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/watch/state.rs b/src/watch/state.rs index c27dedf397..5263bc5788 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -269,6 +269,9 @@ impl<'a> WatchState<'a> { } pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result { + // Ignore any input until checking all exercises is done. + let _input_pause_guard = InputPauseGuard::scoped_pause(); + if let Some(first_pending_exercise_ind) = self.app_state.check_all_exercises(stdout)? { // Only change exercise if the current one is done. if self.app_state.current_exercise().done { From 243cf5f2610c64183331d77e3d8c803c551dabeb Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 15:49:24 +0100 Subject: [PATCH 1323/1432] Update CHANGELOG --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19bb8fc356..a2085b91bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ + + +## 6.4.0 (2024-11-11) + +### Added + +- The list of exercises is now searchable by pressing `s` or `/` πŸ”οΈ (thanks to [@frroossst](https://github.com/frroossst)) +- New option `c` in the prompt to manually check all exercises βœ… (thanks to [@Nahor](https://github.com/Nahor)) +- New command `check-all` to manually check all exercises βœ… (thanks to [@Nahor](https://github.com/Nahor)) +- Addictive animation for showing the progress of checking all exercises. A nice showcase of parallelism in Rust ✨ +- New option `x` in the prompt to reset the file of the current exercise πŸ”„ +- Allow `dead_code` for all exercises and solutions ⚰️ (thanks to [@huss4in](https://github.com/huss4in)) +- Pause input while running an exercise to avoid unexpected prompt interactions ⏸️ +- Limit the maximum number of exercises to 999. Any third-party exercises willing to reach that limit? πŸ” + +### Changed + +- `enums3`: Remove redundant enum definition task (thanks to [@senekor](https://github.com/senekor)) +- `if2`: Make the exercise less confusing by avoiding "fizz", "fuzz", "foo", "bar" and "baz" (thanks to [@senekor](https://github.com/senekor)) +- `hashmap3`: Use the method `Entry::or_default`. +- Update the state of all exercises when checking all of them (thanks to [@Nahor](https://github.com/Nahor)) +- The main prompt doesn't need a confirmation with ENTER on Unix-like systems anymore. +- No more jumping back to a previous exercise when its file is changed. Use the list to jump between exercises. +- Dump the solution file after an exercise is done even if the solution's directory doesn't exist. +- Rework the footer in the list. +- Optimize the file watcher. + +### Fixed + +- Fix bad contrast in the list on terminals with a light theme. + ## 6.3.0 (2024-08-29) @@ -113,7 +144,7 @@ You can read about the motivations of this change in [this issue](https://github ### List mode -A list mode was added using [Ratatui](https://ratatui.rs). +A new list mode was added! You can enter it by entering `l` in the watch mode. It offers the following features: @@ -814,7 +845,7 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER #### Bug Fixes -- Update deps to version compatable with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401)) +- Update deps to version compatible with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401)) - **docs:** - Added a necessary step to Windows installation process (#242) ([3906efcd](https://github.com/rust-lang/rustlings/commit/3906efcd52a004047b460ed548037093de3f523f)) - Fixed mangled sentence from book; edited for clarity (#266) ([ade52ff](https://github.com/rust-lang/rustlings/commit/ade52ffb739987287ddd5705944c8777705faed9)) From 410eb69d250a2c856e9d23b60a4e77e5558b8134 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 15:49:50 +0100 Subject: [PATCH 1324/1432] Remove "chore: " from the commit message of releases --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 5be6d70650..2397ad3d96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ panic = "abort" [package.metadata.release] pre-release-hook = ["./release-hook.sh"] +pre-release-commit-message = "Release πŸŽ‰" [workspace.lints.rust] unsafe_code = "forbid" From e6cb1042946816a6ac835cf1f15a71898bdf4ed6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 11 Nov 2024 15:51:27 +0100 Subject: [PATCH 1325/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23c416ff04..a61cf39af8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,7 +451,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.3.0" +version = "6.4.0" dependencies = [ "anyhow", "clap", @@ -468,7 +468,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.3.0" +version = "6.4.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 2397ad3d96..ff88de2dcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ exclude = [ ] [workspace.package] -version = "6.3.0" +version = "6.4.0" authors = [ "Mo Bitar ", # https://github.com/mo8it "Liv ", # https://github.com/shadows-withal @@ -51,7 +51,7 @@ clap = { version = "4.5.20", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = "7.0.0" os_pipe = "1.2.1" -rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } serde_json = "1.0.132" serde.workspace = true toml_edit.workspace = true From 38016cb2d6053c7d4f18c7ca98880a3ac7d392fa Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 13 Nov 2024 16:06:41 +0100 Subject: [PATCH 1326/1432] clippy3: Make the intent more clear --- exercises/22_clippy/clippy3.rs | 6 ++++-- solutions/22_clippy/clippy3.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/exercises/22_clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs index 4f78834918..7a3cb39006 100644 --- a/exercises/22_clippy/clippy3.rs +++ b/exercises/22_clippy/clippy3.rs @@ -4,9 +4,11 @@ #[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fn main() { - let my_option: Option<()> = None; + let my_option: Option<&str> = None; + // Assume that you don't know the value of `my_option`. + // In the case of `Some`, we want to print its value. if my_option.is_none() { - println!("{:?}", my_option.unwrap()); + println!("{}", my_option.unwrap()); } let my_arr = &[ diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs index 811d184759..b7eaa57079 100644 --- a/solutions/22_clippy/clippy3.rs +++ b/solutions/22_clippy/clippy3.rs @@ -3,11 +3,11 @@ use std::mem; #[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fn main() { - let my_option: Option<()> = None; + let my_option: Option<&str> = None; // `unwrap` of an `Option` after checking if it is `None` will panic. // Use `if-let` instead. if let Some(value) = my_option { - println!("{value:?}"); + println!("{value}"); } // A comma was missing. From d5cae8ff597ab85d817d3abb6f30159f1823a0f2 Mon Sep 17 00:00:00 2001 From: Antoine Dupuis Date: Wed, 13 Nov 2024 23:51:09 +0100 Subject: [PATCH 1327/1432] Add alternative solution using From trait --- solutions/13_error_handling/errors6.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs index 86793619f2..7bad200b9d 100644 --- a/solutions/13_error_handling/errors6.rs +++ b/solutions/13_error_handling/errors6.rs @@ -29,6 +29,21 @@ impl ParsePosNonzeroError { } } +/// As an alternative solution, implementing the `From` trait allows for the +/// automatic conversion from a `ParseIntError` into a `ParsePosNonzeroError` +/// using the `?` operator, without the need to call `map_err`. +/// +/// ``` +/// let x: i64 = s.parse()?; +/// ``` +/// +/// Traits like `From` will be dealt with in later exercises. +impl From for ParsePosNonzeroError { + fn from(err: ParseIntError) -> Self { + ParsePosNonzeroError::ParseInt(err) + } +} + #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); From fc0cd8f0f88a03b7fdb69a6ba668b8479bd3eddd Mon Sep 17 00:00:00 2001 From: Antoine Dupuis Date: Thu, 14 Nov 2024 09:14:40 +0100 Subject: [PATCH 1328/1432] Switch comment style to // --- solutions/13_error_handling/errors6.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs index 7bad200b9d..ce18073afb 100644 --- a/solutions/13_error_handling/errors6.rs +++ b/solutions/13_error_handling/errors6.rs @@ -29,15 +29,15 @@ impl ParsePosNonzeroError { } } -/// As an alternative solution, implementing the `From` trait allows for the -/// automatic conversion from a `ParseIntError` into a `ParsePosNonzeroError` -/// using the `?` operator, without the need to call `map_err`. -/// -/// ``` -/// let x: i64 = s.parse()?; -/// ``` -/// -/// Traits like `From` will be dealt with in later exercises. +// As an alternative solution, implementing the `From` trait allows for the +// automatic conversion from a `ParseIntError` into a `ParsePosNonzeroError` +// using the `?` operator, without the need to call `map_err`. +// +// ``` +// let x: i64 = s.parse()?; +// ``` +// +// Traits like `From` will be dealt with in later exercises. impl From for ParsePosNonzeroError { fn from(err: ParseIntError) -> Self { ParsePosNonzeroError::ParseInt(err) From d07de879a7dcfd5645bb8a658d0b20eff37250dc Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 11 Dec 2024 00:12:22 +0100 Subject: [PATCH 1329/1432] Update deps --- Cargo.lock | 83 +++++++++++++++++++++++++----------------------------- Cargo.toml | 10 +++---- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a61cf39af8..f3c4af7506 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,9 +53,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "autocfg" @@ -83,9 +83,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.20" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -93,9 +93,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", @@ -117,9 +117,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" @@ -160,19 +160,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "filetime" @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -207,17 +207,11 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -260,9 +254,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "kqueue" @@ -286,9 +280,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libredox" @@ -331,11 +325,10 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", "log", "wasi", @@ -411,9 +404,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -438,15 +431,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -498,18 +491,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -518,9 +511,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -581,9 +574,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -627,9 +620,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "utf8parse" diff --git a/Cargo.toml b/Cargo.toml index ff88de2dcc..102bd6c5b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.214", features = ["derive"] } +serde = { version = "1.0.215", features = ["derive"] } toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] } [package] @@ -46,18 +46,18 @@ include = [ ] [dependencies] -anyhow = "1.0.93" -clap = { version = "4.5.20", features = ["derive"] } +anyhow = "1.0.94" +clap = { version = "4.5.23", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = "7.0.0" os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } -serde_json = "1.0.132" +serde_json = "1.0.133" serde.workspace = true toml_edit.workspace = true [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.38.38", default-features = false, features = ["std", "stdio", "termios"] } +rustix = { version = "0.38.42", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] tempfile = "3.14.0" From 6e60f441e9e93e7ebc3dc0072de7eaef8052931d Mon Sep 17 00:00:00 2001 From: Joel Marcey Date: Fri, 13 Dec 2024 10:44:21 -0800 Subject: [PATCH 1330/1432] Fix argument comment in test of if2.rs --- exercises/03_if/if2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index 10037f262a..ca8493cc81 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -19,7 +19,7 @@ mod tests { #[test] fn yummy_food() { - // This means that calling `picky_eater` with the argument "food" should return "Yummy!". + // This means that calling `picky_eater` with the argument "strawberry" should return "Yummy!". assert_eq!(picky_eater("strawberry"), "Yummy!"); } From ed1ee38923adb39c37453721060a8843c98605e7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 28 Dec 2024 16:40:07 +0100 Subject: [PATCH 1331/1432] Link to simplified Chinese translation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a223670cf..e0f659237e 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,8 @@ If you need any help while doing the exercises and the builtin-hints aren't help Third-party exercises are a set of exercises maintained by the community. You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them: -- [ζ—₯本θͺžη‰ˆ Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. +- πŸ‡―πŸ‡΅ [Japanese Translation](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. +- πŸ‡¨πŸ‡³ [Simplified Chinese Translation](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises. Do you want to create your own set of Rustlings exercises to focus on some specific topic? Or do you want to translate the original Rustlings exercises? From 53ec59ed95bfddf17ade467c6bceb702b7d04d51 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 28 Dec 2024 16:41:43 +0100 Subject: [PATCH 1332/1432] Rename translations --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e0f659237e..3118451f80 100644 --- a/README.md +++ b/README.md @@ -137,8 +137,8 @@ If you need any help while doing the exercises and the builtin-hints aren't help Third-party exercises are a set of exercises maintained by the community. You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them: -- πŸ‡―πŸ‡΅ [Japanese Translation](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. -- πŸ‡¨πŸ‡³ [Simplified Chinese Translation](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises. +- πŸ‡―πŸ‡΅ [Japanese Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. +- πŸ‡¨πŸ‡³ [Simplified Chinese Rustlings](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises. Do you want to create your own set of Rustlings exercises to focus on some specific topic? Or do you want to translate the original Rustlings exercises? From bde6f7470c651c1d608eda6a18940b092a982184 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 28 Dec 2024 16:46:24 +0100 Subject: [PATCH 1333/1432] Co-ordinates -> Coordinates --- exercises/12_options/options3.rs | 2 +- solutions/12_options/options3.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 4cedb51242..c97b1d3cf4 100644 --- a/exercises/12_options/options3.rs +++ b/exercises/12_options/options3.rs @@ -9,7 +9,7 @@ fn main() { // TODO: Fix the compiler error by adding something to this match statement. match optional_point { - Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), + Some(p) => println!("Coordinates are {},{}", p.x, p.y), _ => panic!("No match!"), } diff --git a/solutions/12_options/options3.rs b/solutions/12_options/options3.rs index 0081eeb2c7..c918f711dc 100644 --- a/solutions/12_options/options3.rs +++ b/solutions/12_options/options3.rs @@ -10,7 +10,7 @@ fn main() { // Solution 1: Matching over the `Option` (not `&Option`) but without moving // out of the `Some` variant. match optional_point { - Some(ref p) => println!("Co-ordinates are {},{}", p.x, p.y), + Some(ref p) => println!("Coordinates are {},{}", p.x, p.y), // ^^^ added _ => panic!("No match!"), } @@ -18,7 +18,8 @@ fn main() { // Solution 2: Matching over a reference (`&Option`) by added `&` before // `optional_point`. match &optional_point { - Some(p) => println!("Co-ordinates are {},{}", p.x, p.y), + //^ added + Some(p) => println!("Coordinates are {},{}", p.x, p.y), _ => panic!("No match!"), } From 0b55809bb9c75ecdb5018e214a5ee3aa0363348f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 1 Jan 2025 22:01:39 +0100 Subject: [PATCH 1334/1432] Fix building from source on Windows --- build.rs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..79a1fadda0 --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +fn main() { + // Fix building from source on Windows because it can't handle file links. + #[cfg(windows)] + std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml") + .expect("Failed to copy the file `dev/Cargo.toml` to `dev-Cargo.toml`"); +} From 1aec7c1152e9b57142d5efaee4a9d95072b760bf Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 1 Jan 2025 22:07:41 +0100 Subject: [PATCH 1335/1432] Fix Windows CI --- build.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.rs b/build.rs index 79a1fadda0..5687864183 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,5 @@ fn main() { // Fix building from source on Windows because it can't handle file links. #[cfg(windows)] - std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml") - .expect("Failed to copy the file `dev/Cargo.toml` to `dev-Cargo.toml`"); + let _ = std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml"); } From d12735a57336c8bbad2c26ea0328d88c117918b5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 16 Jan 2025 10:41:17 +0100 Subject: [PATCH 1336/1432] Update deps --- Cargo.lock | 127 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 14 +++--- 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3c4af7506..32750f0399 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,19 +43,20 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "autocfg" @@ -71,9 +72,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "cfg-if" @@ -83,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -93,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -105,9 +106,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck", "proc-macro2", @@ -133,7 +134,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm_winapi", "mio", "parking_lot", @@ -195,6 +196,17 @@ dependencies = [ "libc", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -219,11 +231,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.8.0", "inotify-sys", "libc", ] @@ -237,15 +249,6 @@ dependencies = [ "libc", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -280,9 +283,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libredox" @@ -290,16 +293,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lock_api" @@ -313,9 +316,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "memchr" @@ -337,11 +340,11 @@ dependencies = [ [[package]] name = "notify" -version = "7.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "filetime", "fsevent-sys", "inotify", @@ -351,17 +354,14 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "notify-types" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" -dependencies = [ - "instant", -] +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "once_cell" @@ -404,38 +404,38 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -491,18 +491,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -574,9 +574,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -585,12 +585,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -761,9 +762,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 102bd6c5b0..c0eb1d126b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c rust-version = "1.80" [workspace.dependencies] -serde = { version = "1.0.215", features = ["derive"] } +serde = { version = "1.0.217", features = ["derive"] } toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] } [package] @@ -46,21 +46,21 @@ include = [ ] [dependencies] -anyhow = "1.0.94" -clap = { version = "4.5.23", features = ["derive"] } +anyhow = "1.0.95" +clap = { version = "4.5.26", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } -notify = "7.0.0" +notify = "8.0.0" os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } -serde_json = "1.0.133" +serde_json = "1.0.135" serde.workspace = true toml_edit.workspace = true [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.38.42", default-features = false, features = ["std", "stdio", "termios"] } +rustix = { version = "0.38.43", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.14.0" +tempfile = "3.15.0" [profile.release] panic = "abort" From fbfd4f25e7e715007f5f3f6678f5d336d24d3660 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 16 Jan 2025 10:41:48 +0100 Subject: [PATCH 1337/1432] Disable following symlinks in the watcher --- src/watch.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/watch.rs b/src/watch.rs index 6259c9df38..3a56b4b65b 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -74,7 +74,9 @@ fn run_watch( let mut watcher = RecommendedWatcher::new( notify_event_handler, - Config::default().with_poll_interval(Duration::from_secs(1)), + Config::default() + .with_follow_symlinks(false) + .with_poll_interval(Duration::from_secs(1)), ) .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; From 298be671b9108e490fcef6b8febace8659ec710a Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 18 Feb 2025 20:03:49 +0100 Subject: [PATCH 1338/1432] Update deps --- Cargo.lock | 87 +++++++++++++++++++++++++++++++++--------------------- Cargo.toml | 10 +++---- 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32750f0399..3c340b645f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.26" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.26" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -198,13 +198,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -221,9 +222,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", @@ -334,7 +335,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -365,9 +366,9 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "os_pipe" @@ -431,9 +432,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.8.0", "errno", @@ -470,9 +471,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -511,9 +512,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -562,9 +563,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "strsim" @@ -574,9 +575,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -585,9 +586,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", @@ -608,9 +609,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", @@ -621,9 +622,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "utf8parse" @@ -647,6 +648,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "winapi" version = "0.3.9" @@ -762,9 +772,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" dependencies = [ "memchr", ] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] diff --git a/Cargo.toml b/Cargo.toml index c0eb1d126b..c229a3fd19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ rust-version = "1.80" [workspace.dependencies] serde = { version = "1.0.217", features = ["derive"] } -toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.24", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -47,20 +47,20 @@ include = [ [dependencies] anyhow = "1.0.95" -clap = { version = "4.5.26", features = ["derive"] } +clap = { version = "4.5.30", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = "8.0.0" os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } -serde_json = "1.0.135" +serde_json = "1.0.138" serde.workspace = true toml_edit.workspace = true [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.38.43", default-features = false, features = ["std", "stdio", "termios"] } +rustix = { version = "0.38.44", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.15.0" +tempfile = "3.17.1" [profile.release] panic = "abort" From d9872f2615a11ce94deb85c8f1c215d69abd7992 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 18 Feb 2025 20:10:52 +0100 Subject: [PATCH 1339/1432] Upgrade to edition 2024 --- CHANGELOG.md | 7 ++++ Cargo.toml | 4 +- dev/Cargo.toml | 2 +- src/app_state.rs | 60 +++++++++++++++-------------- src/cmd.rs | 2 +- src/dev.rs | 2 +- src/dev/check.rs | 60 +++++++++++++++++++++-------- src/dev/new.rs | 10 +++-- src/exercise.rs | 4 +- src/info_file.rs | 2 +- src/init.rs | 12 ++++-- src/list.rs | 7 ++-- src/list/state.rs | 4 +- src/main.rs | 2 +- src/run.rs | 4 +- src/term.rs | 2 +- src/watch/notify_event.rs | 6 +-- src/watch/state.rs | 9 +++-- src/watch/terminal_event.rs | 2 +- tests/test_exercises/dev/Cargo.toml | 2 +- 20 files changed, 122 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2085b91bc..b9826cf002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +### Changed + +- Upgrade to Rust edition 2024 +- Raise the minimum supported Rust version to `1.85` + ## 6.4.0 (2024-11-11) diff --git a/Cargo.toml b/Cargo.toml index c229a3fd19..e9b29eca05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ authors = [ ] repository = "https://github.com/rust-lang/rustlings" license = "MIT" -edition = "2021" # On Update: Update the edition of the `rustfmt` command that checks the solutions. -rust-version = "1.80" +edition = "2024" # On Update: Update the edition of the `rustfmt` command that checks the solutions. +rust-version = "1.85" [workspace.dependencies] serde = { version = "1.0.217", features = ["derive"] } diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 29a557a019..ae380d1751 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -192,7 +192,7 @@ bin = [ [package] name = "exercises" -edition = "2021" +edition = "2024" # Don't publish the exercises on crates.io! publish = false diff --git a/src/app_state.rs b/src/app_state.rs index 5979150faa..d1c45d4963 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,11 +1,11 @@ -use anyhow::{bail, Context, Error, Result}; -use crossterm::{cursor, terminal, QueueableCommand}; +use anyhow::{Context, Error, Result, bail}; +use crossterm::{QueueableCommand, cursor, terminal}; use std::{ collections::HashSet, env, fs::{File, OpenOptions}, io::{Read, Seek, StdoutLock, Write}, - path::{Path, MAIN_SEPARATOR_STR}, + path::{MAIN_SEPARATOR_STR, Path}, process::{Command, Stdio}, sync::{ atomic::{AtomicUsize, Ordering::Relaxed}, @@ -427,32 +427,34 @@ impl AppState { let next_exercise_ind = &next_exercise_ind; let slf = &self; thread::Builder::new() - .spawn_scoped(s, move || loop { - let exercise_ind = next_exercise_ind.fetch_add(1, Relaxed); - let Some(exercise) = slf.exercises.get(exercise_ind) else { - // No more exercises. - break; - }; - - if exercise_progress_sender - .send((exercise_ind, CheckProgress::Checking)) - .is_err() - { - break; - }; - - let success = exercise.run_exercise(None, &slf.cmd_runner); - let progress = match success { - Ok(true) => CheckProgress::Done, - Ok(false) => CheckProgress::Pending, - Err(_) => CheckProgress::None, - }; - - if exercise_progress_sender - .send((exercise_ind, progress)) - .is_err() - { - break; + .spawn_scoped(s, move || { + loop { + let exercise_ind = next_exercise_ind.fetch_add(1, Relaxed); + let Some(exercise) = slf.exercises.get(exercise_ind) else { + // No more exercises. + break; + }; + + if exercise_progress_sender + .send((exercise_ind, CheckProgress::Checking)) + .is_err() + { + break; + }; + + let success = exercise.run_exercise(None, &slf.cmd_runner); + let progress = match success { + Ok(true) => CheckProgress::Done, + Ok(false) => CheckProgress::Pending, + Err(_) => CheckProgress::None, + }; + + if exercise_progress_sender + .send((exercise_ind, progress)) + .is_err() + { + break; + } } }) .context("Failed to spawn a thread to check all exercises")?; diff --git a/src/cmd.rs b/src/cmd.rs index 30f988a6de..551df8f0ae 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use serde::Deserialize; use std::{ io::Read, diff --git a/src/dev.rs b/src/dev.rs index 8af40d6943..354d77c4b1 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use clap::Subcommand; use std::path::PathBuf; diff --git a/src/dev/check.rs b/src/dev/check.rs index 956c2be2de..aacc2f440f 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,8 +1,8 @@ -use anyhow::{anyhow, bail, Context, Error, Result}; +use anyhow::{Context, Error, Result, anyhow, bail}; use std::{ cmp::Ordering, collections::HashSet, - fs::{self, read_dir, OpenOptions}, + fs::{self, OpenOptions, read_dir}, io::{self, Read, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, @@ -10,11 +10,11 @@ use std::{ }; use crate::{ - cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY}, + CURRENT_FORMAT_VERSION, + cargo_toml::{BINS_BUFFER_CAPACITY, append_bins, bins_start_end_ind}, cmd::CmdRunner, - exercise::{RunnableExercise, OUTPUT_CAPACITY}, + exercise::{OUTPUT_CAPACITY, RunnableExercise}, info_file::{ExerciseInfo, InfoFile}, - CURRENT_FORMAT_VERSION, }; const MAX_N_EXERCISES: usize = 999; @@ -42,10 +42,14 @@ fn check_cargo_toml( if old_bins != new_bins { if cfg!(debug_assertions) { - bail!("The file `dev/Cargo.toml` is outdated. Run `cargo run -- dev update` to update it. Then run `cargo run -- dev check` again"); + bail!( + "The file `dev/Cargo.toml` is outdated. Run `cargo run -- dev update` to update it. Then run `cargo run -- dev check` again" + ); } - bail!("The file `Cargo.toml` is outdated. Run `rustlings dev update` to update it. Then run `rustlings dev check` again"); + bail!( + "The file `Cargo.toml` is outdated. Run `rustlings dev update` to update it. Then run `rustlings dev check` again" + ); } Ok(()) @@ -63,7 +67,9 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { bail!("Found an empty exercise name in `info.toml`"); } if name.len() > MAX_EXERCISE_NAME_LEN { - bail!("The length of the exercise name `{name}` is bigger than the maximum {MAX_EXERCISE_NAME_LEN}"); + bail!( + "The length of the exercise name `{name}` is bigger than the maximum {MAX_EXERCISE_NAME_LEN}" + ); } if let Some(c) = forbidden_char(name) { bail!("Char `{c}` in the exercise name `{name}` is not allowed"); @@ -79,7 +85,9 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { } if exercise_info.hint.trim_ascii().is_empty() { - bail!("The exercise `{name}` has an empty hint. Please provide a hint or at least tell the user why a hint isn't needed for this exercise"); + bail!( + "The exercise `{name}` has an empty hint. Please provide a hint or at least tell the user why a hint isn't needed for this exercise" + ); } if !names.insert(name) { @@ -96,20 +104,28 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { .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"); + bail!( + "The `main` function is missing in the file `{path}`.\nCreate at least an empty `main` function to avoid language server errors" + ); } if !file_buf.contains("// TODO") { - bail!("Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user."); + bail!( + "Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user." + ); } let contains_tests = file_buf.contains("#[test]\n"); if exercise_info.test { if !contains_tests { - bail!("The file `{path}` doesn't contain any tests. If you don't want to add tests to this exercise, set `test = false` for this exercise in the `info.toml` file"); + bail!( + "The file `{path}` doesn't contain any tests. If you don't want to add tests to this exercise, set `test = false` for this exercise in the `info.toml` file" + ); } } else if contains_tests { - bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file"); + bail!( + "The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file" + ); } file_buf.clear(); @@ -125,7 +141,10 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { // Only one level of directory nesting is allowed. fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet) -> Result<()> { let unexpected_file = |path: &Path| { - anyhow!("Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory", path.display()) + anyhow!( + "Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory", + path.display() + ) }; for entry in read_dir(dir).with_context(|| format!("Failed to open the `{dir}` directory"))? { @@ -154,7 +173,10 @@ fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet) -> R 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()); + bail!( + "Found `{}` but expected only files. Only one level of exercise nesting is allowed", + path.display() + ); } let file_name = path.file_name().unwrap(); @@ -224,8 +246,12 @@ fn check_exercises_unsolved( fn check_exercises(info_file: &'static InfoFile, cmd_runner: &'static CmdRunner) -> 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::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 => (), } diff --git a/src/dev/new.rs b/src/dev/new.rs index 154cd2247b..ba3517f5b8 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use std::{ env::set_current_dir, fs::{self, create_dir}, @@ -6,7 +6,7 @@ use std::{ process::Command, }; -use crate::{init::RUST_ANALYZER_TOML, CURRENT_FORMAT_VERSION}; +use crate::{CURRENT_FORMAT_VERSION, init::RUST_ANALYZER_TOML}; // Create a directory relative to the current directory and print its path. fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> { @@ -55,7 +55,9 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { write_rel_file( "info.toml", &dir_path_str, - format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"), + format!( + "{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}" + ), )?; write_rel_file("Cargo.toml", &dir_path_str, CARGO_TOML)?; @@ -130,7 +132,7 @@ bin = [] [package] name = "exercises" -edition = "2021" +edition = "2024" # Don't publish the exercises on crates.io! publish = false diff --git a/src/exercise.rs b/src/exercise.rs index 849082847a..fdfbc4f6ea 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,13 +1,13 @@ use anyhow::Result; use crossterm::{ - style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, QueueableCommand, + style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, }; use std::io::{self, StdoutLock, Write}; use crate::{ cmd::CmdRunner, - term::{self, terminal_file_link, write_ansi, CountedWrite}, + term::{self, CountedWrite, terminal_file_link, write_ansi}, }; /// The initial capacity of the output buffer. diff --git a/src/info_file.rs b/src/info_file.rs index fdc8f0f35c..ec61f8ad0b 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Error, Result}; +use anyhow::{Context, Error, Result, bail}; use serde::Deserialize; use std::{fs, io::ErrorKind}; diff --git a/src/init.rs b/src/init.rs index ce49bb65fa..208425c2a3 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,7 +1,7 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use crossterm::{ - style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, QueueableCommand, + style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, }; use serde::Deserialize; use std::{ @@ -57,7 +57,9 @@ pub fn init() -> Result<()> { if !workspace_manifest_content.contains("[workspace]\n") && !workspace_manifest_content.contains("workspace.") { - bail!("The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory"); + bail!( + "The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory" + ); } stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\nPress ENTER to continue ")?; @@ -75,7 +77,9 @@ pub fn init() -> Result<()> { .stdout(Stdio::null()) .status()?; if !status.success() { - bail!("Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory"); + bail!( + "Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory" + ); } stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the `Cargo.toml` file of this Cargo workspace.\n")?; diff --git a/src/list.rs b/src/list.rs index 9f243a1724..a2eee9e16a 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,14 +1,13 @@ use anyhow::{Context, Result}; use crossterm::{ - cursor, + QueueableCommand, cursor, event::{ self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, MouseEventKind, }, terminal::{ - disable_raw_mode, enable_raw_mode, DisableLineWrap, EnableLineWrap, EnterAlternateScreen, - LeaveAlternateScreen, + DisableLineWrap, EnableLineWrap, EnterAlternateScreen, LeaveAlternateScreen, + disable_raw_mode, enable_raw_mode, }, - QueueableCommand, }; use std::io::{self, StdoutLock, Write}; diff --git a/src/list/state.rs b/src/list/state.rs index 0670fa46a8..ae65ec2be9 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -1,11 +1,11 @@ use anyhow::{Context, Result}; use crossterm::{ + QueueableCommand, cursor::{MoveTo, MoveToNextLine}, style::{ Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor, }, terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate}, - QueueableCommand, }; use std::{ fmt::Write as _, @@ -15,7 +15,7 @@ use std::{ use crate::{ app_state::AppState, exercise::Exercise, - term::{progress_bar, CountedWrite, MaxLenWriter}, + term::{CountedWrite, MaxLenWriter, progress_bar}, }; use super::scroll_state::ScrollState; diff --git a/src/main.rs b/src/main.rs index eeb1883edd..6688e3e6f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use app_state::StateFileStatus; use clap::{Parser, Subcommand}; use std::{ diff --git a/src/run.rs b/src/run.rs index ac8b26ad76..6f4f099b47 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,7 +1,7 @@ use anyhow::Result; use crossterm::{ - style::{Color, ResetColor, SetForegroundColor}, QueueableCommand, + style::{Color, ResetColor, SetForegroundColor}, }; use std::{ io::{self, Write}, @@ -10,7 +10,7 @@ use std::{ use crate::{ app_state::{AppState, ExercisesProgress}, - exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, + exercise::{OUTPUT_CAPACITY, RunnableExercise, solution_link_line}, }; pub fn run(app_state: &mut AppState) -> Result { diff --git a/src/term.rs b/src/term.rs index cb0a07ceaf..1e08c84f64 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,8 +1,8 @@ use crossterm::{ + Command, QueueableCommand, cursor::MoveTo, style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor}, terminal::{Clear, ClearType}, - Command, QueueableCommand, }; use std::{ fmt, fs, diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index 2051e544bf..9c05f10dad 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -1,18 +1,18 @@ use anyhow::{Context, Result}; use notify::{ - event::{AccessKind, AccessMode, MetadataKind, ModifyKind, RenameMode}, Event, EventKind, + event::{AccessKind, AccessMode, MetadataKind, ModifyKind, RenameMode}, }; use std::{ sync::{ atomic::Ordering::Relaxed, - mpsc::{sync_channel, RecvTimeoutError, Sender, SyncSender}, + mpsc::{RecvTimeoutError, Sender, SyncSender, sync_channel}, }, thread, time::Duration, }; -use super::{WatchEvent, EXERCISE_RUNNING}; +use super::{EXERCISE_RUNNING, WatchEvent}; const DEBOUNCE_DURATION: Duration = Duration::from_millis(200); diff --git a/src/watch/state.rs b/src/watch/state.rs index 5263bc5788..2413becd28 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,24 +1,25 @@ use anyhow::{Context, Result}; use crossterm::{ + QueueableCommand, style::{ Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor, }, - terminal, QueueableCommand, + terminal, }; use std::{ io::{self, Read, StdoutLock, Write}, - sync::mpsc::{sync_channel, Sender, SyncSender}, + sync::mpsc::{Sender, SyncSender, sync_channel}, thread, }; use crate::{ app_state::{AppState, ExercisesProgress}, clear_terminal, - exercise::{solution_link_line, RunnableExercise, OUTPUT_CAPACITY}, + exercise::{OUTPUT_CAPACITY, RunnableExercise, solution_link_line}, term::progress_bar, }; -use super::{terminal_event::terminal_event_handler, InputPauseGuard, WatchEvent}; +use super::{InputPauseGuard, WatchEvent, terminal_event::terminal_event_handler}; const HEADING_ATTRIBUTES: Attributes = Attributes::none() .with(Attribute::Bold) diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 48411db0f7..2400a3df54 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -4,7 +4,7 @@ use std::sync::{ mpsc::{Receiver, Sender}, }; -use super::{WatchEvent, EXERCISE_RUNNING}; +use super::{EXERCISE_RUNNING, WatchEvent}; pub enum InputEvent { Next, diff --git a/tests/test_exercises/dev/Cargo.toml b/tests/test_exercises/dev/Cargo.toml index 01fe7c10d0..74dcc20ade 100644 --- a/tests/test_exercises/dev/Cargo.toml +++ b/tests/test_exercises/dev/Cargo.toml @@ -7,5 +7,5 @@ bin = [ [package] name = "test_exercises" -edition = "2021" +edition = "2024" publish = false From a56ccb6f4ff168803b44b975a257acb028a82cad Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 18 Feb 2025 20:12:23 +0100 Subject: [PATCH 1340/1432] Fix new Clippy lint --- src/cargo_toml.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index 8d417ffa93..e966809f35 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -74,13 +74,13 @@ pub fn updated_cargo_toml( let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?; let mut updated_cargo_toml = Vec::with_capacity(BINS_BUFFER_CAPACITY); - updated_cargo_toml.extend_from_slice(current_cargo_toml[..bins_start_ind].as_bytes()); + updated_cargo_toml.extend_from_slice(¤t_cargo_toml.as_bytes()[..bins_start_ind]); append_bins( &mut updated_cargo_toml, exercise_infos, exercise_path_prefix, ); - updated_cargo_toml.extend_from_slice(current_cargo_toml[bins_end_ind..].as_bytes()); + updated_cargo_toml.extend_from_slice(¤t_cargo_toml.as_bytes()[bins_end_ind..]); Ok(updated_cargo_toml) } From 65dc019fa6a5fe7518fcb77f1f076dfc5b8d9f1b Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 18 Feb 2025 20:15:28 +0100 Subject: [PATCH 1341/1432] Fix new Clippy error in solution --- solutions/13_error_handling/errors2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs index 0597c8c9d9..f0e144e746 100644 --- a/solutions/13_error_handling/errors2.rs +++ b/solutions/13_error_handling/errors2.rs @@ -16,7 +16,7 @@ use std::num::ParseIntError; -#[allow(unused_variables)] +#[allow(unused_variables, clippy::question_mark)] fn total_cost(item_quantity: &str) -> Result { let processing_fee = 1; let cost_per_item = 5; From 06af3ffc99f803f3be349bbb88bb0ce3b31982e0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 18 Feb 2025 20:17:27 +0100 Subject: [PATCH 1342/1432] Bump MSRV in release hook --- release-hook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-hook.sh b/release-hook.sh index 8da5636dea..db3d86eecd 100755 --- a/release-hook.sh +++ b/release-hook.sh @@ -13,4 +13,4 @@ cargo test --workspace --all-targets cargo run -- dev check --require-solutions # MSRV -cargo +1.80 run -- dev check --require-solutions +cargo +1.85 run -- dev check --require-solutions From 1eb6c1e469de2492d823d4739114d8a85cd6660b Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Feb 2025 13:06:11 +0100 Subject: [PATCH 1343/1432] Update the edition of the solution format checker --- Cargo.toml | 2 +- src/dev/check.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e9b29eca05..a3b1d0d734 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ authors = [ ] repository = "https://github.com/rust-lang/rustlings" license = "MIT" -edition = "2024" # On Update: Update the edition of the `rustfmt` command that checks the solutions. +edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and `CARGO_TOML` in `dev new`. rust-version = "1.85" [workspace.dependencies] diff --git a/src/dev/check.rs b/src/dev/check.rs index aacc2f440f..c89c4eb228 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -313,7 +313,7 @@ fn check_solutions( fmt_cmd .arg("--check") .arg("--edition") - .arg("2021") + .arg("2024") .arg("--color") .arg("always") .stdin(Stdio::null()); From 374c3874afcd01ed0c88047589967d568ceac49f Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 21 Feb 2025 13:08:34 +0100 Subject: [PATCH 1344/1432] Apply 2024 edition formatting to solutions --- solutions/03_if/if1.rs | 6 +----- solutions/11_hashmaps/hashmaps3.rs | 8 +++++--- solutions/16_lifetimes/lifetimes1.rs | 6 +----- solutions/16_lifetimes/lifetimes2.rs | 6 +----- solutions/quizzes/quiz2.rs | 2 +- 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/solutions/03_if/if1.rs b/solutions/03_if/if1.rs index 079c671584..8512a60ff1 100644 --- a/solutions/03_if/if1.rs +++ b/solutions/03_if/if1.rs @@ -1,9 +1,5 @@ fn bigger(a: i32, b: i32) -> i32 { - if a > b { - a - } else { - b - } + if a > b { a } else { b } } fn main() { diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs index 433b16c331..485bf83034 100644 --- a/solutions/11_hashmaps/hashmaps3.rs +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -60,9 +60,11 @@ England,Spain,1,0"; fn build_scores() { let scores = build_scores_table(RESULTS); - assert!(["England", "France", "Germany", "Italy", "Poland", "Spain"] - .into_iter() - .all(|team_name| scores.contains_key(team_name))); + assert!( + ["England", "France", "Germany", "Italy", "Poland", "Spain"] + .into_iter() + .all(|team_name| scores.contains_key(team_name)) + ); } #[test] diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs index ca7b688da5..4f56834f9d 100644 --- a/solutions/16_lifetimes/lifetimes1.rs +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -5,11 +5,7 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { // ^^^^ ^^ ^^ ^^ - if x.len() > y.len() { - x - } else { - y - } + if x.len() > y.len() { x } else { y } } fn main() { diff --git a/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs index b0f2ef1fa3..3ca49093e8 100644 --- a/solutions/16_lifetimes/lifetimes2.rs +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -1,9 +1,5 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { - if x.len() > y.len() { - x - } else { - y - } + if x.len() > y.len() { x } else { y } } fn main() { diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs index 58cbe4e2a6..8b073b1867 100644 --- a/solutions/quizzes/quiz2.rs +++ b/solutions/quizzes/quiz2.rs @@ -62,8 +62,8 @@ mod tests { // Import `transformer`. use super::my_module::transformer; - use super::my_module::transformer_iter; use super::Command; + use super::my_module::transformer_iter; #[test] fn it_works() { From 46c6fb2c82d632a9b635ce74d4ef4292e3e0e90f Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 25 Feb 2025 11:21:19 +0100 Subject: [PATCH 1345/1432] Update deps --- Cargo.lock | 46 +++++++++++++++++++++++----------------------- Cargo.toml | 8 ++++---- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c340b645f..3eaf2dc82f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "anstream" @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "autocfg" @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libredox" @@ -317,9 +317,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -492,18 +492,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -512,9 +512,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -622,9 +622,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "utf8parse" @@ -772,9 +772,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index a3b1d0d734..693d9fde35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and rust-version = "1.85" [workspace.dependencies] -serde = { version = "1.0.217", features = ["derive"] } +serde = { version = "1.0.218", features = ["derive"] } toml_edit = { version = "0.22.24", default-features = false, features = ["parse", "serde"] } [package] @@ -46,13 +46,13 @@ include = [ ] [dependencies] -anyhow = "1.0.95" -clap = { version = "4.5.30", features = ["derive"] } +anyhow = "1.0.96" +clap = { version = "4.5.31", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = "8.0.0" os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } -serde_json = "1.0.138" +serde_json = "1.0.139" serde.workspace = true toml_edit.workspace = true From 425c9821e0ef2d69f5b59750a8fc444165d64689 Mon Sep 17 00:00:00 2001 From: Peter Neave Date: Fri, 28 Feb 2025 11:46:39 +1100 Subject: [PATCH 1346/1432] Use consistent apostrophes in markdown files --- exercises/01_variables/README.md | 2 +- exercises/08_enums/README.md | 2 +- exercises/13_error_handling/README.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exercises/01_variables/README.md b/exercises/01_variables/README.md index 7964ff29f4..5ba2efcaa2 100644 --- a/exercises/01_variables/README.md +++ b/exercises/01_variables/README.md @@ -1,7 +1,7 @@ # Variables In Rust, variables are immutable by default. -When a variable is immutable, once a value is bound to a name, you can’t change that value. +When a variable is immutable, once a value is bound to a name, you can't change that value. You can make them mutable by adding `mut` in front of the variable name. ## Further information diff --git a/exercises/08_enums/README.md b/exercises/08_enums/README.md index 30d4d91db2..2ca95e6c20 100644 --- a/exercises/08_enums/README.md +++ b/exercises/08_enums/README.md @@ -1,7 +1,7 @@ # Enums Rust allows you to define types called "enums" which enumerate possible values. -Enums are a feature in many languages, but their capabilities differ in each language. Rust’s enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell. +Enums are a feature in many languages, but their capabilities differ in each language. Rust's enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell. Useful in combination with enums is Rust's "pattern matching" facility, which makes it easy to run different code for different values of an enumeration. ## Further information diff --git a/exercises/13_error_handling/README.md b/exercises/13_error_handling/README.md index 3b21f2b782..9b6674bc7a 100644 --- a/exercises/13_error_handling/README.md +++ b/exercises/13_error_handling/README.md @@ -1,8 +1,8 @@ # Error handling -Most errors aren’t serious enough to require the program to stop entirely. -Sometimes, when a function fails, it’s for a reason that you can easily interpret and respond to. -For example, if you try to open a file and that operation fails because the file doesn’t exist, you might want to create the file instead of terminating the process. +Most errors aren't serious enough to require the program to stop entirely. +Sometimes, when a function fails, it's for a reason that you can easily interpret and respond to. +For example, if you try to open a file and that operation fails because the file doesn't exist, you might want to create the file instead of terminating the process. ## Further information From fcd77a83ccb557d68c3224c8d705889f891a06de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 7 Mar 2025 19:17:11 -0400 Subject: [PATCH 1347/1432] test trim idempotence --- exercises/09_strings/strings3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index 39fce18c39..f5e45b0ffc 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -23,6 +23,7 @@ mod tests { assert_eq!(trim_me("Hello! "), "Hello!"); assert_eq!(trim_me(" What's up!"), "What's up!"); assert_eq!(trim_me(" Hola! "), "Hola!"); + assert_eq!(trim_me("Hi!"), "Hi!"); } #[test] From 7019f4d1783e9b20ff6c9225a173490985d2658e Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 14 Mar 2025 11:33:37 +0100 Subject: [PATCH 1348/1432] Update pipeline --- .github/workflows/rust.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 80f052d611..9a5d8693bd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,22 +22,22 @@ jobs: - uses: DavidAnson/markdownlint-cli2-action@v16 with: globs: "exercises/**/*.md" - - name: Run cargo fmt + - name: rustfmt run: cargo fmt --all --check test: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - uses: actions/checkout@v4 - uses: swatinem/rust-cache@v2 - - name: Run cargo test + - name: cargo test run: cargo test --workspace dev-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: swatinem/rust-cache@v2 - - name: Run rustlings dev check + - name: rustlings dev check run: cargo run -- dev check --require-solutions From 8db85946af22ccbcbcca1432acfb73036bd2a099 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 14 Mar 2025 11:33:45 +0100 Subject: [PATCH 1349/1432] Update deps --- Cargo.lock | 122 ++++++++++++++++++++++++++++++----------------------- Cargo.toml | 12 +++--- 2 files changed, 76 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3eaf2dc82f..f5b19d7bc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "autocfg" @@ -72,9 +72,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "cfg-if" @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -134,11 +134,11 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "crossterm_winapi", "mio", "parking_lot", - "rustix", + "rustix 0.38.44", "signal-hook", "signal-hook-mio", "winapi", @@ -222,9 +222,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -236,7 +236,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "inotify-sys", "libc", ] @@ -258,9 +258,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "kqueue" @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libredox" @@ -294,7 +294,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", "redox_syscall", ] @@ -305,6 +305,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" + [[package]] name = "lock_api" version = "0.4.12" @@ -345,7 +351,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "filetime", "fsevent-sys", "inotify", @@ -366,9 +372,9 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "os_pipe" @@ -405,29 +411,29 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -436,10 +442,23 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +dependencies = [ + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.9.2", "windows-sys 0.59.0", ] @@ -452,7 +471,7 @@ dependencies = [ "crossterm", "notify", "os_pipe", - "rustix", + "rustix 1.0.2", "rustlings-macros", "serde", "serde_json", @@ -471,9 +490,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -492,18 +511,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -512,9 +531,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -575,9 +594,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.98" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -586,15 +605,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.17.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" dependencies = [ - "cfg-if", "fastrand", "getrandom", "once_cell", - "rustix", + "rustix 1.0.2", "windows-sys 0.59.0", ] @@ -622,9 +640,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "utf8parse" @@ -772,9 +790,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] @@ -785,5 +803,5 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] diff --git a/Cargo.toml b/Cargo.toml index 693d9fde35..c7c277cec8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and rust-version = "1.85" [workspace.dependencies] -serde = { version = "1.0.218", features = ["derive"] } +serde = { version = "1.0.219", features = ["derive"] } toml_edit = { version = "0.22.24", default-features = false, features = ["parse", "serde"] } [package] @@ -46,21 +46,21 @@ include = [ ] [dependencies] -anyhow = "1.0.96" -clap = { version = "4.5.31", features = ["derive"] } +anyhow = "1.0.97" +clap = { version = "4.5.32", features = ["derive"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } notify = "8.0.0" os_pipe = "1.2.1" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } -serde_json = "1.0.139" +serde_json = "1.0.140" serde.workspace = true toml_edit.workspace = true [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.38.44", default-features = false, features = ["std", "stdio", "termios"] } +rustix = { version = "1.0.2", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.17.1" +tempfile = "3.19.0" [profile.release] panic = "abort" From 7c0d269279aaed64d6b8240ed5c1e9f6a981181e Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 14 Mar 2025 11:42:16 +0100 Subject: [PATCH 1350/1432] Update README --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3118451f80..3e6d6c7ca3 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,13 @@ Before installing Rustlings, you need to have the **latest version of Rust** ins Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. This will also install _Cargo_, Rust's package/project manager. -> 🐧 If you're on Linux, make sure you've installed `gcc` (for a linker). +> 🐧 If you are on Linux, make sure you have installed `gcc` (for a linker). > -> Deb: `sudo apt install gcc`. -> Dnf: `sudo dnf install gcc`. +> Deb: `sudo apt install gcc` +> +> Dnf: `sudo dnf install gcc` -> 🍎 If you're on MacOS, make sure you've installed Xcode and its developer tools by running `xcode-select --install`. +> 🍎 If you are on MacOS, make sure you have installed Xcode and its developer tools by running `xcode-select --install`. ### Installing Rustlings @@ -102,7 +103,7 @@ Ask for hints by entering `h` in the _watch mode_ πŸ’‘ ### Watch Mode -After [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`. +After the [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`. This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers). It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory. @@ -161,6 +162,4 @@ cargo uninstall rustlings See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md) πŸ”— -## Contributors ✨ - -Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) πŸŽ‰ +Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) ✨ From d2abc359cc10c42a94aa680294eeaca2d7c502b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= Date: Mon, 17 Mar 2025 18:36:13 +0100 Subject: [PATCH 1351/1432] Remove TODO from 2 solutions --- solutions/06_move_semantics/move_semantics4.rs | 2 -- solutions/19_smart_pointers/rc1.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs index 64fdd9dbde..1a39d4fc8a 100644 --- a/solutions/06_move_semantics/move_semantics4.rs +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -4,8 +4,6 @@ fn main() { #[cfg(test)] mod tests { - // TODO: Fix the compiler errors only by reordering the lines in the test. - // Don't add, change or remove any line. #[test] fn move_semantics4() { let mut x = Vec::new(); diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs index c0a41abfa8..edf40ebe11 100644 --- a/solutions/19_smart_pointers/rc1.rs +++ b/solutions/19_smart_pointers/rc1.rs @@ -63,12 +63,10 @@ mod tests { println!("reference count = {}", Rc::strong_count(&sun)); // 7 references saturn.details(); - // TODO let uranus = Planet::Uranus(Rc::clone(&sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 8 references uranus.details(); - // TODO let neptune = Planet::Neptune(Rc::clone(&sun)); println!("reference count = {}", Rc::strong_count(&sun)); // 9 references neptune.details(); From 3cc7e0377c71080708281c2d8661d20f00a54df6 Mon Sep 17 00:00:00 2001 From: cassian-goode Date: Tue, 25 Mar 2025 09:24:49 -0400 Subject: [PATCH 1352/1432] Fix typo - errors5.rs Minor typo correction in exercise instructions --- exercises/13_error_handling/errors5.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 5721835102..125779b867 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -6,7 +6,7 @@ // // In short, this particular use case for boxes is for when you want to own a // value and you care only that it is a type which implements a particular -// trait. To do so, The `Box` is declared as of type `Box` where +// trait. To do so, the `Box` is declared as of type `Box` where // `Trait` is the trait the compiler looks for on any value used in that // context. For this exercise, that context is the potential errors which // can be returned in a `Result`. From 9978c17d5fc651ac61a4526d717c7f645bc932f8 Mon Sep 17 00:00:00 2001 From: Hunter Z Date: Mon, 31 Mar 2025 12:58:06 +0800 Subject: [PATCH 1353/1432] Update README.md Update the URL while add more reference. --- exercises/21_macros/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/21_macros/README.md b/exercises/21_macros/README.md index 337816d6e6..724c844104 100644 --- a/exercises/21_macros/README.md +++ b/exercises/21_macros/README.md @@ -10,5 +10,6 @@ of exercises to Rustlings, but is all about learning to write Macros. ## Further information -- [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html) +- [The Rust Book - Macros](https://doc.rust-lang.org/book/ch20-06-macros.html#macros) - [The Little Book of Rust Macros](https://veykril.github.io/tlborm/) +- [Rust by Example - macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html) From ecaecc2f76b67d6300dfc789bb4c812d93e1973b Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 3 Apr 2025 17:58:27 +0200 Subject: [PATCH 1354/1432] Update deps --- Cargo.lock | 56 ++++++++++++++++++++----------------- Cargo.toml | 20 ++++++------- rustlings-macros/Cargo.toml | 2 +- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5b19d7bc0..d534f8e0ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.32" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -198,14 +198,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -307,9 +307,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "lock_api" @@ -323,9 +323,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" @@ -372,9 +372,9 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "os_pipe" @@ -427,6 +427,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "redox_syscall" version = "0.5.10" @@ -451,14 +457,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.2", + "linux-raw-sys 0.9.3", "windows-sys 0.59.0", ] @@ -471,7 +477,7 @@ dependencies = [ "crossterm", "notify", "os_pipe", - "rustix 1.0.2", + "rustix 1.0.5", "rustlings-macros", "serde", "serde_json", @@ -605,14 +611,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.0.2", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -668,9 +674,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -799,9 +805,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.9.0", ] diff --git a/Cargo.toml b/Cargo.toml index c7c277cec8..9d9a0f948a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,8 @@ edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and rust-version = "1.85" [workspace.dependencies] -serde = { version = "1.0.219", features = ["derive"] } -toml_edit = { version = "0.22.24", default-features = false, features = ["parse", "serde"] } +serde = { version = "1.0", features = ["derive"] } +toml_edit = { version = "0.22", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -46,21 +46,21 @@ include = [ ] [dependencies] -anyhow = "1.0.97" -clap = { version = "4.5.32", features = ["derive"] } -crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } -notify = "8.0.0" -os_pipe = "1.2.1" +anyhow = "1.0" +clap = { version = "4.5", features = ["derive"] } +crossterm = { version = "0.28", default-features = false, features = ["windows", "events"] } +notify = "8.0" +os_pipe = "1.2" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } -serde_json = "1.0.140" +serde_json = "1.0" serde.workspace = true toml_edit.workspace = true [target.'cfg(not(windows))'.dependencies] -rustix = { version = "1.0.2", default-features = false, features = ["std", "stdio", "termios"] } +rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.19.0" +tempfile = "3.19" [profile.release] panic = "abort" diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 3ed56a1878..1bf6d1b8c7 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -16,7 +16,7 @@ include = [ proc-macro = true [dependencies] -quote = "1.0.37" +quote = "1.0" serde.workspace = true toml_edit.workspace = true From 63d8986f2a6bc97496065d3bac495a77016fd42f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 3 Apr 2025 18:22:55 +0200 Subject: [PATCH 1355/1432] Update links --- exercises/08_enums/README.md | 2 +- exercises/21_macros/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/08_enums/README.md b/exercises/08_enums/README.md index 2ca95e6c20..b05cb42260 100644 --- a/exercises/08_enums/README.md +++ b/exercises/08_enums/README.md @@ -7,4 +7,4 @@ Useful in combination with enums is Rust's "pattern matching" facility, which ma ## Further information - [Enums](https://doc.rust-lang.org/book/ch06-00-enums.html) -- [Pattern syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html) +- [Pattern syntax](https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html) diff --git a/exercises/21_macros/README.md b/exercises/21_macros/README.md index 724c844104..de7fb7ba3b 100644 --- a/exercises/21_macros/README.md +++ b/exercises/21_macros/README.md @@ -10,6 +10,6 @@ of exercises to Rustlings, but is all about learning to write Macros. ## Further information -- [The Rust Book - Macros](https://doc.rust-lang.org/book/ch20-06-macros.html#macros) +- [The Rust Book - Macros](https://doc.rust-lang.org/book/ch20-05-macros.html) - [The Little Book of Rust Macros](https://veykril.github.io/tlborm/) - [Rust by Example - macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html) From bd3bdd620bce981f648a1c5fcc3efe0a80cf9911 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 8 Apr 2025 20:23:11 -0500 Subject: [PATCH 1356/1432] Fix typo in traits hint --- rustlings-macros/info.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index e705598187..f46754d4e1 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -763,7 +763,7 @@ hint = """ Notice how the trait takes ownership of `self` and returns `Self`. Although the signature of `append_bar` in the trait takes `self` as argument, -the implementation can take `mut self` instead. This is possible because the +the implementation can take `mut self` instead. This is possible because the value is owned anyway.""" [[exercises]] From 7b2d42b0f01a859c0cc0e28260a01769759b1362 Mon Sep 17 00:00:00 2001 From: Rahmat Nazali Salimi Date: Thu, 10 Apr 2025 15:38:43 +0700 Subject: [PATCH 1357/1432] Change `icecream` to `ice cream` --- exercises/12_options/options1.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index 9964807859..d0c412a8d4 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -1,8 +1,8 @@ -// This function returns how much icecream there is left in the fridge. +// This function returns how much ice cream there is left in the fridge. // If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, -// someone eats it all, so no icecream is left (value 0). Return `None` if +// someone eats it all, so no ice cream is left (value 0). Return `None` if // `hour_of_day` is higher than 23. -fn maybe_icecream(hour_of_day: u16) -> Option { +fn maybe_ice_cream(hour_of_day: u16) -> Option { // TODO: Complete the function body. } @@ -18,19 +18,19 @@ mod tests { fn raw_value() { // TODO: Fix this test. How do you get the value contained in the // Option? - let icecreams = maybe_icecream(12); + let ice_creams = maybe_ice_cream(12); - assert_eq!(icecreams, 5); // Don't change this line. + assert_eq!(ice_creams, 5); // Don't change this line. } #[test] - fn check_icecream() { - assert_eq!(maybe_icecream(0), Some(5)); - assert_eq!(maybe_icecream(9), Some(5)); - assert_eq!(maybe_icecream(18), Some(5)); - assert_eq!(maybe_icecream(22), Some(0)); - assert_eq!(maybe_icecream(23), Some(0)); - assert_eq!(maybe_icecream(24), None); - assert_eq!(maybe_icecream(25), None); + fn check_ice_cream() { + assert_eq!(maybe_ice_cream(0), Some(5)); + assert_eq!(maybe_ice_cream(9), Some(5)); + assert_eq!(maybe_ice_cream(18), Some(5)); + assert_eq!(maybe_ice_cream(22), Some(0)); + assert_eq!(maybe_ice_cream(23), Some(0)); + assert_eq!(maybe_ice_cream(24), None); + assert_eq!(maybe_ice_cream(25), None); } } From 29dc8ea9fa7b081201ebcec5eb01c588082b459d Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 29 Apr 2025 21:35:55 +0200 Subject: [PATCH 1358/1432] Update deps --- Cargo.lock | 101 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d534f8e0ba..a514a9e671 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "autocfg" @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.35" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -130,15 +130,16 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "crossterm" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ "bitflags 2.9.0", "crossterm_winapi", + "document-features", "mio", "parking_lot", - "rustix 0.38.44", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -153,6 +154,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -161,9 +171,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -222,9 +232,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -284,9 +294,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libredox" @@ -301,15 +311,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.15" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] -name = "linux-raw-sys" -version = "0.9.3" +name = "litrs" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" @@ -411,9 +421,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -435,26 +445,13 @@ checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags 2.9.0", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.0.5" @@ -464,7 +461,7 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys", "windows-sys 0.59.0", ] @@ -477,7 +474,7 @@ dependencies = [ "crossterm", "notify", "os_pipe", - "rustix 1.0.5", + "rustix", "rustlings-macros", "serde", "serde_json", @@ -579,18 +576,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "strsim" @@ -600,9 +597,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -618,24 +615,24 @@ dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.0.5", + "rustix", "windows-sys 0.59.0", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", @@ -796,9 +793,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 9d9a0f948a..34a4791a2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ include = [ [dependencies] anyhow = "1.0" clap = { version = "4.5", features = ["derive"] } -crossterm = { version = "0.28", default-features = false, features = ["windows", "events"] } +crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] } notify = "8.0" os_pipe = "1.2" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } From 9bcd4198c5bcfdb85ba8b493e0c75ab6750a9325 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 29 Apr 2025 21:36:56 +0200 Subject: [PATCH 1359/1432] Fix formatting --- rustlings-macros/info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index f46754d4e1..516fd321fe 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -763,8 +763,8 @@ hint = """ Notice how the trait takes ownership of `self` and returns `Self`. Although the signature of `append_bar` in the trait takes `self` as argument, -the implementation can take `mut self` instead. This is possible because -the value is owned anyway.""" +the implementation can take `mut self` instead. This is possible because the +value is owned anyway.""" [[exercises]] name = "traits3" From c5f49cfa48500ff71893070578206903f58ed2cc Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 May 2025 20:30:51 +0200 Subject: [PATCH 1360/1432] Remove needless_option_as_deref exception --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 34a4791a2a..dbab70a91d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,8 +84,6 @@ infinite_loop = "deny" mem_forget = "deny" dbg_macro = "warn" todo = "warn" -# TODO: Remove after the following fix is released: https://github.com/rust-lang/rust-clippy/pull/13102 -needless_option_as_deref = "allow" [lints] workspace = true From a063bcfb4cf5e7e6cb98a8738a73aee8b7a57c7a Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 May 2025 20:30:56 +0200 Subject: [PATCH 1361/1432] Update deps --- Cargo.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a514a9e671..707c921d45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "heck" @@ -274,9 +274,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "kqueue" -version = "1.0.8" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" dependencies = [ "kqueue-sys", "libc", @@ -445,18 +445,18 @@ checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.0", ] [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags 2.9.0", "errno", @@ -555,9 +555,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -608,9 +608,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom", @@ -793,9 +793,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] From 48bab776096cde6ddd6547dfdb7d879a49c3edfe Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 May 2025 20:31:13 +0200 Subject: [PATCH 1362/1432] Apply Clippy lints --- exercises/01_variables/variables5.rs | 2 +- solutions/01_variables/variables5.rs | 2 +- solutions/22_clippy/clippy3.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index 49db8e9ed2..cf5620da5f 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -1,6 +1,6 @@ fn main() { let number = "T-H-R-E-E"; // Don't change this line - println!("Spell a number: {}", number); + println!("Spell a number: {number}"); // TODO: Fix the compiler error by changing the line below without renaming the variable. number = 3; diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs index 9057754ce4..0ea3903013 100644 --- a/solutions/01_variables/variables5.rs +++ b/solutions/01_variables/variables5.rs @@ -1,6 +1,6 @@ fn main() { let number = "T-H-R-E-E"; - println!("Spell a number: {}", number); + println!("Spell a number: {number}"); // Using variable shadowing // https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs index b7eaa57079..81f381e02a 100644 --- a/solutions/22_clippy/clippy3.rs +++ b/solutions/22_clippy/clippy3.rs @@ -15,7 +15,7 @@ fn main() { -1, -2, -3, -4, -5, -6, ]; - println!("My array! Here it is: {:?}", my_arr); + println!("My array! Here it is: {my_arr:?}"); let mut my_empty_vec = vec![1, 2, 3, 4, 5]; // `resize` mutates a vector instead of returning a new one. @@ -27,5 +27,5 @@ fn main() { let mut value_b = 66; // Use `mem::swap` to correctly swap two values. mem::swap(&mut value_a, &mut value_b); - println!("value a: {}; value b: {}", value_a, value_b); + println!("value a: {value_a}; value b: {value_b}"); } From e76ca5e2b97f0b7b292bbe504debb42ec7b36a87 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 12 May 2025 20:38:04 +0200 Subject: [PATCH 1363/1432] Use a separate target dir for rust analyzer --- src/init.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/init.rs b/src/init.rs index 208425c2a3..a60fba70df 100644 --- a/src/init.rs +++ b/src/init.rs @@ -178,6 +178,7 @@ const INIT_SOLUTION_FILE: &[u8] = b"fn main() { pub const RUST_ANALYZER_TOML: &[u8] = br#"check.command = "clippy" check.extraArgs = ["--profile", "test"] +cargo.targetDir = true "#; const GITIGNORE: &[u8] = b"Cargo.lock From 9a3586878d73cede3258ce076c609324cef8cc07 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 13 May 2025 16:24:42 +0200 Subject: [PATCH 1364/1432] Sync solution --- solutions/09_strings/strings3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/solutions/09_strings/strings3.rs b/solutions/09_strings/strings3.rs index a478e62ad0..ee6b56afe1 100644 --- a/solutions/09_strings/strings3.rs +++ b/solutions/09_strings/strings3.rs @@ -26,6 +26,7 @@ mod tests { assert_eq!(trim_me("Hello! "), "Hello!"); assert_eq!(trim_me(" What's up!"), "What's up!"); assert_eq!(trim_me(" Hola! "), "Hola!"); + assert_eq!(trim_me("Hi!"), "Hi!"); } #[test] From 5ee7dfb5c22ab0ac9037c86bc9320c944010038b Mon Sep 17 00:00:00 2001 From: liv Date: Fri, 16 May 2025 11:05:02 +0200 Subject: [PATCH 1365/1432] chore: build site with proper path prefix --- oranda.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/oranda.json b/oranda.json index ecc490b20f..626e496885 100644 --- a/oranda.json +++ b/oranda.json @@ -1,4 +1,7 @@ { + "build": { + "path_prefix": "rustlings" + }, "project": { "homepage": "https://rustlings.cool", "repository": "https://github.com/rust-lang/rustlings" From 8dff0df2667d3c6f1812bdb390c708709b762847 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 May 2025 10:50:07 +0200 Subject: [PATCH 1366/1432] Use std pipe --- CHANGELOG.md | 2 +- Cargo.lock | 33 +++++++++++---------------------- Cargo.toml | 3 +-- src/cmd.rs | 4 ++-- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9826cf002..70529775c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### Changed - Upgrade to Rust edition 2024 -- Raise the minimum supported Rust version to `1.85` +- Raise the minimum supported Rust version to `1.87` diff --git a/Cargo.lock b/Cargo.lock index 707c921d45..c285a52a79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,9 +72,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cfg-if" @@ -134,7 +134,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "crossterm_winapi", "document-features", "mio", @@ -171,9 +171,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -246,7 +246,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "inotify-sys", "libc", ] @@ -304,7 +304,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "redox_syscall", ] @@ -361,7 +361,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "filetime", "fsevent-sys", "inotify", @@ -386,16 +386,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "os_pipe" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -449,7 +439,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -458,7 +448,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -473,7 +463,6 @@ dependencies = [ "clap", "crossterm", "notify", - "os_pipe", "rustix", "rustlings-macros", "serde", @@ -806,5 +795,5 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] diff --git a/Cargo.toml b/Cargo.toml index dbab70a91d..27531b37bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ authors = [ repository = "https://github.com/rust-lang/rustlings" license = "MIT" edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and `CARGO_TOML` in `dev new`. -rust-version = "1.85" +rust-version = "1.87" [workspace.dependencies] serde = { version = "1.0", features = ["derive"] } @@ -50,7 +50,6 @@ anyhow = "1.0" clap = { version = "4.5", features = ["derive"] } crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] } notify = "8.0" -os_pipe = "1.2" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } serde_json = "1.0" serde.workspace = true diff --git a/src/cmd.rs b/src/cmd.rs index 551df8f0ae..b2c58f6abb 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result, bail}; use serde::Deserialize; use std::{ - io::Read, + io::{Read, pipe}, path::PathBuf, process::{Command, Stdio}, }; @@ -17,7 +17,7 @@ fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec>) -> }; let mut handle = if let Some(output) = output { - let (mut reader, writer) = os_pipe::pipe().with_context(|| { + let (mut reader, writer) = pipe().with_context(|| { format!("Failed to create a pipe to run the command `{description}``") })?; From e73fff3bd4d6c55a2b741415ef02b9e945ef3b42 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 May 2025 11:08:25 +0200 Subject: [PATCH 1367/1432] Add dev alias --- .cargo/config.toml | 2 ++ dev/Cargo.toml | 2 +- src/dev/check.rs | 4 ++-- src/dev/update.rs | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000000..b8fa3f7745 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +dev = ["run", "--", "dev"] diff --git a/dev/Cargo.toml b/dev/Cargo.toml index ae380d1751..4f725b704b 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,4 +1,4 @@ -# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`. This comment line will be stripped in `rustlings init`. +# Don't edit the `bin` list manually! It is updated by `cargo dev update`. This comment line will be stripped in `rustlings init`. bin = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, { name = "intro1_sol", path = "../solutions/00_intro/intro1.rs" }, diff --git a/src/dev/check.rs b/src/dev/check.rs index c89c4eb228..9cde7f24cc 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -43,7 +43,7 @@ fn check_cargo_toml( if old_bins != new_bins { if cfg!(debug_assertions) { bail!( - "The file `dev/Cargo.toml` is outdated. Run `cargo run -- dev update` to update it. Then run `cargo run -- dev check` again" + "The file `dev/Cargo.toml` is outdated. Run `cargo dev update` to update it. Then run `cargo run -- dev check` again" ); } @@ -379,7 +379,7 @@ pub fn check(require_solutions: bool) -> Result<()> { } if cfg!(debug_assertions) { - // A hack to make `cargo run -- dev check` work when developing Rustlings. + // A hack to make `cargo dev check` work when developing Rustlings. check_cargo_toml(&info_file.exercises, "dev/Cargo.toml", b"../")?; } else { check_cargo_toml(&info_file.exercises, "Cargo.toml", b"")?; diff --git a/src/dev/update.rs b/src/dev/update.rs index 6de3c8f79b..e0855a0e93 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -28,7 +28,7 @@ pub fn update() -> Result<()> { let info_file = InfoFile::parse()?; if cfg!(debug_assertions) { - // A hack to make `cargo run -- dev update` work when developing Rustlings. + // A hack to make `cargo dev update` work when developing Rustlings. update_cargo_toml(&info_file.exercises, "dev/Cargo.toml", b"../") .context("Failed to update the file `dev/Cargo.toml`")?; From 5927a781a31f496223721f33ea19460daa3f70ab Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 May 2025 11:29:32 +0200 Subject: [PATCH 1368/1432] Remove Oranda --- .github/workflows/web.yml | 87 ------------------------------------- .gitignore | 4 -- CHANGELOG.md | 90 --------------------------------------- README.md | 4 -- oranda.json | 16 ------- 5 files changed, 201 deletions(-) delete mode 100644 .github/workflows/web.yml delete mode 100644 oranda.json diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml deleted file mode 100644 index ec5d446221..0000000000 --- a/.github/workflows/web.yml +++ /dev/null @@ -1,87 +0,0 @@ -# Workflow to build your docs with oranda (and mdbook) -# and deploy them to Github Pages -name: Web - -# We're going to push to the gh-pages branch, so we need that permission -permissions: - contents: write - -# What situations do we want to build docs in? -# All of these work independently and can be removed / commented out -# if you don't want oranda/mdbook running in that situation -on: - # Check that a PR didn't break docs! - # - # Note that the "Deploy to Github Pages" step won't run in this mode, - # so this won't have any side-effects. But it will tell you if a PR - # completely broke oranda/mdbook. Sadly we don't provide previews (yet)! - pull_request: - - # Whenever something gets pushed to main, update the docs! - # This is great for getting docs changes live without cutting a full release. - # - # Note that if you're using cargo-dist, this will "race" the Release workflow - # that actually builds the Github Release that oranda tries to read (and - # this will almost certainly complete first). As a result you will publish - # docs for the latest commit but the oranda landing page won't know about - # the latest release. The workflow_run trigger below will properly wait for - # cargo-dist, and so this half-published state will only last for ~10 minutes. - # - # If you only want docs to update with releases, disable this, or change it to - # a "release" branch. You can, of course, also manually trigger a workflow run - # when you want the docs to update. - push: - branches: - - main - - # Whenever a workflow called "Release" completes, update the docs! - # - # If you're using cargo-dist, this is recommended, as it will ensure that - # oranda always sees the latest release right when it's available. Note - # however that Github's UI is wonky when you use workflow_run, and won't - # show this workflow as part of any commit. You have to go to the "actions" - # tab for your repo to see this one running (the gh-pages deploy will also - # only show up there). - workflow_run: - workflows: [ "Release" ] - types: - - completed - -# Alright, let's do it! -jobs: - web: - name: Build and deploy site and docs - runs-on: ubuntu-latest - steps: - # Setup - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: swatinem/rust-cache@v2 - - # If you use any mdbook plugins, here's the place to install them! - - # Install and run oranda (and mdbook) - # This will write all output to ./public/ (including copying mdbook's output to there) - - name: Install and run oranda - run: | - curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/download/v0.3.1/oranda-installer.sh | sh - oranda build - - # Deploy to our gh-pages branch (creating it if it doesn't exist) - # the "public" dir that oranda made above will become the root dir - # of this branch. - # - # Note that once the gh-pages branch exists, you must - # go into repo's settings > pages and set "deploy from branch: gh-pages" - # the other defaults work fine. - - name: Deploy to Github Pages - uses: JamesIves/github-pages-deploy-action@v4.4.1 - # ONLY if we're on main (so no PRs or feature branches allowed!) - if: ${{ github.ref == 'refs/heads/main' }} - with: - branch: gh-pages - # Gotta tell the action where to find oranda's output - folder: public - token: ${{ secrets.GITHUB_TOKEN }} - single-commit: true diff --git a/.gitignore b/.gitignore index 945382c37d..ea65eb1ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,6 @@ Cargo.lock # State file .rustlings-state.txt -# oranda -public/ -.netlify - # OS .DS_Store .direnv/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 70529775c0..6781ba89ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,6 @@ - Upgrade to Rust edition 2024 - Raise the minimum supported Rust version to `1.87` - - ## 6.4.0 (2024-11-11) ### Added @@ -36,8 +34,6 @@ - Fix bad contrast in the list on terminals with a light theme. - - ## 6.3.0 (2024-08-29) ### Added @@ -77,8 +73,6 @@ - Fix the list when the terminal height is too low. - Restore the terminal after an error in the list. - - ## 6.2.0 (2024-08-09) ### Added @@ -95,8 +89,6 @@ - Run the final check of all exercises in parallel. - Small exercise improvements. - - ## 6.1.0 (2024-07-10) #### Added @@ -114,15 +106,11 @@ - Exit with a helpful error message on missing/unsupported terminal/TTY. - Mark the last exercise as done. - - ## 6.0.1 (2024-07-04) Small exercise improvements and fixes. Most importantly, fixed that the exercise `clippy1` was already solved πŸ˜… - - ## 6.0.0 (2024-07-03) This release is the result of a complete rewrite to deliver a ton of new features and improvements ✨ @@ -188,8 +176,6 @@ Do you want to create your own set of Rustlings exercises to focus on some speci Or do you want to translate the original Rustlings exercises? Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! - - ## 5.6.1 (2023-09-18) #### Changed @@ -205,8 +191,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - `as_ref_mut`: Fixed a typo in a test function name. - `enums3`: Fixed formatting with `rustfmt`. - - ## 5.6.0 (2023-09-04) #### Added @@ -246,16 +230,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Lots of Nix housekeeping that I don't feel qualified to write about! - Improved CI workflows, we're now testing on multiple platforms at once. - - ## 5.5.1 (2023-05-17) #### Fixed - Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix. - - ## 5.5.0 (2023-05-17) #### Added @@ -290,8 +270,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Added a markdown linter to run on GitHub actions - Split quick installation section into two code blocks - - ## 5.4.1 (2023-03-10) #### Changed @@ -307,8 +285,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - `macros4`: Prevented auto-fix by adding `#[rustfmt::skip]` - `cli`: Actually show correct progress percentages - - ## 5.4.0 (2023-02-12) #### Changed @@ -337,8 +313,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Bumped min Rust version to 1.58 in installation script - - ## 5.3.0 (2022-12-23) #### Added @@ -371,8 +345,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Applied some Clippy and rustfmt formatting - Added a note on Windows PowerShell and other shell compatibility - - ## 5.2.1 (2022-09-06) #### Fixed @@ -386,8 +358,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Fixed a typo in README.md - - ## 5.2.0 (2022-08-27) #### Added @@ -404,16 +374,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **quiz1**: Adjusted the explanations to be consistent with the tests - - ## 5.1.1 (2022-08-17) #### Bug Fixes - Fixed an incorrect assertion in options1 - - ## 5.1.0 (2022-08-16) #### Features @@ -448,8 +414,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Clarified manual installation instructions using `cargo install --path .` - Added a link to our Zulip in the readme file - - ## 5.0.0 (2022-07-16) #### Features @@ -522,8 +486,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Updated spacing in Cargo.toml. - Added a GitHub actions config so that tests run on every PR/commit. - - ## 4.8.0 (2022-07-01) #### Features @@ -544,8 +506,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Replaced the git.io URL with the fully qualified URL because of git.io's sunsetting. - Removed the deprecated Rust GitPod extension. - - ## 4.7.1 (2022-04-20) #### Features @@ -566,8 +526,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - The changelog will now be manually written instead of being automatically generated by the Git log. - - ## 4.7.0 (2022-04-14) #### Features @@ -608,8 +566,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Add hints on how to get GCC installed (#741) ([bc56861](https://github.com/rust-lang/rustlings/commit/bc5686174463ad6f4f6b824b0e9b97c3039d4886)) - Fix some code blocks that were not highlighted ([17f9d74](https://github.com/rust-lang/rustlings/commit/17f9d7429ccd133a72e815fb5618e0ce79560929)) - - ## 4.6.0 (2021-09-25) #### Features @@ -632,8 +588,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1)) - **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a)) - - ## 4.5.0 (2021-07-07) #### Features @@ -654,8 +608,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e)) - **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5)) - - ## 4.4.0 (2021-04-24) #### Bug Fixes @@ -697,8 +649,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - updated progress percentage ([1c6f7e4b](https://github.com/rust-lang/rustlings/commit/1c6f7e4b7b9b3bd36f4da2bb2b69c549cc8bd913)) - added progress info ([c0e3daac](https://github.com/rust-lang/rustlings/commit/c0e3daacaf6850811df5bc57fa43e0f249d5cfa4)) - - ## 4.3.0 (2020-12-29) #### Features @@ -721,8 +671,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Update description (#584) ([96347df9](https://github.com/rust-lang/rustlings/commit/96347df9df294f01153b29d9ad4ba361f665c755)) - **vec1:** Have test compare every element in a and v ([9b6c6293](https://github.com/rust-lang/rustlings/commit/9b6c629397b24b944f484f5b2bbd8144266b5695)) - - ## 4.2.0 (2020-11-07) #### Features @@ -743,8 +691,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - missing comma in test ([4fb230da](https://github.com/rust-lang/rustlings/commit/4fb230daf1251444fcf29e085cee222a91f8a37e)) - **quiz3:** Second test is for odd numbers, not even. (#553) ([18e0bfef](https://github.com/rust-lang/rustlings/commit/18e0bfef1de53071e353ba1ec5837002ff7290e6)) - - ## 4.1.0 (2020-10-05) #### Bug Fixes @@ -767,8 +713,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **cli:** Added 'cls' command to 'watch' mode (#474) ([4f2468e1](https://github.com/rust-lang/rustlings/commit/4f2468e14f574a93a2e9b688367b5752ed96ae7b)) - **try_from_into:** Add insufficient length test (#469) ([523d18b8](https://github.com/rust-lang/rustlings/commit/523d18b873a319f7c09262f44bd40e2fab1830e5)) - - ## 4.0.0 (2020-07-08) #### Breaking Changes @@ -810,8 +754,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **test2:** name of type String and &str (#394) ([d6c0a688](https://github.com/rust-lang/rustlings/commit/d6c0a688e6a96f93ad60d540d4b326f342fc0d45)) - **variables6:** minor typo (#419) ([524e17df](https://github.com/rust-lang/rustlings/commit/524e17df10db95f7b90a0f75cc8997182a8a4094)) - - ## 3.0.0 (2020-04-11) #### Breaking Changes @@ -834,8 +776,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229)) - **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71)) - - ### 2.2.1 (2020-02-27) #### Bug Fixes @@ -846,8 +786,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Add clippy lints (#269) ([1e2fd9c9](https://github.com/rust-lang/rustlings/commit/1e2fd9c92f8cd6e389525ca1a999fca4c90b5921)) - - ## 2.2.0 (2020-02-25) #### Bug Fixes @@ -875,8 +813,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Added traits exercises (#274 but specifically #216, which originally added this :heart:) ([b559cdd](https://github.com/rust-lang/rustlings/commit/b559cdd73f32c0d0cfc1feda39f82b3e3583df17)) - - ## 2.1.0 (2019-11-27) #### Bug Fixes @@ -894,8 +830,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **watch:** show hint while watching ([8143d57b](https://github.com/rust-lang/rustlings/commit/8143d57b4e88c51341dd4a18a14c536042cc009c)) - - ## 2.0.0 (2019-11-12) #### Bug Fixes @@ -916,8 +850,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **cli:** check for rustc before doing anything ([36a033b8](https://github.com/rust-lang/rustlings/commit/36a033b87a6549c1e5639c908bf7381c84f4f425)) - **hint:** Add test for hint ([ce9fa6eb](https://github.com/rust-lang/rustlings/commit/ce9fa6ebbfdc3e7585d488d9409797285708316f)) - - ### 1.5.1 (2019-11-11) #### Bug Fixes @@ -929,8 +861,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **threads:** Move Threads behind SLT ([fbe91a67](https://github.com/rust-lang/rustlings/commit/fbe91a67a482bfe64cbcdd58d06ba830a0f39da3), closes [#205](https://github.com/rust-lang/rustlings/issues/205)) - **watch:** clear screen before each `verify()` ([3aff590](https://github.com/rust-lang/rustlings/commit/3aff59085586c24196a547c2693adbdcf4432648)) - - ## 1.5.0 (2019-11-09) #### Bug Fixes @@ -955,8 +885,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Added exercise for struct update syntax ([1c4c8764](https://github.com/rust-lang/rustlings/commit/1c4c8764ed118740cd4cee73272ddc6cceb9d959)) - **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031)) - - ### 1.4.1 (2019-08-13) #### Bug Fixes @@ -965,8 +893,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **option1:** Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6)) - **test1:** Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446)) - - ## 1.4.0 (2019-07-13) #### Bug Fixes @@ -983,8 +909,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - **changelog:** Use clog for changelogs ([34e31232](https://github.com/rust-lang/rustlings/commit/34e31232dfddde284a341c9609b33cd27d9d5724)) - **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031)) - - ### 1.3.0 (2019-06-05) #### Features @@ -1000,16 +924,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Fix broken link (#164, @HanKruiger) - Remove highlighting and syntect (#167, @komaeda) - - ### 1.2.2 (2019-05-07) #### Bug Fixes - Reverted `--nocapture` flag since it was causing tests to pass unconditionally - - ### 1.2.1 (2019-04-22) #### Bug Fixes @@ -1017,8 +937,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Fix the `--nocapture` feature (@komaeda) - Provide a nicer error message for when you're in the wrong directory - - ### 1.2.0 (2019-04-22) #### Features @@ -1026,8 +944,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Add errors to exercises that compile without user changes (@yvan-sraka) - Use --nocapture when testing, enabling `println!` when running (@komaeda) - - ### 1.1.1 (2019-04-14) #### Bug fixes @@ -1040,8 +956,6 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Fix links by deleting book version (@diodfr, #142) - Canonicalize paths to fix path matching (@cjpearce, #143) - - ### 1.1.0 (2019-03-20) - errors2.rs: update link to Rust book (#124) @@ -1051,16 +965,12 @@ Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXER - Give a warning when Rustlings isn't run from the right directory (#123) - Verify that rust version is recent enough to install Rustlings (#131) - - ### 1.0.1 (2019-03-06) - Adds a way to install Rustlings in one command (`curl -L https://git.io/rustlings | bash`) - Makes `rustlings watch` react to create file events (@shaunbennett, #117) - Reworks the exercise management to use an external TOML file instead of just listing them in the code - - ### 1.0.0 (2019-03-06) Initial release. diff --git a/README.md b/README.md index 3e6d6c7ca3..8480b1a3d7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ -

- # Rustlings πŸ¦€β€οΈ -
- Greetings and welcome to Rustlings. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! diff --git a/oranda.json b/oranda.json deleted file mode 100644 index 626e496885..0000000000 --- a/oranda.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "build": { - "path_prefix": "rustlings" - }, - "project": { - "homepage": "https://rustlings.cool", - "repository": "https://github.com/rust-lang/rustlings" - }, - "marketing": { - "analytics": { - "plausible": { - "domain": "rustlings.cool" - } - } - } -} From 08548abcc27ede812b69c399aff24b8a8ff9d041 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 May 2025 11:35:24 +0200 Subject: [PATCH 1369/1432] Remove .editorconfig --- .editorconfig | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index aab09aa35d..0000000000 --- a/.editorconfig +++ /dev/null @@ -1,7 +0,0 @@ -root = true - -[*.rs] -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 4 From a28000acc4945bc0865f15964b3605ea9d31cedf Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 May 2025 11:35:46 +0200 Subject: [PATCH 1370/1432] Remove markdown lint --- .github/workflows/rust.yml | 7 ++----- .markdownlint.yml | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 .markdownlint.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9a5d8693bd..2446939933 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,4 +1,4 @@ -name: Rustlings Tests +name: Check on: push: @@ -19,9 +19,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: DavidAnson/markdownlint-cli2-action@v16 - with: - globs: "exercises/**/*.md" - name: rustfmt run: cargo fmt --all --check test: @@ -40,4 +37,4 @@ jobs: - uses: actions/checkout@v4 - uses: swatinem/rust-cache@v2 - name: rustlings dev check - run: cargo run -- dev check --require-solutions + run: cargo dev check --require-solutions diff --git a/.markdownlint.yml b/.markdownlint.yml deleted file mode 100644 index d5f7e3913d..0000000000 --- a/.markdownlint.yml +++ /dev/null @@ -1,2 +0,0 @@ -# MD013/line-length Line length, Expected: 80 -MD013: false From 74ab9924b4193c0fb66f3c0e3667a4d7b4edfb18 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 May 2025 21:08:29 +0200 Subject: [PATCH 1371/1432] Start with Zola --- website/.gitignore | 7 ++++++ website/config.toml | 20 ++++++++++++++++ website/input.css | 54 ++++++++++++++++++++++++++++++++++++++++++++ website/justfile | 5 ++++ website/package.json | 7 ++++++ 5 files changed, 93 insertions(+) create mode 100644 website/.gitignore create mode 100644 website/config.toml create mode 100644 website/input.css create mode 100644 website/justfile create mode 100644 website/package.json diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 0000000000..648e07746e --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,7 @@ +/node_modules/ +/package-lock.json + +/public/ + +/static/main.css +/static/processed_images/ diff --git a/website/config.toml b/website/config.toml new file mode 100644 index 0000000000..d23b3314e9 --- /dev/null +++ b/website/config.toml @@ -0,0 +1,20 @@ +base_url = "https://rust-lang.github.io/rustlings" +title = "Rustlings" +description = "Small exercises to get you used to reading and writing Rust code!" + +compile_sass = false +build_search_index = false + +[markdown] +highlight_code = true + +[[extra.menu_items]] +name = "Home" +url = "/" +[[extra.menu_items]] +name = "Custom Exercises" +url = "/custom-exercises" + +[[extra.footer_items]] +name = "Repository" +url = "https://github.com/rust-lang/rustlings" diff --git a/website/input.css b/website/input.css new file mode 100644 index 0000000000..b26d9ba489 --- /dev/null +++ b/website/input.css @@ -0,0 +1,54 @@ +@import 'tailwindcss'; + +@layer base { + h1 { + @apply text-4xl mt-3 mb-3 text-gray-50 font-bold; + } + h2 { + @apply text-3xl mt-4 mb-1.5 text-gray-50 font-bold; + } + h3 { + @apply text-2xl mt-5 mb-1.5 text-gray-50 font-bold; + } + h4 { + @apply text-xl mt-6 mb-1.5 text-gray-50 font-bold; + } + p { + @apply mb-2; + } + a { + @apply text-[#F74C00] underline hover:decoration-orange-400 transition duration-300; + } + ul { + @apply mt-2 mb-3 ml-1 list-disc list-inside marker:text-sky-600; + } + ol { + @apply mt-2 mb-3 ml-1 list-decimal list-inside marker:text-sky-500; + } + li { + @apply my-0.5; + } + code { + @apply bg-white/10 px-1 pb-px pt-1 rounded-md; + } + pre code { + @apply bg-inherit p-0 text-inherit; + } + hr { + @apply my-5 rounded-full; + } + img { + @apply max-w-full w-full h-full mx-auto my-5 object-contain md:w-3/4 lg:w-3/5 rounded-sm shadow-sm; + } + blockquote { + @apply p-4 my-3 border-s-4 border-gray-300 bg-gray-800 italic; + } + + pre { + @apply px-2 pt-2 pb-px overflow-x-auto text-sm sm:text-base rounded-sm mt-2 mb-4 after:content-[attr(data-lang)] after:text-[8px] after:opacity-40 selection:bg-gray-500/75; + } + pre code mark { + @apply pb-0.5 pt-1 pr-px text-inherit rounded-xs; + } +} + diff --git a/website/justfile b/website/justfile new file mode 100644 index 0000000000..5c9c677051 --- /dev/null +++ b/website/justfile @@ -0,0 +1,5 @@ +zola: + zola serve --open + +tailwind: + fnm exec --using latest npx @tailwindcss/cli -i input.css -o static/main.css diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000000..ee8a1c8812 --- /dev/null +++ b/website/package.json @@ -0,0 +1,7 @@ +{ + "devDependencies": { + "rustywind": "^0.24", + "tailwindcss": "^4.1", + "@tailwindcss/cli": "^4.1" + } +} From 7ec698696537aac4433bb0e60fb10310a6b34bc1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 16 May 2025 23:11:08 +0200 Subject: [PATCH 1372/1432] Add templates --- website/config.toml | 6 + website/content/_index.md | 143 ++++++++++++++++++++++ website/content/custom-exercises/index.md | 66 ++++++++++ website/input.css | 14 +-- website/justfile | 2 +- website/templates/404.html | 16 +++ website/templates/anchor-link.html | 2 + website/templates/base.html | 83 +++++++++++++ website/templates/index.html | 11 ++ website/templates/macros.html | 46 +++++++ website/templates/page.html | 11 ++ 11 files changed, 392 insertions(+), 8 deletions(-) create mode 100644 website/content/_index.md create mode 100644 website/content/custom-exercises/index.md create mode 100644 website/templates/404.html create mode 100644 website/templates/anchor-link.html create mode 100644 website/templates/base.html create mode 100644 website/templates/index.html create mode 100644 website/templates/macros.html create mode 100644 website/templates/page.html diff --git a/website/config.toml b/website/config.toml index d23b3314e9..6a9114a95b 100644 --- a/website/config.toml +++ b/website/config.toml @@ -7,6 +7,12 @@ build_search_index = false [markdown] highlight_code = true +highlight_theme = "ayu-mirage" + +insert_anchor_links = "heading" + +[extra] +logo_path = "images/happy_ferris.svg" [[extra.menu_items]] name = "Home" diff --git a/website/content/_index.md b/website/content/_index.md new file mode 100644 index 0000000000..2ad12ebb86 --- /dev/null +++ b/website/content/_index.md @@ -0,0 +1,143 @@ ++++ ++++ + +Greetings and welcome to Rustlings. +This project contains small exercises to get you used to reading and writing Rust code. +This includes reading and responding to compiler messages! + +It is recommended to do the Rustlings exercises in parallel to reading [the official Rust book](https://doc.rust-lang.org/book/), the most comprehensive resource for learning Rust πŸ“šοΈ + + + +## Getting Started + +### Installing Rust + +Before installing Rustlings, you need to have the **latest version of Rust** installed. +Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. +This will also install _Cargo_, Rust's package/project manager. + +> 🐧 If you are on Linux, make sure you have installed `gcc` (for a linker). +> +> Deb: `sudo apt install gcc` +> +> Dnf: `sudo dnf install gcc` + +> 🍎 If you are on MacOS, make sure you have installed Xcode and its developer tools by running `xcode-select --install`. + +### Installing Rustlings + +The following command will download and compile Rustlings: + +```bash +cargo install rustlings +``` + +
+If the installation fails… (click to expand) + +- Make sure you have the latest Rust version by running `rustup update` +- Try adding the `--locked` flag: `cargo install rustlings --locked` +- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new) + +
+ +### Initialization + +After installing Rustlings, run the following command to initialize the `rustlings/` directory: + +```bash +rustlings init +``` + +
+If the command rustlings can't be found… (click to expand) + +You are probably using Linux and installed Rust using your package manager. + +Cargo installs binaries to the directory `~/.cargo/bin`. +Sadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. + +The solution is to … + +- either add `~/.cargo/bin` manually to `PATH` +- or to uninstall Rust from the package manager and install it using the official way with `rustup`: https://www.rust-lang.org/tools/install + +
+ +Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises: + +```bash +cd rustlings/ +rustlings +``` + +## Working environment + +### Editor + +Our general recommendation is [VS Code](https://code.visualstudio.com/) with the [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). +But any editor that supports [rust-analyzer](https://rust-analyzer.github.io/) should be enough for working on the exercises. + +### Terminal + +While working with Rustlings, please use a modern terminal for the best user experience. +The default terminal on Linux and Mac should be sufficient. +On Windows, we recommend the [Windows Terminal](https://aka.ms/terminal). + +## Doing exercises + +The exercises are sorted by topic and can be found in the subdirectory `exercises/`. +For every topic, there is an additional `README.md` file with some resources to get you started on the topic. +We highly recommend that you have a look at them before you start πŸ“šοΈ + +Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! +Some exercises contain tests that need to pass for the exercise to be done βœ… + +Search for `TODO` and `todo!()` to find out what you need to change. +Ask for hints by entering `h` in the _watch mode_ πŸ’‘ + +### Watch Mode + +After the [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`. + +This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers). +It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory. + +
+If detecting file changes in the exercises/ directory fails… (click to expand) + +> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. +> +> Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). + +
+ +### Exercise List + +In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list. + +The list allows you to… + +- See the status of all exercises (done or pending) +- `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one) +- `r`: Reset status and file of the selected exercise (you need to _reload/reopen_ its file in your editor afterwards) + +See the footer of the list for all possible keys. + +## Questions? + +If you need any help while doing the exercises and the builtin-hints aren't helpful, feel free to ask in the [_Q&A_ category of the discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question wasn't asked yet πŸ’‘ + +## Continuing On + +Once you've completed Rustlings, put your new knowledge to good use! +Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. + +## Uninstalling Rustlings + +If you want to remove Rustlings from your system, run the following command: + +```bash +cargo uninstall rustlings +``` diff --git a/website/content/custom-exercises/index.md b/website/content/custom-exercises/index.md new file mode 100644 index 0000000000..e8bef7690f --- /dev/null +++ b/website/content/custom-exercises/index.md @@ -0,0 +1,66 @@ ++++ +title = "Custom Exercises" ++++ + +Custom exercises are a set of exercises maintained by the community. +You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them: + +- πŸ‡―πŸ‡΅ [Japanese Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. +- πŸ‡¨πŸ‡³ [Simplified Chinese Rustlings](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises. + +Do you want to create your own set of Rustlings exercises to focus on some specific topic? +Or do you want to translate the original Rustlings exercises? + + + +The support of Rustlings for custom exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. +You could also offer a translation of the original Rustlings exercises as custom exercises. + +## Getting started + +To create custom exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`. +This command will, similar to `cargo new PROJECT_NAME`, create a template directory called `PROJECT_NAME` with all what you need to get started. + +Read the comments in the generated `info.toml` file to understand its format. +It allows you to set a custom welcome and final message and specify the metadata of every exercise. + +## Create an exercise + +Here is an example of the metadata of one file: + +```toml +[[exercises]] +name = "intro1" +hint = """ +To finish this exercise, you need to … +This link might help you …""" +``` + +After entering this in `info.toml`, create the file `intro1.rs` in the `exercises/` directory. +The exercise needs to contain a `main` function, but it can be empty. +Adding tests is recommended. +Look at the official Rustlings exercises for inspiration. + +You can optionally add a solution file `intro1.rs` to the `solutions/` directory. + +Now, run `rustlings dev check`. +It will tell you about any issues with your exercises. +For example, it will tell you to run `rustlings dev update` to update the `Cargo.toml` file to include the new exercise `intro1`. + +`rustlings dev check` will also run your solutions (if you have any) to make sure that they run successfully. + +That's it! +You finished your first exercise πŸŽ‰ + +## Publish + +Now, add more exercises and publish them as a Git repository. + +Users just have to clone that repository and run `rustlings` in it to start working on your set of exercises just like the official ones. + +One difference to the official exercises is that the solution files will not be hidden until the user finishes an exercise. +But you can trust the users to not look at the solution too early πŸ˜‰ + +## Share + +After publishing your set of exercises, open an issue or a pull request in the official Rustlings repository to link to your project in the README πŸ˜ƒ diff --git a/website/input.css b/website/input.css index b26d9ba489..cd7a777b9a 100644 --- a/website/input.css +++ b/website/input.css @@ -2,22 +2,22 @@ @layer base { h1 { - @apply text-4xl mt-3 mb-3 text-gray-50 font-bold; + @apply text-4xl mt-3 mb-3 font-bold; } h2 { - @apply text-3xl mt-4 mb-1.5 text-gray-50 font-bold; + @apply text-3xl mt-4 mb-1.5 font-bold; } h3 { - @apply text-2xl mt-5 mb-1.5 text-gray-50 font-bold; + @apply text-2xl mt-5 mb-1.5 font-bold; } h4 { - @apply text-xl mt-6 mb-1.5 text-gray-50 font-bold; + @apply text-xl mt-6 mb-1.5 font-bold; } p { @apply mb-2; } a { - @apply text-[#F74C00] underline hover:decoration-orange-400 transition duration-300; + @apply text-[#FFC832] underline hover:decoration-orange-400 transition duration-300; } ul { @apply mt-2 mb-3 ml-1 list-disc list-inside marker:text-sky-600; @@ -41,11 +41,11 @@ @apply max-w-full w-full h-full mx-auto my-5 object-contain md:w-3/4 lg:w-3/5 rounded-sm shadow-sm; } blockquote { - @apply p-4 my-3 border-s-4 border-gray-300 bg-gray-800 italic; + @apply px-3 pt-2 pb-0.5 my-4 border-s-4 border-white/80 bg-white/7 rounded-sm italic; } pre { - @apply px-2 pt-2 pb-px overflow-x-auto text-sm sm:text-base rounded-sm mt-2 mb-4 after:content-[attr(data-lang)] after:text-[8px] after:opacity-40 selection:bg-gray-500/75; + @apply px-2 pt-2 pb-px overflow-x-auto text-sm sm:text-base rounded-sm mt-2 mb-4 after:content-[attr(data-lang)] after:text-[8px] after:opacity-40 selection:bg-white/15; } pre code mark { @apply pb-0.5 pt-1 pr-px text-inherit rounded-xs; diff --git a/website/justfile b/website/justfile index 5c9c677051..6d41913827 100644 --- a/website/justfile +++ b/website/justfile @@ -2,4 +2,4 @@ zola: zola serve --open tailwind: - fnm exec --using latest npx @tailwindcss/cli -i input.css -o static/main.css + fnm exec --using latest npx @tailwindcss/cli -w -i input.css -o static/main.css diff --git a/website/templates/404.html b/website/templates/404.html new file mode 100644 index 0000000000..ecee207de9 --- /dev/null +++ b/website/templates/404.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% import "macros.html" as macros %} + +{% block content %} +
+

DON'T PANIC!

+

404: Page not found!

+ + + + {{ macros::btn(link=get_url(path="/") , content="Back to the homepage") }} +
+{% endblock %} diff --git a/website/templates/anchor-link.html b/website/templates/anchor-link.html new file mode 100644 index 0000000000..c8644d96f7 --- /dev/null +++ b/website/templates/anchor-link.html @@ -0,0 +1,2 @@ + diff --git a/website/templates/base.html b/website/templates/base.html new file mode 100644 index 0000000000..64beaab3e7 --- /dev/null +++ b/website/templates/base.html @@ -0,0 +1,83 @@ + + + + + + + {%- set timestamp = now(timestamp=true) -%} + + {%- if page.title -%} + {% set_global title = page.title %} + {%- elif section.title -%} + {% set_global title = section.title %} + {%- else -%} + {% set_global title = config.title %} + {%- endif -%} + + {%- if page.description -%} + {% set_global description = page.description %} + {%- elif section.description -%} + {% set_global description = section.description %} + {%- else -%} + {% set_global description = config.description %} + {%- endif -%} + + {%- if page.permalink -%} + {% set_global permalink = page.permalink %} + {%- elif section.permalink -%} + {% set_global permalink = section.permalink %} + {%- endif %} + + {%- block title -%}{{- title -}}{%- endblock -%} + + + + + + + + + + + {% if permalink %}{% endif %} + + + +
+ + + +
+ +
+ {% block content %}{% endblock %} +
+ + + + diff --git a/website/templates/index.html b/website/templates/index.html new file mode 100644 index 0000000000..5877765154 --- /dev/null +++ b/website/templates/index.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% import "macros.html" as macros %} + +{% block content %} +
+

Rustlings

+ + {{ section.content | replace(from="", to=macros::toc() ) | safe }} +
+{% endblock %} diff --git a/website/templates/macros.html b/website/templates/macros.html new file mode 100644 index 0000000000..c40abe53b1 --- /dev/null +++ b/website/templates/macros.html @@ -0,0 +1,46 @@ +{% macro toc() %} +

Landscape mode recommended on mobile devices

+ + {%- if page.toc -%} + {% set_global toc = page.toc %} + {%- else -%} + {% set_global toc = section.toc %} + {%- endif -%} + + {% if toc %} +
+ +
+ {% endif %} +{% endmacro %} + +{% macro btn(link, content) %} + {{ content | safe }} +{% endmacro %} diff --git a/website/templates/page.html b/website/templates/page.html new file mode 100644 index 0000000000..90b269df5e --- /dev/null +++ b/website/templates/page.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% import "macros.html" as macros %} + +{% block content %} +
+

{{ page.title }}

+ + {{ page.content | replace(from="", to=macros::toc() ) | safe }} +
+{% endblock %} From fda18e8895a162f484245b8a6a2eb0921632d0c7 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 12:24:27 +0200 Subject: [PATCH 1373/1432] Add Ferris SVGs --- website/static/images/happy_ferris.svg | 33 ++++++++++++ website/static/images/panic.svg | 70 ++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 website/static/images/happy_ferris.svg create mode 100644 website/static/images/panic.svg diff --git a/website/static/images/happy_ferris.svg b/website/static/images/happy_ferris.svg new file mode 100644 index 0000000000..c7f240dd97 --- /dev/null +++ b/website/static/images/happy_ferris.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/static/images/panic.svg b/website/static/images/panic.svg new file mode 100644 index 0000000000..be55f5e09b --- /dev/null +++ b/website/static/images/panic.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 61c17cb349b649223e943b6db0786feacabc8ca5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 12:24:44 +0200 Subject: [PATCH 1374/1432] Change syntax highlighting theme --- website/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/config.toml b/website/config.toml index 6a9114a95b..906bb8bca4 100644 --- a/website/config.toml +++ b/website/config.toml @@ -7,7 +7,7 @@ build_search_index = false [markdown] highlight_code = true -highlight_theme = "ayu-mirage" +highlight_theme = "dracula" insert_anchor_links = "heading" From 7e26418952afa7d25d1958ee0e4d810955acf94a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 12:25:08 +0200 Subject: [PATCH 1375/1432] Remove the third-party exercises file --- THIRD_PARTY_EXERCISES.md | 53 ---------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 THIRD_PARTY_EXERCISES.md diff --git a/THIRD_PARTY_EXERCISES.md b/THIRD_PARTY_EXERCISES.md deleted file mode 100644 index 62646c5bed..0000000000 --- a/THIRD_PARTY_EXERCISES.md +++ /dev/null @@ -1,53 +0,0 @@ -# Third-Party Exercises - -The support of Rustlings for third-party exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. -You could also offer a translation of the original Rustlings exercises as third-party exercises. - -## Getting started - -To create third-party exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`. -This command will, similar to `cargo new PROJECT_NAME`, create a template directory called `PROJECT_NAME` with all what you need to get started. - -Read the comments in the generated `info.toml` file to understand its format. -It allows you to set a custom welcome and final message and specify the metadata of every exercise. - -## Create an exercise - -Here is an example of the metadata of one file: - -```toml -[[exercises]] -name = "intro1" -hint = """ -To finish this exercise, you need to … -This link might help you …""" -``` - -After entering this in `info.toml`, create the file `intro1.rs` in the `exercises/` directory. -The exercise needs to contain a `main` function, but it can be empty. -Adding tests is recommended. -Look at the official Rustlings exercises for inspiration. - -You can optionally add a solution file `intro1.rs` to the `solutions/` directory. - -Now, run `rustlings dev check`. -It will tell you about any issues with your exercises. -For example, it will tell you to run `rustlings dev update` to update the `Cargo.toml` file to include the new exercise `intro1`. - -`rustlings dev check` will also run your solutions (if you have any) to make sure that they run successfully. - -That's it! -You finished your first exercise πŸŽ‰ - -## Publish - -Now, add more exercises and publish them as a Git repository. - -Users just have to clone that repository and run `rustlings` in it to start working on your set of exercises just like the official ones. - -One difference to the official exercises is that the solution files will not be hidden until the user finishes an exercise. -But you can trust the users to not look at the solution too early πŸ˜‰ - -## Share - -After publishing your set of exercises, open an issue or a pull request in the official Rustlings repository to link to your project in the README πŸ˜ƒ From b9d1e636a46ee6bc8ffa5dd889137229d76976ce Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 12:25:32 +0200 Subject: [PATCH 1376/1432] Reduce the README to the website link --- README.md | 160 +----------------------------------------------------- 1 file changed, 1 insertion(+), 159 deletions(-) diff --git a/README.md b/README.md index 8480b1a3d7..bb3fe212ae 100644 --- a/README.md +++ b/README.md @@ -1,161 +1,3 @@ # Rustlings πŸ¦€β€οΈ -Greetings and welcome to Rustlings. -This project contains small exercises to get you used to reading and writing Rust code. -This includes reading and responding to compiler messages! - -It is recommended to do the Rustlings exercises in parallel to reading [the official Rust book](https://doc.rust-lang.org/book/), the most comprehensive resource for learning Rust πŸ“šοΈ - -[Rust By Example](https://doc.rust-lang.org/rust-by-example/) is another recommended resource that you might find helpful. -It contains code examples and exercises similar to Rustlings, but online. - -## Getting Started - -### Installing Rust - -Before installing Rustlings, you need to have the **latest version of Rust** installed. -Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. -This will also install _Cargo_, Rust's package/project manager. - -> 🐧 If you are on Linux, make sure you have installed `gcc` (for a linker). -> -> Deb: `sudo apt install gcc` -> -> Dnf: `sudo dnf install gcc` - -> 🍎 If you are on MacOS, make sure you have installed Xcode and its developer tools by running `xcode-select --install`. - -### Installing Rustlings - -The following command will download and compile Rustlings: - -```bash -cargo install rustlings -``` - -
-If the installation fails… (click to expand) - -- Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings --locked` -- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new) - -
- -### Initialization - -After installing Rustlings, run the following command to initialize the `rustlings/` directory: - -```bash -rustlings init -``` - -
-If the command rustlings can't be found… (click to expand) - -You are probably using Linux and installed Rust using your package manager. - -Cargo installs binaries to the directory `~/.cargo/bin`. -Sadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. - -The solution is to … - -- either add `~/.cargo/bin` manually to `PATH` -- or to uninstall Rust from the package manager and install it using the official way with `rustup`: https://www.rust-lang.org/tools/install - -
- -Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises: - -```bash -cd rustlings/ -rustlings -``` - -## Working environment - -### Editor - -Our general recommendation is [VS Code](https://code.visualstudio.com/) with the [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). -But any editor that supports [rust-analyzer](https://rust-analyzer.github.io/) should be enough for working on the exercises. - -### Terminal - -While working with Rustlings, please use a modern terminal for the best user experience. -The default terminal on Linux and Mac should be sufficient. -On Windows, we recommend the [Windows Terminal](https://aka.ms/terminal). - -## Doing exercises - -The exercises are sorted by topic and can be found in the subdirectory `exercises/`. -For every topic, there is an additional `README.md` file with some resources to get you started on the topic. -We highly recommend that you have a look at them before you start πŸ“šοΈ - -Most exercises contain an error that keeps them from compiling, and it's up to you to fix it! -Some exercises contain tests that need to pass for the exercise to be done βœ… - -Search for `TODO` and `todo!()` to find out what you need to change. -Ask for hints by entering `h` in the _watch mode_ πŸ’‘ - -### Watch Mode - -After the [initialization](#initialization), Rustlings can be launched by simply running the command `rustlings`. - -This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers). -It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory. - -
-If detecting file changes in the exercises/ directory fails… (click to expand) - -> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. -> -> Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). - -
- -### Exercise List - -In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list. - -The list allows you to… - -- See the status of all exercises (done or pending) -- `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one) -- `r`: Reset status and file of the selected exercise (you need to _reload/reopen_ its file in your editor afterwards) - -See the footer of the list for all possible keys. - -## Questions? - -If you need any help while doing the exercises and the builtin-hints aren't helpful, feel free to ask in the [_Q&A_ category of the discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question wasn't asked yet πŸ’‘ - -## Third-Party Exercises - -Third-party exercises are a set of exercises maintained by the community. -You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them: - -- πŸ‡―πŸ‡΅ [Japanese Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. -- πŸ‡¨πŸ‡³ [Simplified Chinese Rustlings](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises. - -Do you want to create your own set of Rustlings exercises to focus on some specific topic? -Or do you want to translate the original Rustlings exercises? -Then follow the the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)! - -## Continuing On - -Once you've completed Rustlings, put your new knowledge to good use! -Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. - -## Uninstalling Rustlings - -If you want to remove Rustlings from your system, run the following command: - -```bash -cargo uninstall rustlings -``` - -## Contributing - -See [CONTRIBUTING.md](https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md) πŸ”— - -Thanks to [all the wonderful contributors](https://github.com/rust-lang/rustlings/graphs/contributors) ✨ +➑️ [**rustlings.rust-lang.org**](https://rustlings.rust-lang.org) ⬅️ From 6d5369d4d02092757e0a75ef55cf1ea87bfe2182 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 12:25:55 +0200 Subject: [PATCH 1377/1432] Add more menu and footer items --- website/config.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/website/config.toml b/website/config.toml index 906bb8bca4..5a360fbada 100644 --- a/website/config.toml +++ b/website/config.toml @@ -18,9 +18,24 @@ logo_path = "images/happy_ferris.svg" name = "Home" url = "/" [[extra.menu_items]] +name = "Setup" +url = "/setup" +[[extra.menu_items]] +name = "Usage" +url = "/usage" +[[extra.menu_items]] +name = "Q/A" +url = "https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=" +[[extra.menu_items]] name = "Custom Exercises" url = "/custom-exercises" [[extra.footer_items]] name = "Repository" url = "https://github.com/rust-lang/rustlings" +[[extra.footer_items]] +name = "Changelog" +url = "https://github.com/rust-lang/rustlings/blob/main/CHANGELOG.md" +[[extra.footer_items]] +name = "MIT License" +url = "https://github.com/rust-lang/rustlings/blob/main/LICENSE" From 2673177b17eac262e70a0ac8f376346fecb636b5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 13:02:34 +0200 Subject: [PATCH 1378/1432] Update header and footer --- website/config.toml | 2 +- website/static/images/rust_logo.svg | 61 +++++++++++++++++++++++++++++ website/templates/base.html | 19 +++++---- 3 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 website/static/images/rust_logo.svg diff --git a/website/config.toml b/website/config.toml index 5a360fbada..031e02886d 100644 --- a/website/config.toml +++ b/website/config.toml @@ -15,7 +15,7 @@ insert_anchor_links = "heading" logo_path = "images/happy_ferris.svg" [[extra.menu_items]] -name = "Home" +name = "Rustlings" url = "/" [[extra.menu_items]] name = "Setup" diff --git a/website/static/images/rust_logo.svg b/website/static/images/rust_logo.svg new file mode 100644 index 0000000000..3b42cfe034 --- /dev/null +++ b/website/static/images/rust_logo.svg @@ -0,0 +1,61 @@ + + + diff --git a/website/templates/base.html b/website/templates/base.html index 64beaab3e7..afe8d89153 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -48,16 +48,16 @@ -
+
-
-### Exercise List +## Exercise List In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list. From b4a6b87e2402c8b1e56227957ed9b61406d2ce24 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 19:34:53 +0200 Subject: [PATCH 1386/1432] Less padding --- website/templates/base.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/templates/base.html b/website/templates/base.html index afe8d89153..5745db36a7 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -47,7 +47,7 @@ {% if permalink %}{% endif %} - +
-
+
Date: Sat, 17 May 2025 19:53:30 +0200 Subject: [PATCH 1387/1432] Remove macros --- website/input.css | 2 +- website/templates/404.html | 6 ++---- website/templates/base.html | 12 +++++------ website/templates/index.html | 2 -- website/templates/macros.html | 38 ----------------------------------- website/templates/page.html | 34 ++++++++++++++++++++++++++++--- 6 files changed, 40 insertions(+), 54 deletions(-) delete mode 100644 website/templates/macros.html diff --git a/website/input.css b/website/input.css index cd7a777b9a..3dd5501389 100644 --- a/website/input.css +++ b/website/input.css @@ -38,7 +38,7 @@ @apply my-5 rounded-full; } img { - @apply max-w-full w-full h-full mx-auto my-5 object-contain md:w-3/4 lg:w-3/5 rounded-sm shadow-sm; + @apply md:w-3/4 lg:w-3/5; } blockquote { @apply px-3 pt-2 pb-0.5 my-4 border-s-4 border-white/80 bg-white/7 rounded-sm italic; diff --git a/website/templates/404.html b/website/templates/404.html index ecee207de9..9bbd441819 100644 --- a/website/templates/404.html +++ b/website/templates/404.html @@ -1,16 +1,14 @@ {% extends "base.html" %} -{% import "macros.html" as macros %} - {% block content %} {% endblock %} diff --git a/website/templates/base.html b/website/templates/base.html index 5745db36a7..8306ac7d65 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -52,7 +52,7 @@ @@ -68,17 +68,17 @@ {% block content %}{% endblock %} -
-
+ diff --git a/website/templates/index.html b/website/templates/index.html index c61defd3a1..0d2b2e3986 100644 --- a/website/templates/index.html +++ b/website/templates/index.html @@ -1,7 +1,5 @@ {% extends "base.html" %} -{% import "macros.html" as macros %} - {% block content %}

Rustlings

diff --git a/website/templates/macros.html b/website/templates/macros.html deleted file mode 100644 index dd7b928c74..0000000000 --- a/website/templates/macros.html +++ /dev/null @@ -1,38 +0,0 @@ -{% macro toc() %} - {% if page.toc %} -
- -
- {% endif %} -{% endmacro %} - -{% macro btn(link, content) %} - {{ content | safe }} -{% endmacro %} diff --git a/website/templates/page.html b/website/templates/page.html index 90b269df5e..8c9a9f1a20 100644 --- a/website/templates/page.html +++ b/website/templates/page.html @@ -1,11 +1,39 @@ {% extends "base.html" %} -{% import "macros.html" as macros %} - {% block content %}

{{ page.title }}

- {{ page.content | replace(from="", to=macros::toc() ) | safe }} +
+ +
+ + {{ page.content | safe }}
{% endblock %} From 8fa598ae7eb8c014657cea787376109b7f4c5d18 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 20:20:19 +0200 Subject: [PATCH 1388/1432] Add details shortcode --- website/content/setup/index.md | 43 +++++++++++------------ website/content/usage/index.md | 11 +++--- website/input.css | 2 +- website/templates/shortcodes/details.html | 9 +++++ 4 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 website/templates/shortcodes/details.html diff --git a/website/content/setup/index.md b/website/content/setup/index.md index 14984a6bca..db4ed40f56 100644 --- a/website/content/setup/index.md +++ b/website/content/setup/index.md @@ -6,17 +6,16 @@ title = "Setup" ## Installing Rust -Before installing Rustlings, you need to have the **latest version of Rust** installed. -Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions on installing Rust. +Before installing Rustlings, you must have the **latest version of Rust** installed. +Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions. This will also install _Cargo_, Rust's package/project manager. -> 🐧 If you are on Linux, make sure you have installed `gcc` (for a linker). +> 🐧 If you are on Linux, make sure you have `gcc` installed (_for a linker_). > -> Deb: `sudo apt install gcc` -> -> Dnf: `sudo dnf install gcc` +> Debian: `sudo apt install gcc`\ +> Fedora: `sudo dnf install gcc` -> 🍎 If you are on MacOS, make sure you have installed Xcode and its developer tools by running `xcode-select --install`. +> 🍎 If you are on MacOS, make sure you have _Xcode and its developer tools_ installed: `xcode-select --install` ## Installing Rustlings @@ -26,14 +25,13 @@ The following command will download and compile Rustlings: cargo install rustlings ``` -
-If the installation fails… (click to expand) +{% details(summary="If the installation fails…") %} -> - Make sure you have the latest Rust version by running `rustup update` -> - Try adding the `--locked` flag: `cargo install rustlings --locked` -> - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new) +- Make sure you have the latest Rust version by running `rustup update` +- Try adding the `--locked` flag: `cargo install rustlings --locked` +- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new) -
+{% end %} ## Initialization @@ -43,18 +41,17 @@ After installing Rustlings, run the following command to initialize the `rustlin rustlings init ``` -
-If the command rustlings can't be found… (click to expand) +{% details(summary="If the command rustlings can't be found…") %} -> You are probably using Linux and installed Rust using your package manager. -> -> Cargo installs binaries to the directory `~/.cargo/bin`. -> Sadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. -> -> - Either add `~/.cargo/bin` manually to `PATH` -> - Or uninstall Rust from the package manager and [install it using the official way with `rustup`](https://www.rust-lang.org/tools/install) +You are probably using Linux and installed Rust using your package manager. + +Cargo installs binaries to the directory `~/.cargo/bin`. +Sadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable. + +- Either add `~/.cargo/bin` manually to `PATH` +- Or uninstall Rust from the package manager and [install it using the official way with `rustup`](https://www.rust-lang.org/tools/install) -
+{% end %} Now, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises: diff --git a/website/content/usage/index.md b/website/content/usage/index.md index 7c8b0ddbba..eecb7d2df2 100644 --- a/website/content/usage/index.md +++ b/website/content/usage/index.md @@ -23,14 +23,13 @@ After the [initialization](@/setup/index.md#initialization), Rustlings can be la This will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers). It will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory. -
-If detecting file changes in the exercises/ directory fails… (click to expand) +{% details(summary="If detecting file changes in the exercises/ directory fails…") %} -> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. -> -> Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). +You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. -
+Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). + +{% end %} ## Exercise List diff --git a/website/input.css b/website/input.css index 3dd5501389..b2536753dc 100644 --- a/website/input.css +++ b/website/input.css @@ -41,7 +41,7 @@ @apply md:w-3/4 lg:w-3/5; } blockquote { - @apply px-3 pt-2 pb-0.5 my-4 border-s-4 border-white/80 bg-white/7 rounded-sm italic; + @apply px-3 pt-2 pb-0.5 my-4 border-s-4 border-white/80 bg-white/7 rounded-sm; } pre { diff --git a/website/templates/shortcodes/details.html b/website/templates/shortcodes/details.html new file mode 100644 index 0000000000..c32d7c874f --- /dev/null +++ b/website/templates/shortcodes/details.html @@ -0,0 +1,9 @@ +
+ + {{ summary | safe }} (click to expand) + + +
+ {{ body | markdown | safe }} +
+
From 5fc787f4e4af9223d1b6d579db3460b2cbf8bbd1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 20:45:20 +0200 Subject: [PATCH 1389/1432] Style details body --- website/templates/shortcodes/details.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/templates/shortcodes/details.html b/website/templates/shortcodes/details.html index c32d7c874f..bcdd6b5082 100644 --- a/website/templates/shortcodes/details.html +++ b/website/templates/shortcodes/details.html @@ -3,7 +3,7 @@ {{ summary | safe }} (click to expand) -
+
{{ body | markdown | safe }}
From dc468882cc00254b0b87fdcf39d9270d34e6adf8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 20:45:28 +0200 Subject: [PATCH 1390/1432] Highlight platform --- website/content/setup/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/content/setup/index.md b/website/content/setup/index.md index db4ed40f56..54551ada7c 100644 --- a/website/content/setup/index.md +++ b/website/content/setup/index.md @@ -10,12 +10,12 @@ Before installing Rustlings, you must have the **latest version of Rust** instal Visit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions. This will also install _Cargo_, Rust's package/project manager. -> 🐧 If you are on Linux, make sure you have `gcc` installed (_for a linker_). +> 🐧 If you are on **Linux**, make sure you have `gcc` installed (_for a linker_). > > Debian: `sudo apt install gcc`\ > Fedora: `sudo dnf install gcc` -> 🍎 If you are on MacOS, make sure you have _Xcode and its developer tools_ installed: `xcode-select --install` +> 🍎 If you are on **MacOS**, make sure you have _Xcode and its developer tools_ installed: `xcode-select --install` ## Installing Rustlings From 8c247632594db859e319cd561678218de3aca53d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 21:02:01 +0200 Subject: [PATCH 1391/1432] Q/A -> Q&A --- website/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/config.toml b/website/config.toml index 84acb39e39..8ecb09b99a 100644 --- a/website/config.toml +++ b/website/config.toml @@ -27,7 +27,7 @@ url = "/usage" name = "Custom Exercises" url = "/custom-exercises" [[extra.menu_items]] -name = "Q/A" +name = "Q&A" url = "https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=" [[extra.footer_items]] From f6a657a0c3628d75dae7cd1d3a822dc19d379f15 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 21:02:45 +0200 Subject: [PATCH 1392/1432] Finish the usage page --- website/content/usage/index.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/website/content/usage/index.md b/website/content/usage/index.md index eecb7d2df2..7bf02f5702 100644 --- a/website/content/usage/index.md +++ b/website/content/usage/index.md @@ -27,7 +27,7 @@ It will rerun the current exercise automatically every time you change the exerc You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. -Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). +Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or a virtual machine (e.g. WSL). {% end %} @@ -45,17 +45,11 @@ See the footer of the list for all possible keys. ## Questions? -If you need any help while doing the exercises and the builtin-hints aren't helpful, feel free to ask in the [_Q&A_ category of the discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question wasn't asked yet πŸ’‘ +If you need any help while doing the exercises and the builtin hints aren't helpful, feel free to ask in the [_Q&A_ discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question isn't answered there πŸ’‘ ## Continuing On Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. -## Uninstalling Rustlings - -If you want to remove Rustlings from your system, run the following command: - -```bash -cargo uninstall rustlings -``` +> If you want to create your own custom Rustlings exercises, visit the [**custom exercises**](@/custom-exercises/index.md) page πŸ—οΈ From a51d6f1309b986c270d781b08f6949f29806b18a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 21:25:19 +0200 Subject: [PATCH 1393/1432] third-party/custom -> community --- CHANGELOG.md | 10 +++++----- src/app_state.rs | 4 ++-- src/dev.rs | 2 +- src/dev/new.rs | 8 ++++---- src/info_file.rs | 4 ++-- src/main.rs | 2 +- website/config.toml | 4 ++-- .../index.md | 19 +++++++++---------- website/content/usage/index.md | 2 +- 9 files changed, 27 insertions(+), 28 deletions(-) rename website/content/{custom-exercises => community-exercises}/index.md (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6781ba89ab..c1dbb42bbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ - New option `x` in the prompt to reset the file of the current exercise πŸ”„ - Allow `dead_code` for all exercises and solutions ⚰️ (thanks to [@huss4in](https://github.com/huss4in)) - Pause input while running an exercise to avoid unexpected prompt interactions ⏸️ -- Limit the maximum number of exercises to 999. Any third-party exercises willing to reach that limit? πŸ” +- Limit the maximum number of exercises to 999. Any community exercises willing to reach that limit? πŸ” ### Changed @@ -93,7 +93,7 @@ #### Added -- `dev check`: Check that all exercises (including third-party ones) include at least one `TODO` comment. +- `dev check`: Check that all exercises (including community ones) include at least one `TODO` comment. - `dev check`: Check that all exercises actually fail to run (not already solved). #### Changed @@ -168,13 +168,13 @@ This should avoid issues related to the language server or to running exercises, Clippy lints are now shown on all exercises, not only the Clippy exercises πŸ“Ž Make Clippy your friend from early on πŸ₯° -### Third-party exercises +### Community Exercises -Rustlings now supports third-party exercises! +Rustlings now supports community exercises! Do you want to create your own set of Rustlings exercises to focus on some specific topic? Or do you want to translate the original Rustlings exercises? -Then follow the link to the guide about [third-party exercises](THIRD_PARTY_EXERCISES.md)! +Then follow the link to the guide about [community exercises](https://rustlings.rust-lang.org/community-exercises)! ## 5.6.1 (2023-09-18) diff --git a/src/app_state.rs b/src/app_state.rs index d1c45d4963..f3f348133e 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -315,7 +315,7 @@ impl AppState { } // Official exercises: Dump the original file from the binary. - // Third-party exercises: Reset the exercise file with `git stash`. + // Community exercises: Reset the exercise file with `git stash`. fn reset(&self, exercise_ind: usize, path: &str) -> Result<()> { if self.official_exercises { return EMBEDDED_FILES @@ -385,7 +385,7 @@ impl AppState { } /// Official exercises: Dump the solution file from the binary and return its path. - /// Third-party exercises: Check if a solution file exists and return its path in that case. + /// Community exercises: Check if a solution file exists and return its path in that case. pub fn current_solution_path(&self) -> Result> { if cfg!(debug_assertions) { return Ok(None); diff --git a/src/dev.rs b/src/dev.rs index 354d77c4b1..41fddbeb98 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -8,7 +8,7 @@ mod update; #[derive(Subcommand)] pub enum DevCommands { - /// Create a new project for third-party Rustlings exercises + /// Create a new project for community exercises New { /// The path to create the project in path: PathBuf, diff --git a/src/dev/new.rs b/src/dev/new.rs index ba3517f5b8..883b6fa777 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -86,10 +86,10 @@ target/ "; const INFO_FILE_BEFORE_FORMAT_VERSION: &str = - "# The format version is an indicator of the compatibility of third-party exercises with the + "# The format version is an indicator of the compatibility of community 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 +# In case Rustlings makes an unavoidable breaking change to the expected format of community # 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 = "; @@ -97,7 +97,7 @@ 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.""" +welcome_message = """Welcome to these community 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""" @@ -141,7 +141,7 @@ publish = false const README: &str = "# Rustlings πŸ¦€ -Welcome to these third-party Rustlings exercises πŸ˜ƒ +Welcome to these community Rustlings exercises πŸ˜ƒ First, [install Rustlings using the official instructions](https://github.com/rust-lang/rustlings) βœ… diff --git a/src/info_file.rs b/src/info_file.rs index ec61f8ad0b..634bece9a8 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -79,7 +79,7 @@ impl RunnableExercise for ExerciseInfo { /// The deserialized `info.toml` file. #[derive(Deserialize)] pub struct InfoFile { - /// For possible breaking changes in the future for third-party exercises. + /// For possible breaking changes in the future for community exercises. pub format_version: u8, /// Shown to users when starting with the exercises. pub welcome_message: Option, @@ -91,7 +91,7 @@ pub struct InfoFile { impl InfoFile { /// Official exercises: Parse the embedded `info.toml` file. - /// Third-party exercises: Parse the `info.toml` file in the current directory. + /// Community exercises: Parse the `info.toml` file in the current directory. pub fn parse() -> Result { // Read a local `info.toml` if it exists. let slf = match fs::read_to_string("info.toml") { diff --git a/src/main.rs b/src/main.rs index 6688e3e6f3..bce2593dc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,7 @@ enum Subcommands { /// The name of the exercise name: Option, }, - /// Commands for developing (third-party) Rustlings exercises + /// Commands for developing (community) Rustlings exercises #[command(subcommand)] Dev(DevCommands), } diff --git a/website/config.toml b/website/config.toml index 8ecb09b99a..796f13a498 100644 --- a/website/config.toml +++ b/website/config.toml @@ -24,8 +24,8 @@ url = "/setup" name = "Usage" url = "/usage" [[extra.menu_items]] -name = "Custom Exercises" -url = "/custom-exercises" +name = "Community Exercises" +url = "/community-exercises" [[extra.menu_items]] name = "Q&A" url = "https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=" diff --git a/website/content/custom-exercises/index.md b/website/content/community-exercises/index.md similarity index 85% rename from website/content/custom-exercises/index.md rename to website/content/community-exercises/index.md index e8bef7690f..1c8cd5773b 100644 --- a/website/content/custom-exercises/index.md +++ b/website/content/community-exercises/index.md @@ -1,24 +1,23 @@ +++ -title = "Custom Exercises" +title = "Community Exercises" +++ -Custom exercises are a set of exercises maintained by the community. -You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them: +Do you want to create your own set of Rustlings exercises to focus on some specific topic? +Or do you want to translate the original Rustlings exercises? + +## List of Community Exercises - πŸ‡―πŸ‡΅ [Japanese Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. - πŸ‡¨πŸ‡³ [Simplified Chinese Rustlings](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises. -Do you want to create your own set of Rustlings exercises to focus on some specific topic? -Or do you want to translate the original Rustlings exercises? - - +You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them -The support of Rustlings for custom exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. -You could also offer a translation of the original Rustlings exercises as custom exercises. +The support of Rustlings for community exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. +You could also offer a translation of the original Rustlings exercises as community exercises. ## Getting started -To create custom exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`. +To create community exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`. This command will, similar to `cargo new PROJECT_NAME`, create a template directory called `PROJECT_NAME` with all what you need to get started. Read the comments in the generated `info.toml` file to understand its format. diff --git a/website/content/usage/index.md b/website/content/usage/index.md index 7bf02f5702..88dabf4ae4 100644 --- a/website/content/usage/index.md +++ b/website/content/usage/index.md @@ -52,4 +52,4 @@ If you need any help while doing the exercises and the builtin hints aren't help Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. -> If you want to create your own custom Rustlings exercises, visit the [**custom exercises**](@/custom-exercises/index.md) page πŸ—οΈ +> If you want to create your own Rustlings exercises, visit the [**community exercises**](@/community-exercises/index.md) page πŸ—οΈ From 54a74fd63810020806161d5ed030810039735ea2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 21:28:25 +0200 Subject: [PATCH 1394/1432] Use internal links for validation --- website/config.toml | 8 ++++---- website/templates/404.html | 2 +- website/templates/base.html | 10 ++++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/website/config.toml b/website/config.toml index 796f13a498..0c01dc7d26 100644 --- a/website/config.toml +++ b/website/config.toml @@ -16,16 +16,16 @@ logo_path = "images/happy_ferris.svg" [[extra.menu_items]] name = "Rustlings" -url = "/" +url = "@/_index.md" [[extra.menu_items]] name = "Setup" -url = "/setup" +url = "@/setup/index.md" [[extra.menu_items]] name = "Usage" -url = "/usage" +url = "@/usage/index.md" [[extra.menu_items]] name = "Community Exercises" -url = "/community-exercises" +url = "@/community-exercises/index.md" [[extra.menu_items]] name = "Q&A" url = "https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=" diff --git a/website/templates/404.html b/website/templates/404.html index 9bbd441819..234eb46f41 100644 --- a/website/templates/404.html +++ b/website/templates/404.html @@ -9,6 +9,6 @@

404: Page not found!

src="{{ get_url(path='images/panic.svg') | safe }}" alt=""> - Back to homepage + Back to homepage {% endblock %} diff --git a/website/templates/base.html b/website/templates/base.html index 8306ac7d65..91b6cee8ca 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -50,7 +50,7 @@
{{ menu_item.name }} + {%- if menu_item.url is starting_with("@") -%} + {% set_global menu_item_url = get_url(path=menu_item.url) %} + {%- else -%} + {% set_global menu_item_url = menu_item.url %} + {%- endif %} + + {{ menu_item.name }} {% endfor %}
From 69a9e9cafc36cc73c196e462aa7fb7d812ab1012 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 22:05:48 +0200 Subject: [PATCH 1395/1432] Less top margin for blockquotes --- website/input.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/input.css b/website/input.css index b2536753dc..af0675d8b7 100644 --- a/website/input.css +++ b/website/input.css @@ -41,7 +41,7 @@ @apply md:w-3/4 lg:w-3/5; } blockquote { - @apply px-3 pt-2 pb-0.5 my-4 border-s-4 border-white/80 bg-white/7 rounded-sm; + @apply px-3 pt-2 pb-0.5 mb-4 mt-2 border-s-4 border-white/80 bg-white/7 rounded-sm; } pre { From 512ded81c4e6075e1bcfdf4f2675814d556588bc Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 22:05:57 +0200 Subject: [PATCH 1396/1432] Done community exercises page --- website/content/community-exercises/index.md | 40 ++++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/website/content/community-exercises/index.md b/website/content/community-exercises/index.md index 1c8cd5773b..0f713d7cc7 100644 --- a/website/content/community-exercises/index.md +++ b/website/content/community-exercises/index.md @@ -2,37 +2,36 @@ title = "Community Exercises" +++ -Do you want to create your own set of Rustlings exercises to focus on some specific topic? -Or do you want to translate the original Rustlings exercises? - ## List of Community Exercises - πŸ‡―πŸ‡΅ [Japanese Rustlings](https://github.com/sotanengel/rustlings-jp):A Japanese translation of the Rustlings exercises. - πŸ‡¨πŸ‡³ [Simplified Chinese Rustlings](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises. -You can use the same `rustlings` program that you installed with `cargo install rustlings` to run them +> You can use the same `rustlings` program that you installed with `cargo install rustlings` to run community exercises. + +## Creating Community Exercises -The support of Rustlings for community exercises allows you to create your own set of Rustlings exercises to focus on some specific topic. +Rustling's support for community exercises allows you to create your own exercises to focus on some specific topic. You could also offer a translation of the original Rustlings exercises as community exercises. -## Getting started +### Getting Started To create community exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`. -This command will, similar to `cargo new PROJECT_NAME`, create a template directory called `PROJECT_NAME` with all what you need to get started. +This command will, similar to `cargo new PROJECT_NAME`, create the template directory `PROJECT_NAME` with all what you need to get started. -Read the comments in the generated `info.toml` file to understand its format. +_Read the comments_ in the generated `info.toml` file to understand its format. It allows you to set a custom welcome and final message and specify the metadata of every exercise. -## Create an exercise +### Creating an Exercise -Here is an example of the metadata of one file: +Here is an example of the metadata of one exercise: ```toml [[exercises]] name = "intro1" hint = """ To finish this exercise, you need to … -This link might help you …""" +These links might help you …""" ``` After entering this in `info.toml`, create the file `intro1.rs` in the `exercises/` directory. @@ -51,15 +50,24 @@ For example, it will tell you to run `rustlings dev update` to update the `Cargo That's it! You finished your first exercise πŸŽ‰ -## Publish +### Cargo.toml + +Except of the `bin` list, you can modify the `Cargo.toml` file as you want. + +> The `bin` list is automatically updated by running `rustlings dev update` + +- You can add dependencies in the `[dependencies]` table. +- You might want to [configure some lints](https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section) for all exercises. You can do so in the `[lints.rust]` and `[lints.clippy]` tables. + +### Publishing Now, add more exercises and publish them as a Git repository. -Users just have to clone that repository and run `rustlings` in it to start working on your set of exercises just like the official ones. +Users just have to clone that repository and run `rustlings` in it to start working on your exercises (just like the official ones). One difference to the official exercises is that the solution files will not be hidden until the user finishes an exercise. -But you can trust the users to not look at the solution too early πŸ˜‰ +But you can trust your users to not open the solution too early πŸ˜‰ -## Share +### Sharing -After publishing your set of exercises, open an issue or a pull request in the official Rustlings repository to link to your project in the README πŸ˜ƒ +After publishing your community exercises, open an issue or a pull request in the [official Rustlings repository](https://github.com/rust-lang/rustlings) to add your project to the [list of community exercises](#list-of-community-exercises) πŸ˜ƒ From 596e7f36cc0fc8b6b755d444b7f054da19734b8e Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 22:33:00 +0200 Subject: [PATCH 1397/1432] Add website CI --- .github/workflows/website.yml | 46 +++++++++++++++++++++++++++++++++++ website/package.json | 7 +++--- 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/website.yml diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml new file mode 100644 index 0000000000..355d40bd45 --- /dev/null +++ b/.github/workflows/website.yml @@ -0,0 +1,46 @@ +name: Website + +on: + push: + branches: [main] + +jobs: + # Build & upload the static files as an artifact + build: + defaults: + run: + working-directory: website + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Zola + run: sudo snap install zola + - name: Install TailwindCSS + run: npm install @tailwindcss/cli + - name: Build CSS + - run: npx @tailwindcss/cli -m -i input.css -o static/main.css + - name: Build site + run: zola build + - name: Upload static files as artifact + uses: actions/upload-pages-artifact@v3 + with: + path: public/ + + deploy: + needs: build + + # Grant GITHUB_TOKEN the permissions required to make a Pages deployment + permissions: + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source + + # Deploy to the github-pages environment + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + uses: actions/deploy-pages@v4 diff --git a/website/package.json b/website/package.json index ee8a1c8812..f502431748 100644 --- a/website/package.json +++ b/website/package.json @@ -1,7 +1,8 @@ { - "devDependencies": { - "rustywind": "^0.24", - "tailwindcss": "^4.1", + "dependencies": { "@tailwindcss/cli": "^4.1" + }, + "devDependencies": { + "rustywind": "^0.24" } } From 47e490a997c37cb28ab4022c04155049b0b26f32 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 17 May 2025 22:33:17 +0200 Subject: [PATCH 1398/1432] Run rustywind --- website/templates/404.html | 2 +- website/templates/base.html | 10 +++++----- website/templates/page.html | 2 +- website/templates/shortcodes/details.html | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/website/templates/404.html b/website/templates/404.html index 234eb46f41..eb9d4691a7 100644 --- a/website/templates/404.html +++ b/website/templates/404.html @@ -5,7 +5,7 @@

DON'T PANIC!

404: Page not found!

- diff --git a/website/templates/base.html b/website/templates/base.html index 91b6cee8ca..1a55aebf67 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -47,8 +47,8 @@ {% if permalink %}{% endif %} - -
+ +
From edc8528ddef1b27952b89d6943ccd4bb3bf1a0f1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 19 May 2025 18:20:34 +0200 Subject: [PATCH 1399/1432] Improve CI --- .github/workflows/rust.yml | 9 ++++++++- .github/workflows/website.yml | 16 ++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2446939933..0317f35146 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -3,8 +3,14 @@ name: Check on: push: branches: [main] + paths-ignore: + - website + - '*.md' pull_request: branches: [main] + paths-ignore: + - website + - '*.md' env: CARGO_TERM_COLOR: always @@ -14,7 +20,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: cargo clippy -- --deny warnings + - name: Clippy + run: cargo clippy -- --deny warnings fmt: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 355d40bd45..4f758aa036 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -3,25 +3,24 @@ name: Website on: push: branches: [main] + paths: [website] jobs: - # Build & upload the static files as an artifact build: defaults: run: working-directory: website - runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Install Zola - run: sudo snap install zola - name: Install TailwindCSS - run: npm install @tailwindcss/cli + run: npm install - name: Build CSS - - run: npx @tailwindcss/cli -m -i input.css -o static/main.css + run: npx @tailwindcss/cli -m -i input.css -o static/main.css + - name: Download Zola + run: curl -fsSL https://github.com/getzola/zola/releases/download/v0.20.0/zola-v0.20.0-x86_64-unknown-linux-gnu.tar.gz | tar xz - name: Build site - run: zola build + run: ./zola build - name: Upload static files as artifact uses: actions/upload-pages-artifact@v3 with: @@ -29,17 +28,14 @@ jobs: deploy: needs: build - # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: pages: write # to deploy to Pages id-token: write # to verify the deployment originates from an appropriate source - # Deploy to the github-pages environment environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages From e36dd7a1207ccf940ba820abc276fa465f45b531 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 19 May 2025 18:21:40 +0200 Subject: [PATCH 1400/1432] Update MSRV in the release hook --- release-hook.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release-hook.sh b/release-hook.sh index db3d86eecd..42135369ab 100755 --- a/release-hook.sh +++ b/release-hook.sh @@ -9,8 +9,8 @@ cargo upgrades # Similar to CI cargo clippy -- --deny warnings cargo fmt --all --check -cargo test --workspace --all-targets -cargo run -- dev check --require-solutions +cargo test --workspace +cargo dev check --require-solutions # MSRV -cargo +1.85 run -- dev check --require-solutions +cargo +1.87 dev check --require-solutions From 04520ae7ad99eb9f6fd75438d4f829b4e6e546a2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 19 May 2025 18:30:40 +0200 Subject: [PATCH 1401/1432] Use the website link as header --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index bb3fe212ae..07d163dc72 100644 --- a/README.md +++ b/README.md @@ -1,3 +1 @@ -# Rustlings πŸ¦€β€οΈ - -➑️ [**rustlings.rust-lang.org**](https://rustlings.rust-lang.org) ⬅️ +# ➑️ [rustlings.rust-lang.org](https://rustlings.rust-lang.org) ⬅️ From f80c2edc3d7e5320ae74e356f6a1d132d7a2796e Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 23 May 2025 13:17:04 +0200 Subject: [PATCH 1402/1432] Remove fnm --- website/justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/justfile b/website/justfile index 6d41913827..7efc3ef9ca 100644 --- a/website/justfile +++ b/website/justfile @@ -2,4 +2,4 @@ zola: zola serve --open tailwind: - fnm exec --using latest npx @tailwindcss/cli -w -i input.css -o static/main.css + npx @tailwindcss/cli -w -i input.css -o static/main.css From adf3ddd968cd076e98c39a383ed79537985eff77 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 23 May 2025 13:14:47 +0200 Subject: [PATCH 1403/1432] Remove rustywind from dev deps --- website/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/website/package.json b/website/package.json index f502431748..38dd27e995 100644 --- a/website/package.json +++ b/website/package.json @@ -1,8 +1,5 @@ { "dependencies": { "@tailwindcss/cli": "^4.1" - }, - "devDependencies": { - "rustywind": "^0.24" } } From bf74a3d0a7ae3c0d12ba572fe1c2f1d711c6e530 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 23 May 2025 13:21:05 +0200 Subject: [PATCH 1404/1432] Update the README with more context before the website link --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 07d163dc72..0ae6265de9 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# ➑️ [rustlings.rust-lang.org](https://rustlings.rust-lang.org) ⬅️ +# [Rustlings](https://rustlings.rust-lang.org) πŸ¦€ + +Small exercises to get you used to reading and writing [Rust](https://www.rust-lang.org) code - _Recommended in parallel to reading [the official Rust book](https://doc.rust-lang.org/book) πŸ“šοΈ_ + +Visit the **website** for a demo, info about setup and more: + +## ➑️ [rustlings.rust-lang.org](https://rustlings.rust-lang.org) ⬅️ From 520dfdc464f6a57fa4ab85c6c48fabce648a3912 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 23 May 2025 13:33:51 +0200 Subject: [PATCH 1405/1432] Add workflow_dispatch to website workflow --- .github/workflows/website.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 4f758aa036..60f92a4811 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -1,6 +1,7 @@ name: Website on: + workflow_dispatch: push: branches: [main] paths: [website] From 734fc482ebd4c860320bf2664229ce385dabfc30 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 23 May 2025 13:37:10 +0200 Subject: [PATCH 1406/1432] Make path relative --- .github/workflows/website.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 60f92a4811..f4ada33a8a 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -25,7 +25,7 @@ jobs: - name: Upload static files as artifact uses: actions/upload-pages-artifact@v3 with: - path: public/ + path: ./public/ deploy: needs: build From 46814d397a1e6314e22f747922dd77c43c3854a6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 23 May 2025 13:44:29 +0200 Subject: [PATCH 1407/1432] Fix path in action --- .github/workflows/website.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index f4ada33a8a..936cd562e5 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -25,7 +25,7 @@ jobs: - name: Upload static files as artifact uses: actions/upload-pages-artifact@v3 with: - path: ./public/ + path: website/public/ deploy: needs: build From cb60c8887cf7554e9ad258a6afb1f81cf92f62e4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 3 Jun 2025 10:29:58 +0200 Subject: [PATCH 1408/1432] Update deps --- Cargo.lock | 63 ++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c285a52a79..7ea1217048 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,18 +38,18 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys", ] [[package]] @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstream", "anstyle", @@ -176,7 +176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -194,7 +194,7 @@ dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -323,9 +323,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -345,14 +345,14 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -371,7 +371,7 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -386,11 +386,17 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -398,9 +404,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -452,7 +458,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -605,7 +611,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -689,7 +695,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -698,15 +704,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.59.0" From 278edc0b962cf03d5eef214063d720279e72953a Mon Sep 17 00:00:00 2001 From: zeonzip <96481337+zeonzip@users.noreply.github.com> Date: Fri, 13 Jun 2025 12:24:09 +0200 Subject: [PATCH 1409/1432] Clarify how to find return type of error4 --- exercises/13_error_handling/errors4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index ba01e54bf5..144fce7b22 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -10,6 +10,7 @@ struct PositiveNonzeroInteger(u64); impl PositiveNonzeroInteger { fn new(value: i64) -> Result { // TODO: This function shouldn't always return an `Ok`. + // Read the tests below to clarify what should be returned. Ok(Self(value as u64)) } } From 57b3727b3e69d50c0b72aa41d79b8d16d47d58fa Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 28 Jun 2025 01:31:49 +0200 Subject: [PATCH 1410/1432] Don't ignore `.rustlings-state.txt` in `.gitignore` of `rustlings dev new` --- src/dev/new.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dev/new.rs b/src/dev/new.rs index 883b6fa777..7c72a6b700 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -78,8 +78,7 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { Ok(()) } -pub const GITIGNORE: &[u8] = b".rustlings-state.txt -Cargo.lock +pub const GITIGNORE: &[u8] = b"Cargo.lock target/ .vscode/ !.vscode/extensions.json From e8da6869f8e0a051e8768135b57950e9f8ed27b9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 28 Jun 2025 01:19:44 +0200 Subject: [PATCH 1411/1432] Don't run rustfmt during `dev check` if there are no solutions --- src/dev/check.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 9cde7f24cc..6ea8d89a68 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -354,16 +354,18 @@ fn check_solutions( } stdout.write_all(b"\n")?; + let n_solutions = sol_paths.len(); let handle = thread::Builder::new() .spawn(move || check_unexpected_files("solutions", &sol_paths)) .context( "Failed to spawn a thread to check for unexpected files in the solutions directory", )?; - if !fmt_cmd - .status() - .context("Failed to run `rustfmt` on all solution files")? - .success() + if n_solutions > 0 + && !fmt_cmd + .status() + .context("Failed to run `rustfmt` on all solution files")? + .success() { bail!("Some solutions aren't formatted. Run `rustfmt` on them"); } From 7af38e684d58562762924dd9d5027fd92e1dfaa8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 28 Jun 2025 01:44:36 +0200 Subject: [PATCH 1412/1432] Print newline after progress on failure --- src/dev/check.rs | 23 ++++++----------------- src/term.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/dev/check.rs b/src/dev/check.rs index 6ea8d89a68..67f54930fd 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -15,6 +15,7 @@ use crate::{ cmd::CmdRunner, exercise::{OUTPUT_CAPACITY, RunnableExercise}, info_file::{ExerciseInfo, InfoFile}, + term::ProgressCounter, }; const MAX_N_EXERCISES: usize = 999; @@ -217,10 +218,7 @@ fn check_exercises_unsolved( .collect::, _>>() .context("Failed to spawn a thread to check if an exercise is already solved")?; - let n_handles = handles.len(); - write!(stdout, "Progress: 0/{n_handles}")?; - stdout.flush()?; - let mut handle_num = 1; + let mut progress_counter = ProgressCounter::new(&mut stdout, handles.len())?; for (exercise_name, handle) in handles { let Ok(result) = handle.join() else { @@ -235,11 +233,8 @@ fn check_exercises_unsolved( Err(e) => return Err(e), } - write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; - stdout.flush()?; - handle_num += 1; + progress_counter.increment()?; } - stdout.write_all(b"\n")?; Ok(()) } @@ -318,10 +313,7 @@ fn check_solutions( .arg("always") .stdin(Stdio::null()); - let n_handles = handles.len(); - write!(stdout, "Progress: 0/{n_handles}")?; - stdout.flush()?; - let mut handle_num = 1; + let mut progress_counter = ProgressCounter::new(&mut stdout, handles.len())?; for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { let Ok(check_result) = handle.join() else { @@ -338,7 +330,7 @@ fn check_solutions( } SolutionCheck::MissingOptional => (), SolutionCheck::RunFailure { output } => { - stdout.write_all(b"\n\n")?; + drop(progress_counter); stdout.write_all(&output)?; bail!( "Running the solution of the exercise {} failed with the error above", @@ -348,11 +340,8 @@ fn check_solutions( SolutionCheck::Err(e) => return Err(e), } - write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; - stdout.flush()?; - handle_num += 1; + progress_counter.increment()?; } - stdout.write_all(b"\n")?; let n_solutions = sol_paths.len(); let handle = thread::Builder::new() diff --git a/src/term.rs b/src/term.rs index 1e08c84f64..fe188c0554 100644 --- a/src/term.rs +++ b/src/term.rs @@ -160,6 +160,38 @@ impl<'a, 'lock> CheckProgressVisualizer<'a, 'lock> { } } +pub struct ProgressCounter<'a, 'lock> { + stdout: &'a mut StdoutLock<'lock>, + total: usize, + counter: usize, +} + +impl<'a, 'lock> ProgressCounter<'a, 'lock> { + pub fn new(stdout: &'a mut StdoutLock<'lock>, total: usize) -> io::Result { + write!(stdout, "Progress: 0/{total}")?; + stdout.flush()?; + + Ok(Self { + stdout, + total, + counter: 0, + }) + } + + pub fn increment(&mut self) -> io::Result<()> { + self.counter += 1; + write!(self.stdout, "\rProgress: {}/{}", self.counter, self.total)?; + self.stdout.flush() + } +} + +impl Drop for ProgressCounter<'_, '_> { + fn drop(&mut self) { + let _ = self.stdout.write_all(b"\n\n"); + let _ = self.stdout.flush(); + } +} + pub fn progress_bar<'a>( writer: &mut impl CountedWrite<'a>, progress: u16, From 9fecdba101a60dafecd0754cb1276dacf489a539 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 28 Jun 2025 02:14:55 +0200 Subject: [PATCH 1413/1432] No need to flush after printing newlines --- src/term.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/term.rs b/src/term.rs index fe188c0554..b7dcd9f101 100644 --- a/src/term.rs +++ b/src/term.rs @@ -188,7 +188,6 @@ impl<'a, 'lock> ProgressCounter<'a, 'lock> { impl Drop for ProgressCounter<'_, '_> { fn drop(&mut self) { let _ = self.stdout.write_all(b"\n\n"); - let _ = self.stdout.flush(); } } From 1a633e275761604c8225839e79c139c90d59bfea Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 4 Jul 2025 23:17:25 +0200 Subject: [PATCH 1414/1432] Split lines after newline --- src/cargo_toml.rs | 9 ++++++++- src/dev/check.rs | 17 ++++++++++++----- src/init.rs | 12 ++++++++---- src/main.rs | 6 +++++- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index e966809f35..ce0dfd0cf3 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -134,7 +134,14 @@ mod tests { ); assert_eq!( - updated_cargo_toml(&exercise_infos, "abc\nbin = [xxx]\n123", b"../").unwrap(), + updated_cargo_toml( + &exercise_infos, + "abc\n\ + bin = [xxx]\n\ + 123", + b"../" + ) + .unwrap(), br#"abc bin = [ { name = "1", path = "../exercises/1.rs" }, diff --git a/src/dev/check.rs b/src/dev/check.rs index 67f54930fd..f71110630d 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -106,13 +106,15 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result> { 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" + "The `main` function is missing in the file `{path}`.\n\ + Create at least an empty `main` function to avoid language server errors" ); } if !file_buf.contains("// TODO") { bail!( - "Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user." + "Didn't find any `// TODO` comment in the file `{path}`.\n\ + You need to have at least one such comment to guide the user." ); } @@ -227,7 +229,10 @@ fn check_exercises_unsolved( match result { Ok(true) => { - bail!("The exercise {exercise_name} is already solved.\n{SKIP_CHECK_UNSOLVED_HINT}",) + bail!( + "The exercise {exercise_name} is already solved.\n\ + {SKIP_CHECK_UNSOLVED_HINT}", + ) } Ok(false) => (), Err(e) => return Err(e), @@ -242,10 +247,12 @@ fn check_exercises_unsolved( fn check_exercises(info_file: &'static InfoFile, cmd_runner: &'static CmdRunner) -> 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" + "`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\n\ + Please migrate to the latest format version" ), Ordering::Greater => bail!( - "`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\nTry updating the Rustlings program" + "`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\n\ + Try updating the Rustlings program" ), Ordering::Equal => (), } diff --git a/src/init.rs b/src/init.rs index a60fba70df..8cb6ac55ee 100644 --- a/src/init.rs +++ b/src/init.rs @@ -58,11 +58,13 @@ pub fn init() -> Result<()> { && !workspace_manifest_content.contains("workspace.") { bail!( - "The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory" + "The current directory is already part of a Cargo project.\n\ + Please initialize Rustlings in a different directory" ); } - stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\nPress ENTER to continue ")?; + stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\n\ + Press ENTER to continue ")?; press_enter_prompt(&mut stdout)?; // Make sure "rustlings" is added to `workspace.members` by making @@ -78,7 +80,8 @@ pub fn init() -> Result<()> { .status()?; if !status.success() { bail!( - "Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory" + "Failed to initialize a new Cargo workspace member.\n\ + Please initialize Rustlings in a different directory" ); } @@ -87,7 +90,8 @@ pub fn init() -> Result<()> { .context("Failed to remove the temporary directory `rustlings/`")?; init_git = false; } else { - stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; + stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\n\ + Press ENTER to continue ")?; press_enter_prompt(&mut stdout)?; } diff --git a/src/main.rs b/src/main.rs index bce2593dc6..29de56b3d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -104,7 +104,11 @@ fn main() -> Result { clear_terminal(&mut stdout)?; let welcome_message = welcome_message.trim_ascii(); - write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; + write!( + stdout, + "{welcome_message}\n\n\ + Press ENTER to continue " + )?; press_enter_prompt(&mut stdout)?; clear_terminal(&mut stdout)?; // Flush to be able to show errors occurring before printing a newline to stdout. From f24861957a0c669060473a1fe7c6180eb43df9e2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 4 Jul 2025 23:23:08 +0200 Subject: [PATCH 1415/1432] Check for Clippy on init --- src/init.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/init.rs b/src/init.rs index 8cb6ac55ee..68011ed4e9 100644 --- a/src/init.rs +++ b/src/init.rs @@ -35,7 +35,27 @@ pub fn init() -> Result<()> { .stdin(Stdio::null()) .stderr(Stdio::null()) .output() - .context(CARGO_LOCATE_PROJECT_ERR)?; + .context( + "Failed to run the command `cargo locate-project …`\n\ + Did you already install Rust?\n\ + Try running `cargo --version` to diagnose the problem.", + )?; + + if !Command::new("cargo") + .arg("clippy") + .arg("--version") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .context("Failed to run the command `cargo clippy --version`")? + .success() + { + bail!( + "Clippy, the official Rust linter, is missing.\n\ + Please install it first before initializing Rustlings." + ) + } let mut stdout = io::stdout().lock(); let mut init_git = true; @@ -170,10 +190,6 @@ pub fn init() -> Result<()> { Ok(()) } -const CARGO_LOCATE_PROJECT_ERR: &str = "Failed to run the command `cargo locate-project …` -Did you already install Rust? -Try running `cargo --version` to diagnose the problem."; - const INIT_SOLUTION_FILE: &[u8] = b"fn main() { // DON'T EDIT THIS SOLUTION FILE! // It will be automatically filled after you finish the exercise. From 3a2fe2c39472f780edb519f040427b9380617332 Mon Sep 17 00:00:00 2001 From: Hud Miller Date: Fri, 18 Jul 2025 13:02:56 -0500 Subject: [PATCH 1416/1432] Fix incorrect book chapter number --- exercises/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/README.md b/exercises/README.md index 237f2f1edc..86b35916bc 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -22,6 +22,6 @@ | iterators | Β§13.2-4 | | smart_pointers | Β§15, Β§16.3 | | threads | Β§16.1-3 | -| macros | Β§19.5 | +| macros | Β§20.5 | | clippy | Β§21.4 | | conversions | n/a | From 4f9f0907c3a15c9f26d6675e65832a382419e8a2 Mon Sep 17 00:00:00 2001 From: deafloo Date: Mon, 21 Jul 2025 12:13:32 +0200 Subject: [PATCH 1417/1432] Add positive tests This prevents solving the exercise without a real IntegerOverflow --- exercises/18_iterators/iterators3.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 6b1eca1734..dce09055dd 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -39,6 +39,8 @@ mod tests { #[test] fn test_success() { assert_eq!(divide(81, 9), Ok(9)); + assert_eq!(divide(81, -1), Ok(-81)); + assert_eq!(divide(i64::MIN, i64::MIN), Ok(1)); } #[test] From a712e484d09ce27a622da5e61d26bbb1004f51d2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 18 Aug 2025 11:34:36 +0200 Subject: [PATCH 1418/1432] Update deps --- Cargo.lock | 324 ++++++++++++++++++++++-------------- Cargo.toml | 5 +- clippy.toml | 16 +- rustlings-macros/Cargo.toml | 2 +- rustlings-macros/src/lib.rs | 2 +- src/embedded.rs | 2 +- src/info_file.rs | 4 +- 7 files changed, 210 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ea1217048..a0c2f08051 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -19,50 +19,50 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" @@ -72,21 +72,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "clap" -version = "4.5.39" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", @@ -118,15 +118,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "crossterm" @@ -134,7 +134,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "crossterm_winapi", "document-features", "mio", @@ -171,12 +171,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -185,18 +185,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys", -] - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -220,9 +208,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" @@ -232,9 +220,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -246,7 +234,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "inotify-sys", "libc", ] @@ -294,20 +282,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" - -[[package]] -name = "libredox" -version = "0.1.3" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.9.1", - "libc", - "redox_syscall", -] +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linux-raw-sys" @@ -317,9 +294,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock_api" @@ -339,9 +316,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mio" @@ -351,18 +328,17 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] name = "notify" -version = "8.0.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.9.1", - "filetime", + "bitflags 2.9.2", "fsevent-sys", "inotify", "kqueue", @@ -371,7 +347,7 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -412,14 +388,14 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -435,30 +411,30 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -474,7 +450,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "toml_edit", + "toml", ] [[package]] @@ -483,7 +459,7 @@ version = "6.4.0" dependencies = [ "quote", "serde", - "toml_edit", + "toml", ] [[package]] @@ -529,9 +505,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -541,9 +517,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] @@ -571,18 +547,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "strsim" @@ -592,9 +568,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -611,31 +587,48 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] -name = "toml_datetime" -version = "0.6.9" +name = "toml" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ + "indexmap", "serde", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] -name = "toml_edit" -version = "0.22.26" +name = "toml_datetime" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ - "indexmap", "serde", - "serde_spanned", - "toml_datetime", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ "winnow", ] +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -660,9 +653,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -695,7 +688,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -704,13 +697,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -719,14 +727,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -735,56 +760,101 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" -dependencies = [ - "memchr", -] +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" [[package]] name = "wit-bindgen-rt" @@ -792,5 +862,5 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] diff --git a/Cargo.toml b/Cargo.toml index 27531b37bf..56adbb5cb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [workspace] -resolver = "2" exclude = [ "tests/test_exercises", "dev", @@ -20,7 +19,7 @@ rust-version = "1.87" [workspace.dependencies] serde = { version = "1.0", features = ["derive"] } -toml_edit = { version = "0.22", default-features = false, features = ["parse", "serde"] } +toml = { version = "0.9", default-features = false, features = ["std", "parse", "serde"] } [package] name = "rustlings" @@ -53,7 +52,7 @@ notify = "8.0" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } serde_json = "1.0" serde.workspace = true -toml_edit.workspace = true +toml.workspace = true [target.'cfg(not(windows))'.dependencies] rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] } diff --git a/clippy.toml b/clippy.toml index afc9253a51..89b0a886f7 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,15 +1,11 @@ disallowed-types = [ - # Inefficient. Use `.queue(…)` instead. - "crossterm::style::Stylize", - "crossterm::style::styled_content::StyledContent", + { path = "crossterm::style::Stylize", reason = "inefficient, use `.queue(…)` instead" }, + { path = "crossterm::style::styled_content::StyledContent", reason = "inefficient, use `.queue(…)` instead" }, ] disallowed-methods = [ - # Inefficient. Use `.queue(…)` instead. - "crossterm::style::style", - # Use `thread::Builder::spawn` instead and handle the error. - "std::thread::spawn", - "std::thread::Scope::spawn", - # Return `ExitCode` instead. - "std::process::exit", + { path = "crossterm::style::style", reason = "inefficient, use `.queue(…)` instead" }, + { path = "std::thread::spawn", replacement = "std::thread::Builder::spawn", reason = "handle the error" }, + { path = "std::thread::Scope::spawn", replacement = "std::thread::Builder::spawn", reason = "handle the error" }, + { path = "std::process::exit", replacement = "std::process::ExitCode" }, ] diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 1bf6d1b8c7..5df648b2d0 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true [dependencies] quote = "1.0" serde.workspace = true -toml_edit.workspace = true +toml.workspace = true [lints] workspace = true diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 6c6067bc9d..b20c6f1d90 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -16,7 +16,7 @@ struct InfoFile { #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { let info_file = include_str!("../info.toml"); - let exercises = toml_edit::de::from_str::(info_file) + let exercises = toml::de::from_str::(info_file) .expect("Failed to parse `info.toml`") .exercises; diff --git a/src/embedded.rs b/src/embedded.rs index 51a14b6a8d..88c1fb0139 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -152,7 +152,7 @@ mod tests { #[test] fn dirs() { - let exercises = toml_edit::de::from_str::(EMBEDDED_FILES.info_file) + let exercises = toml::de::from_str::(EMBEDDED_FILES.info_file) .expect("Failed to parse `info.toml`") .exercises; diff --git a/src/info_file.rs b/src/info_file.rs index 634bece9a8..04e5d644a2 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -95,11 +95,11 @@ impl InfoFile { pub fn parse() -> Result { // Read a local `info.toml` if it exists. let slf = match fs::read_to_string("info.toml") { - Ok(file_content) => toml_edit::de::from_str::(&file_content) + Ok(file_content) => toml::de::from_str::(&file_content) .context("Failed to parse the `info.toml` file")?, Err(e) => { if e.kind() == ErrorKind::NotFound { - return toml_edit::de::from_str(EMBEDDED_FILES.info_file) + return toml::de::from_str(EMBEDDED_FILES.info_file) .context("Failed to parse the embedded `info.toml` file"); } From 2d1d531550afaac36dbf4d15c383bc0e1cbf248d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 21 Aug 2025 22:43:46 +0200 Subject: [PATCH 1419/1432] Fix file links in VS Code --- src/app_state.rs | 12 ++++++------ src/exercise.rs | 38 +++++++++++++++++++++++++------------- src/list/state.rs | 8 +------- src/main.rs | 2 +- src/run.rs | 6 +++--- src/term.rs | 29 +++++++++++++++++++---------- src/watch/state.rs | 4 ++-- 7 files changed, 57 insertions(+), 42 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index f3f348133e..d654d0425d 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -60,8 +60,7 @@ pub struct AppState { file_buf: Vec, official_exercises: bool, cmd_runner: CmdRunner, - // Running in VS Code. - vs_code: bool, + emit_file_links: bool, } impl AppState { @@ -181,7 +180,8 @@ impl AppState { file_buf, official_exercises: !Path::new("info.toml").exists(), cmd_runner, - vs_code: env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode"), + // VS Code has its own file link handling + emit_file_links: env::var_os("TERM_PROGRAM").is_none_or(|v| v != "vscode"), }; Ok((slf, state_file_status)) @@ -218,8 +218,8 @@ impl AppState { } #[inline] - pub fn vs_code(&self) -> bool { - self.vs_code + pub fn emit_file_links(&self) -> bool { + self.emit_file_links } // Write the state file. @@ -621,7 +621,7 @@ mod tests { file_buf: Vec::new(), official_exercises: true, cmd_runner: CmdRunner::build().unwrap(), - vs_code: false, + emit_file_links: true, }; let mut assert = |done: [bool; 3], expected: [Option; 3]| { diff --git a/src/exercise.rs b/src/exercise.rs index fdfbc4f6ea..6f517bee7c 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -7,22 +7,28 @@ use std::io::{self, StdoutLock, Write}; use crate::{ cmd::CmdRunner, - term::{self, CountedWrite, terminal_file_link, write_ansi}, + term::{self, CountedWrite, file_path, terminal_file_link, write_ansi}, }; /// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; -pub fn solution_link_line(stdout: &mut StdoutLock, solution_path: &str) -> io::Result<()> { +pub fn solution_link_line( + stdout: &mut StdoutLock, + solution_path: &str, + emit_file_links: bool, +) -> io::Result<()> { stdout.queue(SetAttribute(Attribute::Bold))?; stdout.write_all(b"Solution")?; stdout.queue(ResetColor)?; stdout.write_all(b" for comparison: ")?; - if let Some(canonical_path) = term::canonicalize(solution_path) { - terminal_file_link(stdout, solution_path, &canonical_path, Color::Cyan)?; - } else { - stdout.write_all(solution_path.as_bytes())?; - } + file_path(stdout, Color::Cyan, |writer| { + if emit_file_links && let Some(canonical_path) = term::canonicalize(solution_path) { + terminal_file_link(writer, solution_path, &canonical_path) + } else { + writer.stdout().write_all(solution_path.as_bytes()) + } + })?; stdout.write_all(b"\n") } @@ -72,12 +78,18 @@ pub struct Exercise { } impl Exercise { - pub fn terminal_file_link<'a>(&self, writer: &mut impl CountedWrite<'a>) -> io::Result<()> { - if let Some(canonical_path) = self.canonical_path.as_deref() { - return terminal_file_link(writer, self.path, canonical_path, Color::Blue); - } - - writer.write_str(self.path) + pub fn terminal_file_link<'a>( + &self, + writer: &mut impl CountedWrite<'a>, + emit_file_links: bool, + ) -> io::Result<()> { + file_path(writer, Color::Blue, |writer| { + if emit_file_links && let Some(canonical_path) = self.canonical_path.as_deref() { + terminal_file_link(writer, self.path, canonical_path) + } else { + writer.write_str(self.path) + } + }) } } diff --git a/src/list/state.rs b/src/list/state.rs index ae65ec2be9..50d06be9cd 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -186,13 +186,7 @@ impl<'a> ListState<'a> { writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?; - // The list links aren't shown correctly in VS Code on Windows. - // But VS Code shows its own links anyway. - if self.app_state.vs_code() { - writer.write_str(exercise.path)?; - } else { - exercise.terminal_file_link(&mut writer)?; - } + exercise.terminal_file_link(&mut writer, self.app_state.emit_file_links())?; writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?; diff --git a/src/main.rs b/src/main.rs index 29de56b3d2..ffd2dfa75b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -167,7 +167,7 @@ fn main() -> Result { } app_state .current_exercise() - .terminal_file_link(&mut stdout)?; + .terminal_file_link(&mut stdout, app_state.emit_file_links())?; stdout.write_all(b"\n")?; return Ok(ExitCode::FAILURE); diff --git a/src/run.rs b/src/run.rs index 6f4f099b47..b473fc2062 100644 --- a/src/run.rs +++ b/src/run.rs @@ -27,7 +27,7 @@ pub fn run(app_state: &mut AppState) -> Result { stdout.write_all(b"Ran ")?; app_state .current_exercise() - .terminal_file_link(&mut stdout)?; + .terminal_file_link(&mut stdout, app_state.emit_file_links())?; stdout.write_all(b" with errors\n")?; return Ok(ExitCode::FAILURE); @@ -41,7 +41,7 @@ pub fn run(app_state: &mut AppState) -> Result { if let Some(solution_path) = app_state.current_solution_path()? { stdout.write_all(b"\n")?; - solution_link_line(&mut stdout, &solution_path)?; + solution_link_line(&mut stdout, &solution_path, app_state.emit_file_links())?; stdout.write_all(b"\n")?; } @@ -50,7 +50,7 @@ pub fn run(app_state: &mut AppState) -> Result { stdout.write_all(b"Next exercise: ")?; app_state .current_exercise() - .terminal_file_link(&mut stdout)?; + .terminal_file_link(&mut stdout, app_state.emit_file_links())?; stdout.write_all(b"\n")?; } ExercisesProgress::AllDone => (), diff --git a/src/term.rs b/src/term.rs index b7dcd9f101..3d149b33e8 100644 --- a/src/term.rs +++ b/src/term.rs @@ -272,22 +272,18 @@ pub fn canonicalize(path: &str) -> Option { }) } -pub fn terminal_file_link<'a>( - writer: &mut impl CountedWrite<'a>, - path: &str, - canonical_path: &str, +pub fn file_path<'a, W: CountedWrite<'a>>( + writer: &mut W, color: Color, + f: impl FnOnce(&mut W) -> io::Result<()>, ) -> io::Result<()> { writer .stdout() .queue(SetForegroundColor(color))? .queue(SetAttribute(Attribute::Underlined))?; - writer.stdout().write_all(b"\x1b]8;;file://")?; - writer.stdout().write_all(canonical_path.as_bytes())?; - writer.stdout().write_all(b"\x1b\\")?; - // Only this part is visible. - writer.write_str(path)?; - writer.stdout().write_all(b"\x1b]8;;\x1b\\")?; + + f(writer)?; + writer .stdout() .queue(SetForegroundColor(Color::Reset))? @@ -296,6 +292,19 @@ pub fn terminal_file_link<'a>( Ok(()) } +pub fn terminal_file_link<'a>( + writer: &mut impl CountedWrite<'a>, + path: &str, + canonical_path: &str, +) -> io::Result<()> { + writer.stdout().write_all(b"\x1b]8;;file://")?; + writer.stdout().write_all(canonical_path.as_bytes())?; + writer.stdout().write_all(b"\x1b\\")?; + // Only this part is visible. + writer.write_str(path)?; + writer.stdout().write_all(b"\x1b]8;;\x1b\\") +} + pub fn write_ansi(output: &mut Vec, command: impl Command) { struct FmtWriter<'a>(&'a mut Vec); diff --git a/src/watch/state.rs b/src/watch/state.rs index 2413becd28..a92dd2d6d7 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -233,7 +233,7 @@ impl<'a> WatchState<'a> { stdout.write_all(b"\n")?; if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { - solution_link_line(stdout, solution_path)?; + solution_link_line(stdout, solution_path, self.app_state.emit_file_links())?; } stdout.write_all( @@ -252,7 +252,7 @@ impl<'a> WatchState<'a> { stdout.write_all(b"\nCurrent exercise: ")?; self.app_state .current_exercise() - .terminal_file_link(stdout)?; + .terminal_file_link(stdout, self.app_state.emit_file_links())?; stdout.write_all(b"\n\n")?; self.show_prompt(stdout)?; From 208a5932165c2fcf74b1dbb602f9427cc302929e Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 21 Aug 2025 22:08:54 +0200 Subject: [PATCH 1420/1432] Ready to release --- CHANGELOG.md | 14 ++++++++++++++ Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1dbb42bbe..32d95107cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,23 @@ ## Unreleased +## 6.5.0 (2025-08-21) + +### Added + +- Check that Clippy is installed before initialization + ### Changed - Upgrade to Rust edition 2024 - Raise the minimum supported Rust version to `1.87` +- Don't follow symlinks in the file watcher +- `dev new`: Don't add `.rustlings-state.txt` to `.gitignore` + +### Fixed + +- Fix file links in VS Code +- Fix error printing when the progress bar is shown +- `dev check`: Don't check formatting if there are no solution files ## 6.4.0 (2024-11-11) diff --git a/Cargo.lock b/Cargo.lock index a0c2f08051..c743dd7fb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,9 +78,9 @@ checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "clap" @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -579,15 +579,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -684,11 +684,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 56adbb5cb9..454f3b8018 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ toml.workspace = true rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.19" +tempfile = "3.21" [profile.release] panic = "abort" From b6b94e3e96dc71099055336e0b742e981c730157 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 21 Aug 2025 23:34:45 +0200 Subject: [PATCH 1421/1432] Sync solution --- solutions/18_iterators/iterators3.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs index 11aa1ec8ce..1d5d67f29f 100644 --- a/solutions/18_iterators/iterators3.rs +++ b/solutions/18_iterators/iterators3.rs @@ -52,6 +52,8 @@ mod tests { #[test] fn test_success() { assert_eq!(divide(81, 9), Ok(9)); + assert_eq!(divide(81, -1), Ok(-81)); + assert_eq!(divide(i64::MIN, i64::MIN), Ok(1)); } #[test] From 628ef55337429c26df53f4bec823653d6944dddb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 21 Aug 2025 23:38:42 +0200 Subject: [PATCH 1422/1432] Fix Clippy chapter --- exercises/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/README.md b/exercises/README.md index 86b35916bc..1df5cc3757 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -23,5 +23,5 @@ | smart_pointers | Β§15, Β§16.3 | | threads | Β§16.1-3 | | macros | Β§20.5 | -| clippy | Β§21.4 | +| clippy | Appendix D | | conversions | n/a | From 295ad2e4bd41147bf10028800a82247c2c0048a4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 21 Aug 2025 23:55:47 +0200 Subject: [PATCH 1423/1432] Raise MSRV --- CHANGELOG.md | 2 +- Cargo.toml | 2 +- release-hook.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32d95107cc..18e0aa660c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ ### Changed - Upgrade to Rust edition 2024 -- Raise the minimum supported Rust version to `1.87` +- Raise the minimum supported Rust version to `1.88` - Don't follow symlinks in the file watcher - `dev new`: Don't add `.rustlings-state.txt` to `.gitignore` diff --git a/Cargo.toml b/Cargo.toml index 454f3b8018..764b6b6657 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ authors = [ repository = "https://github.com/rust-lang/rustlings" license = "MIT" edition = "2024" # On Update: Update the edition of `rustfmt` in `dev check` and `CARGO_TOML` in `dev new`. -rust-version = "1.87" +rust-version = "1.88" [workspace.dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/release-hook.sh b/release-hook.sh index 42135369ab..4934933793 100755 --- a/release-hook.sh +++ b/release-hook.sh @@ -13,4 +13,4 @@ cargo test --workspace cargo dev check --require-solutions # MSRV -cargo +1.87 dev check --require-solutions +cargo +1.88 dev check --require-solutions From 6ec2e194ae606ae4409a626ea0ac025229f6f4b1 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 22 Aug 2025 00:01:03 +0200 Subject: [PATCH 1424/1432] Apply Clippy lints --- src/embedded.rs | 8 ++++---- src/exercise.rs | 22 +++++++++++----------- src/list/state.rs | 17 ++++++++--------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/embedded.rs b/src/embedded.rs index 88c1fb0139..61a5f581e2 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -20,10 +20,10 @@ struct ExerciseFiles { } fn create_dir_if_not_exists(path: &str) -> Result<()> { - if let Err(e) = create_dir(path) { - if e.kind() != io::ErrorKind::AlreadyExists { - return Err(Error::from(e).context(format!("Failed to create the directory {path}"))); - } + if let Err(e) = create_dir(path) + && e.kind() != io::ErrorKind::AlreadyExists + { + return Err(Error::from(e).context(format!("Failed to create the directory {path}"))); } Ok(()) diff --git a/src/exercise.rs b/src/exercise.rs index 6f517bee7c..a0596b5b9e 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -48,17 +48,17 @@ fn run_bin( let success = cmd_runner.run_debug_bin(bin_name, output.as_deref_mut())?; - if let Some(output) = output { - if !success { - // This output is important to show the user that something went wrong. - // Otherwise, calling something like `exit(1)` in an exercise without further output - // leaves the user confused about why the exercise isn't done yet. - write_ansi(output, SetAttribute(Attribute::Bold)); - write_ansi(output, SetForegroundColor(Color::Red)); - output.extend_from_slice(b"The exercise didn't run successfully (nonzero exit code)"); - write_ansi(output, ResetColor); - output.push(b'\n'); - } + if let Some(output) = output + && !success + { + // This output is important to show the user that something went wrong. + // Otherwise, calling something like `exit(1)` in an exercise without further output + // leaves the user confused about why the exercise isn't done yet. + write_ansi(output, SetAttribute(Attribute::Bold)); + write_ansi(output, SetForegroundColor(Color::Red)); + output.extend_from_slice(b"The exercise didn't run successfully (nonzero exit code)"); + write_ansi(output, ResetColor); + output.push(b'\n'); } Ok(success) diff --git a/src/list/state.rs b/src/list/state.rs index 50d06be9cd..4fd1301d6b 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -118,8 +118,8 @@ impl<'a> ListState<'a> { } fn draw_exercise_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> { - if !self.search_query.is_empty() { - if let Some((pre_highlight, highlight, post_highlight)) = exercise + if !self.search_query.is_empty() + && let Some((pre_highlight, highlight, post_highlight)) = exercise .name .find(&self.search_query) .and_then(|ind| exercise.name.split_at_checked(ind)) @@ -127,13 +127,12 @@ impl<'a> ListState<'a> { rest.split_at_checked(self.search_query.len()) .map(|x| (pre_highlight, x.0, x.1)) }) - { - writer.write_str(pre_highlight)?; - writer.stdout.queue(SetForegroundColor(Color::Magenta))?; - writer.write_str(highlight)?; - writer.stdout.queue(SetForegroundColor(Color::Reset))?; - return writer.write_str(post_highlight); - } + { + writer.write_str(pre_highlight)?; + writer.stdout.queue(SetForegroundColor(Color::Magenta))?; + writer.write_str(highlight)?; + writer.stdout.queue(SetForegroundColor(Color::Reset))?; + return writer.write_str(post_highlight); } writer.write_str(exercise.name) From 2af9e89ba536fad01aa828b06e0ac2174bad0f6d Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 22 Aug 2025 00:05:12 +0200 Subject: [PATCH 1425/1432] chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c743dd7fb7..f883653fd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,7 +439,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.4.0" +version = "6.5.0" dependencies = [ "anyhow", "clap", @@ -455,7 +455,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.4.0" +version = "6.5.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 764b6b6657..4469b28112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ exclude = [ ] [workspace.package] -version = "6.4.0" +version = "6.5.0" authors = [ "Mo Bitar ", # https://github.com/mo8it "Liv ", # https://github.com/shadows-withal @@ -49,7 +49,7 @@ anyhow = "1.0" clap = { version = "4.5", features = ["derive"] } crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] } notify = "8.0" -rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.5.0" } serde_json = "1.0" serde.workspace = true toml.workspace = true From 95a597eb8236c5ab180ac38284a655533f83e715 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Tue, 23 Sep 2025 15:26:06 +0200 Subject: [PATCH 1426/1432] Fix workspace detection with windows line endings Some cargo workspaces may contain windows line endings. Even if the file is stored in a repo with unix line endings, users may have some setting activated that automatically translates them to windows line endings when working locally. --- src/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.rs b/src/init.rs index 68011ed4e9..16ea35e8b7 100644 --- a/src/init.rs +++ b/src/init.rs @@ -74,7 +74,7 @@ pub fn init() -> Result<()> { let workspace_manifest_content = fs::read_to_string(&workspace_manifest) .with_context(|| format!("Failed to read the file {}", workspace_manifest.display()))?; - if !workspace_manifest_content.contains("[workspace]\n") + if !workspace_manifest_content.contains("[workspace]") && !workspace_manifest_content.contains("workspace.") { bail!( From d8f4b06c91c54bccf934b84560641da3a7f202a8 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Wed, 24 Sep 2025 20:56:58 +0200 Subject: [PATCH 1427/1432] Remove use of `map` in early vecs2 exercise Students do not have the necessary knowledge at this point to understand what's happening with the iterator combinators. This topic is covered well by the dedicated exercises about iterators later. closes #2102 --- CHANGELOG.md | 4 ++++ exercises/05_vecs/vecs2.rs | 34 ---------------------------------- rustlings-macros/info.toml | 11 +---------- solutions/05_vecs/vecs2.rs | 30 ------------------------------ 4 files changed, 5 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18e0aa660c..2d2b4153d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +### Changed + +- `vecs2`: Removed the use of `map` and `collect`, which are only taught later. + ## 6.5.0 (2025-08-21) ### Added diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index a9be2580f2..0c996266d8 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -9,26 +9,6 @@ fn vec_loop(input: &[i32]) -> Vec { output } -fn vec_map_example(input: &[i32]) -> Vec { - // An example of collecting a vector after mapping. - // We map each element of the `input` slice to its value plus 1. - // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. - input.iter().map(|element| element + 1).collect() -} - -fn vec_map(input: &[i32]) -> Vec { - // TODO: Here, we also want to multiply each element in the `input` slice - // by 2, but with iterator mapping instead of manually pushing into an empty - // vector. - // See the example in the function `vec_map_example` above. - input - .iter() - .map(|element| { - // ??? - }) - .collect() -} - fn main() { // You can optionally experiment here. } @@ -43,18 +23,4 @@ mod tests { let ans = vec_loop(&input); assert_eq!(ans, [4, 8, 12, 16, 20]); } - - #[test] - fn test_vec_map_example() { - let input = [1, 2, 3]; - let ans = vec_map_example(&input); - assert_eq!(ans, [2, 3, 4]); - } - - #[test] - fn test_vec_map() { - let input = [2, 4, 6, 8, 10]; - let ans = vec_map(&input); - assert_eq!(ans, [4, 8, 12, 16, 20]); - } } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 516fd321fe..ca3ecf1f03 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -318,16 +318,7 @@ of the Rust book to learn more.""" name = "vecs2" dir = "05_vecs" hint = """ -In the first function, we create an empty vector and want to push new elements -to it. - -In the second function, we map the values of the input and collect them into -a vector. - -After you've completed both functions, decide for yourself which approach you -like better. - -What do you think is the more commonly used pattern under Rust developers?""" +Use the `.push()` method on the vector to push new elements to it.""" # MOVE SEMANTICS diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs index 87f7625a6e..aae71038fa 100644 --- a/solutions/05_vecs/vecs2.rs +++ b/solutions/05_vecs/vecs2.rs @@ -8,22 +8,6 @@ fn vec_loop(input: &[i32]) -> Vec { output } -fn vec_map_example(input: &[i32]) -> Vec { - // An example of collecting a vector after mapping. - // We map each element of the `input` slice to its value plus 1. - // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`. - input.iter().map(|element| element + 1).collect() -} - -fn vec_map(input: &[i32]) -> Vec { - // We will dive deeper into iterators, but for now, this is all what you - // had to do! - // Advanced note: This method is more efficient because it automatically - // preallocates enough capacity. This can be done manually in `vec_loop` - // using `Vec::with_capacity(input.len())` instead of `Vec::new()`. - input.iter().map(|element| 2 * element).collect() -} - fn main() { // You can optionally experiment here. } @@ -38,18 +22,4 @@ mod tests { let ans = vec_loop(&input); assert_eq!(ans, [4, 8, 12, 16, 20]); } - - #[test] - fn test_vec_map_example() { - let input = [1, 2, 3]; - let ans = vec_map_example(&input); - assert_eq!(ans, [2, 3, 4]); - } - - #[test] - fn test_vec_map() { - let input = [2, 4, 6, 8, 10]; - let ans = vec_map(&input); - assert_eq!(ans, [4, 8, 12, 16, 20]); - } } From 8753dd6b2ebbd666249e830a4d2569e24a036b27 Mon Sep 17 00:00:00 2001 From: Marlon Date: Wed, 12 Nov 2025 21:58:28 +0100 Subject: [PATCH 1428/1432] Fixed typo in the exercieses README.md --- exercises/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/README.md b/exercises/README.md index 1df5cc3757..24ebd06965 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -9,7 +9,7 @@ | vecs | Β§8.1 | | move_semantics | Β§4.1-2 | | structs | Β§5.1, Β§5.3 | -| enums | Β§6, Β§18.3 | +| enums | Β§6, Β§19.3 | | strings | Β§8.2 | | modules | Β§7 | | hashmaps | Β§8.3 | From b5d440fdc3a1fadad6dc6196dad2acddabdc671f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 20 Nov 2025 12:49:07 +0100 Subject: [PATCH 1429/1432] Fix clippy3 --- exercises/22_clippy/clippy3.rs | 9 +++++---- solutions/22_clippy/clippy3.rs | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/exercises/22_clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs index 7a3cb39006..3f23aaeea9 100644 --- a/exercises/22_clippy/clippy3.rs +++ b/exercises/22_clippy/clippy3.rs @@ -1,7 +1,6 @@ -// Here are some more easy Clippy fixes so you can see its utility πŸ“Ž +// Here are some more easy Clippy fixes so you can see its utility. // TODO: Fix all the Clippy lints. -#[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fn main() { let my_option: Option<&str> = None; @@ -11,14 +10,16 @@ fn main() { println!("{}", my_option.unwrap()); } + #[rustfmt::skip] let my_arr = &[ -1, -2, -3 -4, -5, -6 ]; println!("My array! Here it is: {my_arr:?}"); - let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5); - println!("This Vec is empty, see? {my_empty_vec:?}"); + let mut my_vec = vec![1, 2, 3, 4, 5]; + my_vec.resize(0, 5); + println!("This Vec is empty, see? {my_vec:?}"); let mut value_a = 45; let mut value_b = 66; diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs index 81f381e02a..8fc267041a 100644 --- a/solutions/22_clippy/clippy3.rs +++ b/solutions/22_clippy/clippy3.rs @@ -1,6 +1,5 @@ use std::mem; -#[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fn main() { let my_option: Option<&str> = None; @@ -11,17 +10,18 @@ fn main() { } // A comma was missing. + #[rustfmt::skip] let my_arr = &[ -1, -2, -3, -4, -5, -6, ]; println!("My array! Here it is: {my_arr:?}"); - let mut my_empty_vec = vec![1, 2, 3, 4, 5]; + let mut my_vec = vec![1, 2, 3, 4, 5]; // `resize` mutates a vector instead of returning a new one. // `resize(0, …)` clears a vector, so it is better to use `clear`. - my_empty_vec.clear(); - println!("This Vec is empty, see? {my_empty_vec:?}"); + my_vec.clear(); + println!("This Vec is empty, see? {my_vec:?}"); let mut value_a = 45; let mut value_b = 66; From 1ebb4d25a62199a402e00ec4a95a9df4211c5daf Mon Sep 17 00:00:00 2001 From: Jatin Sanghvi <20547963+JatinSanghvi@users.noreply.github.com> Date: Fri, 19 Dec 2025 19:17:47 +0530 Subject: [PATCH 1430/1432] Update solution files to match exercise files --- solutions/09_strings/strings4.rs | 2 +- solutions/11_hashmaps/hashmaps1.rs | 2 +- solutions/12_options/options1.rs | 26 +++++++++++++------------- solutions/13_error_handling/errors5.rs | 2 +- solutions/16_lifetimes/lifetimes2.rs | 4 ++-- solutions/21_macros/macros3.rs | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs index 3c69b976e7..087b0386d4 100644 --- a/solutions/09_strings/strings4.rs +++ b/solutions/09_strings/strings4.rs @@ -19,7 +19,7 @@ fn main() { // `.into()` converts a type into an expected type. // If it is called where `String` is expected, it will convert `&str` to `String`. string("nice weather".into()); - // But if it is called where `&str` is expected, then `&str` is kept `&str` since no conversion is needed. + // But if it is called where `&str` is expected, then `&str` is kept as `&str` since no conversion is needed. // If you remove the `#[allow(…)]` line, then Clippy will tell you to remove `.into()` below since it is a useless conversion. #[allow(clippy::useless_conversion)] string_slice("nice weather".into()); diff --git a/solutions/11_hashmaps/hashmaps1.rs b/solutions/11_hashmaps/hashmaps1.rs index 3a787c4361..0a654b88da 100644 --- a/solutions/11_hashmaps/hashmaps1.rs +++ b/solutions/11_hashmaps/hashmaps1.rs @@ -1,7 +1,7 @@ // 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 3 different -// types of fruits (e.g apple, banana, mango) in the basket and the total count +// types of fruits (e.g. apple, banana, mango) in the basket and the total count // of all the fruits should be at least 5. use std::collections::HashMap; diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs index 4d615dd602..a8e6457d19 100644 --- a/solutions/12_options/options1.rs +++ b/solutions/12_options/options1.rs @@ -1,8 +1,8 @@ -// This function returns how much icecream there is left in the fridge. +// This function returns how much ice cream there is left in the fridge. // If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, -// someone eats it all, so no icecream is left (value 0). Return `None` if +// someone eats it all, so no ice cream is left (value 0). Return `None` if // `hour_of_day` is higher than 23. -fn maybe_icecream(hour_of_day: u16) -> Option { +fn maybe_ice_cream(hour_of_day: u16) -> Option { match hour_of_day { 0..=21 => Some(5), 22..=23 => Some(0), @@ -21,19 +21,19 @@ mod tests { #[test] fn raw_value() { // Using `unwrap` is fine in a test. - let icecreams = maybe_icecream(12).unwrap(); + let ice_creams = maybe_ice_cream(12).unwrap(); - assert_eq!(icecreams, 5); + assert_eq!(ice_creams, 5); } #[test] - fn check_icecream() { - assert_eq!(maybe_icecream(0), Some(5)); - assert_eq!(maybe_icecream(9), Some(5)); - assert_eq!(maybe_icecream(18), Some(5)); - assert_eq!(maybe_icecream(22), Some(0)); - assert_eq!(maybe_icecream(23), Some(0)); - assert_eq!(maybe_icecream(24), None); - assert_eq!(maybe_icecream(25), None); + fn check_ice_cream() { + assert_eq!(maybe_ice_cream(0), Some(5)); + assert_eq!(maybe_ice_cream(9), Some(5)); + assert_eq!(maybe_ice_cream(18), Some(5)); + assert_eq!(maybe_ice_cream(22), Some(0)); + assert_eq!(maybe_ice_cream(23), Some(0)); + assert_eq!(maybe_ice_cream(24), None); + assert_eq!(maybe_ice_cream(25), None); } } diff --git a/solutions/13_error_handling/errors5.rs b/solutions/13_error_handling/errors5.rs index c1424eeee5..93ab3b9d77 100644 --- a/solutions/13_error_handling/errors5.rs +++ b/solutions/13_error_handling/errors5.rs @@ -6,7 +6,7 @@ // // In short, this particular use case for boxes is for when you want to own a // value and you care only that it is a type which implements a particular -// trait. To do so, The `Box` is declared as of type `Box` where +// trait. To do so, the `Box` is declared as of type `Box` where // `Trait` is the trait the compiler looks for on any value used in that // context. For this exercise, that context is the potential errors which // can be returned in a `Result`. diff --git a/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs index 3ca49093e8..412c60216a 100644 --- a/solutions/16_lifetimes/lifetimes2.rs +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -4,7 +4,7 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { fn main() { let string1 = String::from("long string is long"); - // Solution1: You can move `strings2` out of the inner block so that it is + // Solution 1: You can move `strings2` out of the inner block so that it is // not dropped before the print statement. let string2 = String::from("xyz"); let result; @@ -21,7 +21,7 @@ fn main() { { let string2 = String::from("xyz"); result = longest(&string1, &string2); - // Solution2: You can move the print statement into the inner block so + // Solution 2: You can move the print statement into the inner block so // that it is executed before `string2` is dropped. println!("The longest string is '{result}'"); // `string2` dropped here (end of the inner scope). diff --git a/solutions/21_macros/macros3.rs b/solutions/21_macros/macros3.rs index df35be4d9a..9d574f894a 100644 --- a/solutions/21_macros/macros3.rs +++ b/solutions/21_macros/macros3.rs @@ -1,4 +1,4 @@ -// Added the attribute `macro_use` attribute. +// Added the `macro_use` attribute. #[macro_use] mod macros { macro_rules! my_macro { From 45f789114bae670245cddaf234d442e8a5c4a20c Mon Sep 17 00:00:00 2001 From: Rod Elias Date: Mon, 12 Jan 2026 00:22:59 -0300 Subject: [PATCH 1431/1432] chore: minor improvements --- exercises/09_strings/README.md | 2 +- rustlings-macros/info.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/09_strings/README.md b/exercises/09_strings/README.md index fa2104cc37..a3d22f03d1 100644 --- a/exercises/09_strings/README.md +++ b/exercises/09_strings/README.md @@ -1,6 +1,6 @@ # Strings -Rust has two string types, a string slice (`&str`) and an owned string (`String`). +Rust has two string types: a string slice (`&str`) and an owned string (`String`). We're not going to dictate when you should use which one, but we'll show you how to identify and create them, as well as use them. diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index ca3ecf1f03..e42b0f2629 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -21,7 +21,7 @@ get started, here are some notes about how Rustlings operates: final_message = """ We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, don't hesitate to report them on Github. +If you noticed any issues, don't hesitate to report them on GitHub. You can also contribute your own exercises to help the greater community! Before reporting an issue or contributing, please read our guidelines: From 1b47fd97c02524b74096f293249f223a5856556c Mon Sep 17 00:00:00 2001 From: Padraic Slattery Date: Thu, 22 Jan 2026 14:13:29 +0100 Subject: [PATCH 1432/1432] chore: Update outdated GitHub Actions versions --- .github/workflows/rust.yml | 8 ++++---- .github/workflows/website.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0317f35146..0d130904dc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,13 +19,13 @@ jobs: clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Clippy run: cargo clippy -- --deny warnings fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: rustfmt run: cargo fmt --all --check test: @@ -34,14 +34,14 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: swatinem/rust-cache@v2 - name: cargo test run: cargo test --workspace dev-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: swatinem/rust-cache@v2 - name: rustlings dev check run: cargo dev check --require-solutions diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 936cd562e5..d751358143 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -13,7 +13,7 @@ jobs: working-directory: website runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install TailwindCSS run: npm install - name: Build CSS