Compare commits

..

No commits in common. "47f8199a996c098cfd64f5afa08284a588d2b874" and "0f71e122357f3f214c1d9db71f1d84c9f92adc5f" have entirely different histories.

35 changed files with 100 additions and 172 deletions

View file

@ -40,4 +40,4 @@ jobs:
- uses: actions/checkout@v4
- uses: swatinem/rust-cache@v2
- name: Run rustlings dev check
run: cargo run -- dev check --require-solutions
run: cargo run -- dev check

View file

@ -1,10 +1,3 @@
<a name="6.0.1"></a>
## 6.0.1 (2024-07-04)
Small exercise improvements and fixes.
Most importantly, fixed that the exercise `clippy1` was already solved 😅
<a name="6.0.0"></a>
## 6.0.0 (2024-07-03)

62
Cargo.lock generated
View file

@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "castaway"
version = "0.2.3"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
dependencies = [
"rustversion",
]
@ -527,7 +527,7 @@ dependencies = [
"libc",
"redox_syscall 0.5.2",
"smallvec",
"windows-targets 0.52.6",
"windows-targets 0.52.5",
]
[[package]]
@ -654,7 +654,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rustlings"
version = "6.0.1"
version = "6.0.0"
dependencies = [
"anyhow",
"assert_cmd",
@ -673,7 +673,7 @@ dependencies = [
[[package]]
name = "rustlings-macros"
version = "6.0.1"
version = "6.0.0"
dependencies = [
"quote",
"serde",
@ -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.6",
"windows-targets 0.52.5",
]
[[package]]
@ -997,18 +997,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"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.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 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]]
@ -1019,9 +1019,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
@ -1031,9 +1031,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
@ -1043,15 +1043,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
@ -1061,9 +1061,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
@ -1073,9 +1073,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
@ -1085,9 +1085,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
@ -1097,9 +1097,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"

View file

@ -8,7 +8,7 @@ exclude = [
]
[workspace.package]
version = "6.0.1"
version = "6.0.0"
authors = [
"Liv <mokou@fastmail.com>",
"Mo Bitar <mo8it@proton.me>",
@ -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.0.0" }
serde_json = "1.0.120"
serde.workspace = true
toml_edit.workspace = true

View file

@ -3,7 +3,8 @@
// 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!` and check the updated output in the terminal.
// Try adding a new `println!`.
// Try removing a semicolon and see what happens in the terminal!
fn main() {
println!("Hello and");

View file

@ -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_semantics4() {
fn move_semantics5() {
let mut x = 100;
let y = &mut x;
let z = &mut x;

View file

@ -1,5 +1,3 @@
#![allow(clippy::ptr_arg)]
// TODO: Fix the compiler errors without changing anything except adding or
// removing references (the character `&`).
@ -18,7 +16,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}");
}

View file

@ -1,4 +1,3 @@
#[allow(dead_code)]
#[derive(Debug)]
enum Message {
// TODO: Define the different variants used below.
@ -6,7 +5,7 @@ enum Message {
impl Message {
fn call(&self) {
println!("{self:?}");
println!("{:?}", self);
}
}

View file

@ -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 ???;

View file

@ -19,8 +19,7 @@ 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); // Don't change this line.
assert_eq!(icecreams, 5);
}
#[test]

View file

@ -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. 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 (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!

View file

@ -1,5 +1,3 @@
#![allow(clippy::comparison_chain)]
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,

View file

@ -35,7 +35,7 @@ impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero),
x if x == 0 => Err(CreationError::Zero),
x => Ok(Self(x as u64)),
}
}
@ -55,6 +55,7 @@ fn main() {
#[cfg(test)]
mod test {
use super::*;
use std::num::IntErrorKind;
#[test]
fn test_parse_error() {

View file

@ -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

View file

@ -54,7 +54,7 @@ mod tests {
#[test]
fn test_result_with_list() {
assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]);
assert_eq!(result_with_list().unwarp(), [1, 11, 1426, 3]);
}
#[test]

View file

@ -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,11 +9,8 @@
// …
// The eighth thread (offset 7), will sum 7, 15, 23, …
//
// 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.
// 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)]

View file

@ -8,7 +8,6 @@ use std::rc::Rc;
#[derive(Debug)]
struct Sun;
#[allow(dead_code)]
#[derive(Debug)]
enum Planet {
Mercury(Rc<Sun>),

View file

@ -4,9 +4,11 @@
// 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() {
// TODO: Fix the Clippy lint in this line.
let pi = 3.14;
// Use the more accurate `PI` constant.
let pi = PI;
let radius: f32 = 5.0;
let area = pi * radius.powi(2);

View file

@ -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!
// TODO: Write a function that calculates the price of an order of apples given
// the quantity bought.
// 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(???) -> ??? {

View file

@ -16,14 +16,16 @@ 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
@ -31,7 +33,6 @@ 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`."""
@ -129,7 +130,8 @@ 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
@ -310,7 +312,8 @@ 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"
@ -324,7 +327,8 @@ 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
@ -351,7 +355,8 @@ 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"
@ -370,7 +375,8 @@ 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"
@ -511,7 +517,8 @@ Example:
`placeholder("blue");`
should become
`string_slice("blue");`
because "blue" is `&str`, not `String`."""
because "blue" is `&str`, not `String`.
"""
# MODULES
@ -613,7 +620,8 @@ 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"
@ -805,7 +813,8 @@ Here is how to specify a trait bound for an implementation block:
`impl<T: Trait1 + Trait2 + > for Foo<T> { }`
You may need this:
`use std::fmt::Display;`"""
`use std::fmt::Display;`
"""
# LIFETIMES

View file

@ -1,5 +1,3 @@
#![allow(clippy::needless_late_init)]
fn main() {
// Reading uninitialized variables isn't allowed in Rust!
// Therefore, we need to assign a value first.
@ -7,7 +5,7 @@ fn main() {
println!("Number {x}");
// It is possible to declare a variable and initialize it later.
// It possible to declare a variable and initialize it later.
// But it can't be used before initialization.
let y: i32;
y = 42;

View file

@ -1,5 +1,3 @@
#![allow(clippy::ptr_arg)]
fn main() {
let data = "Rust is great!".to_string();

View file

@ -1,4 +1,3 @@
#[allow(dead_code)]
#[derive(Debug)]
enum Message {
Move { x: i64, y: i64 },
@ -9,7 +8,7 @@ enum Message {
impl Message {
fn call(&self) {
println!("{self:?}");
println!("{:?}", self);
}
}

View file

@ -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;

View file

@ -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 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 team_2 = scores.entry(team_2_name).or_insert_with(Team::default);
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;
}

View file

@ -22,7 +22,6 @@ mod tests {
fn raw_value() {
// Using `unwrap` is fine in a test.
let icecreams = maybe_icecream(12).unwrap();
assert_eq!(icecreams, 5);
}

View file

@ -5,18 +5,17 @@
// 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. 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 (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;
#[allow(unused_variables)]
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;

View file

@ -1,5 +1,3 @@
#![allow(clippy::comparison_chain)]
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,

View file

@ -36,7 +36,7 @@ impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<Self, CreationError> {
match value {
x if x < 0 => Err(CreationError::Negative),
0 => Err(CreationError::Zero),
x if x == 0 => Err(CreationError::Zero),
x => Ok(Self(x as u64)),
}
}
@ -57,6 +57,7 @@ fn main() {
#[cfg(test)]
mod test {
use super::*;
use std::num::IntErrorKind;
#[test]
fn test_parse_error() {

View file

@ -1,5 +1,3 @@
#![allow(dead_code)]
trait Licensed {
fn licensing_info(&self) -> String {
"Default license".to_string()

View file

@ -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,11 +9,8 @@
// …
// The eighth thread (offset 7), will sum 7, 15, 23, …
//
// 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.
// 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)]

View file

@ -8,7 +8,6 @@ use std::rc::Rc;
#[derive(Debug)]
struct Sun;
#[allow(dead_code)]
#[derive(Debug)]
enum Planet {
Mercury(Rc<Sun>),

View file

@ -4,7 +4,6 @@
// 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)]

View file

@ -92,10 +92,6 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<hashbrown::HashSet<
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.");
}
if !exercise_info.test && file_buf.contains("#[test]") {
bail!("The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file");
}
@ -162,46 +158,7 @@ fn check_unexpected_files(
Ok(())
}
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<()> {
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"),
@ -211,14 +168,15 @@ 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)
Ok(())
}
fn check_solutions(require_solutions: bool, info_file: &InfoFile, target_dir: &Path) -> 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_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(|| {
@ -244,7 +202,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(&mut output, &target_dir) {
Ok(true) => {
paths.lock().unwrap().insert(PathBuf::from(path));
}
@ -280,9 +238,8 @@ pub fn check(require_solutions: bool) -> Result<()> {
check_cargo_toml(&info_file.exercises, &current_cargo_toml, b"")?;
}
let target_dir = parse_target_dir()?;
check_exercises(&info_file, &target_dir)?;
check_solutions(require_solutions, &info_file, &target_dir)?;
check_exercises(&info_file)?;
check_solutions(require_solutions, &info_file)?;
println!("\nEverything looks fine!");
@ -291,6 +248,3 @@ 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.";

View file

@ -11,17 +11,14 @@ pub struct ExerciseInfo {
pub name: String,
/// Exercise's directory name inside the `exercises/` directory.
pub dir: Option<String>,
/// Run `cargo test` on the exercise.
#[serde(default = "default_true")]
/// Run `cargo test` on the exercise.
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 {