From e5efc68a9101d7d7e38263c8a6ee44dda991fc6a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:34:48 +0100 Subject: Done macro --- rustlings-macros/src/lib.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 rustlings-macros/src/lib.rs (limited to 'rustlings-macros/src') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs new file mode 100644 index 0000000..dd1a588 --- /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() +} -- cgit v1.2.3 From dd025391f2f3a4cb0a45e28163b01538b4b525cb Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:52:51 +0100 Subject: Make everything static --- rustlings-macros/src/lib.rs | 6 +++--- src/main.rs | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'rustlings-macros/src') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index dd1a588..d8cd05c 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 fed8c11..7822d12 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() { -- cgit v1.2.3 From 39bdd086a775d87115691b830f65e2a438874fec Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 18:18:20 +0100 Subject: Use concat explicitly from std --- rustlings-macros/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rustlings-macros/src') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d8cd05c..598b5c3 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),*], -- cgit v1.2.3 From 394ca402a8883581dc040546b4ca18b07d76a7f2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 23:57:54 +0200 Subject: 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(-) (limited to 'rustlings-macros/src') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 598b5c3..d8da666 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 56b4b61..1e2d677 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 ae47d5e..c9fb331 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`") } -- cgit v1.2.3 From b9167e9299bfe7c644cdbc2f6e933873f06b1c3f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 18:19:15 +0200 Subject: Remove redundant checks --- rustlings-macros/src/lib.rs | 11 ++--------- src/info_file.rs | 8 -------- 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'rustlings-macros/src') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d8da666..d95a93a 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -15,8 +15,8 @@ pub fn include_files(_: TokenStream) -> TokenStream { let mut files = Vec::with_capacity(8); let mut dirs = Vec::with_capacity(128); - for entry in read_dir("exercises").expect("Failed to open the exercises directory") { - let entry = entry.expect("Failed to read the exercises directory"); + for entry in read_dir("exercises").expect("Failed to open the `exercises` directory") { + let entry = entry.expect("Failed to read the `exercises` directory"); if entry.file_type().unwrap().is_file() { let path = entry.path(); @@ -46,13 +46,6 @@ pub fn include_files(_: TokenStream) -> TokenStream { return None; } - if path.extension() != Some("rs".as_ref()) { - panic!( - "Found {} but expected only README.md and .rs files", - path.display(), - ); - } - Some(path_to_string(path)) }); diff --git a/src/info_file.rs b/src/info_file.rs index 18e77b9..879609e 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) } } -- cgit v1.2.3 From 2dac8e509bed07c30a98983cfb6b80f73a1582e9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 23 Apr 2024 19:18:25 +0200 Subject: 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 + solutions/04_primitive_types/primitive_types1.rs | 1 + solutions/04_primitive_types/primitive_types2.rs | 1 + solutions/04_primitive_types/primitive_types3.rs | 1 + solutions/04_primitive_types/primitive_types4.rs | 1 + solutions/04_primitive_types/primitive_types5.rs | 1 + solutions/04_primitive_types/primitive_types6.rs | 1 + solutions/05_vecs/vecs1.rs | 1 + solutions/05_vecs/vecs2.rs | 1 + solutions/06_move_semantics/move_semantics1.rs | 1 + solutions/06_move_semantics/move_semantics2.rs | 1 + solutions/06_move_semantics/move_semantics3.rs | 1 + solutions/06_move_semantics/move_semantics4.rs | 1 + solutions/06_move_semantics/move_semantics5.rs | 1 + solutions/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 (limited to 'rustlings-macros/src') diff --git a/Cargo.lock b/Cargo.lock index 1618eec..93617fa 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 6181b1e..1e6a24c 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 27071a5..6549e1c 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 095deaa..f925f69 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 d95a93a..0bf3dbd 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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /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 0000000..70b786d --- /dev/null +++ b/solutions/quizzes/quiz3.rs @@ -0,0 +1 @@ +// TODO diff --git a/src/app_state.rs b/src/app_state.rs index 09de2a3..0767c2b 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 eae3099..ccd8dca 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 e85efe4..4493a57 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 f210db7..f1a9509 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 -- cgit v1.2.3 From 8d45cdb09df7fbff117b91f500ccf82980732a47 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:54:03 +0200 Subject: 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 (limited to 'rustlings-macros/src') diff --git a/Cargo.toml b/Cargo.toml index ce3cf04..caa139e 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 2164269..4b50290 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 f9aba66..20d6776 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 0000000..3795291 --- /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 0bf3dbd..fc2bcf1 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; -- cgit v1.2.3 From d9df809838191962a82e98ff01aaaa73950ba670 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 12 May 2024 17:40:53 +0200 Subject: 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(-) (limited to 'rustlings-macros/src') diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index fc2bcf1..4417a4f 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 8cb3e46..492be34 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 a84e332..23c8d6e 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, + ); + } } } -- cgit v1.2.3 From 11fda5d70f568e0f528d91dd573447719abe05f4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 01:25:38 +0200 Subject: 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 (limited to 'rustlings-macros/src') diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6a2d17..bc00a6b 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 bc10d02..f2015cb 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 4204f27..0000000 --- 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 3795291..0000000 --- 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 0000000..4204f27 --- /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 4417a4f..6c6067b 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 23c8d6e..45f8eca 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 dbe4f08..14b886b 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"); } -- cgit v1.2.3