diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cc8ac923..f1659a6a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,6 +17,7 @@ _fix a bug! ➡️ [open a Pull Request](#prs)_
_implement a new feature! ➡️ [open an Issue to discuss it first, then a Pull Request](#issues)_
+
### Working on the source code
`rustlings` is basically a glorified `rustc` wrapper. Therefore the source code
@@ -24,14 +25,19 @@ isn't really that complicated since the bulk of the work is done by `rustc`.
`src/main.rs` contains a simple `argh` CLI that connects to most of the other source files.
+
### Adding an exercise
The first step is to add the exercise! Name the file `exercises/yourTopic/yourTopicN.rs`, make sure to
put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`.
-Next make sure it runs with `rustlings`. The exercise metadata is stored in `info.toml`, under the `exercises` array. The order of the `exercises` array determines the order the exercises are run by `rustlings verify` and `rustlings watch`.
+Next make sure it runs with `rustlings`. The exercise metadata is stored in `info.toml`, under the `exercises` array.
+The order of the `exercises` array determines the order the exercises are run by `rustlings verify`
+and `rustlings watch`.
+
+Add the metadata for your exercise in the correct order in the `exercises` array. If you are unsure of the correct
+ordering, add it at the bottom and ask in your pull request. The exercise metadata should contain the following:
-Add the metadata for your exercise in the correct order in the `exercises` array. If you are unsure of the correct ordering, add it at the bottom and ask in your pull request. The exercise metadata should contain the following:
```diff
...
+ [[exercises]]
@@ -43,11 +49,16 @@ Add the metadata for your exercise in the correct order in the `exercises` array
...
```
-The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`. If you're working on a Clippy exercise, use `mode = "clippy"`.
+The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have
+tests to verify in your exercise, choose `test`, otherwise `compile`. If you're working on a Clippy exercise,
+use `mode = "clippy"`.
+
+Run `modularize_exercises.sh` script to include your exercise in a module tree.
That's all! Feel free to put up a pull request.
+
### Issues
You can open an issue [here](https://github.com/rust-lang/rustlings/issues/new).
@@ -59,6 +70,7 @@ If you're reporting a bug, please include the output of the following commands:
- Your OS name and version
+
### Pull Requests
Opening a pull request is as easy as forking the repository and committing your
diff --git a/exercises/exercises.rs b/exercises/exercises.rs
new file mode 100644
index 00000000..4e4d04a1
--- /dev/null
+++ b/exercises/exercises.rs
@@ -0,0 +1,190 @@
+// This file was auto-generated with ../modularize_exercises.sh script
+#[path="00_intro/intro1.rs"]
+mod intro1;
+#[path="00_intro/intro2.rs"]
+mod intro2;
+#[path="01_variables/variables1.rs"]
+mod variables1;
+#[path="01_variables/variables2.rs"]
+mod variables2;
+#[path="01_variables/variables3.rs"]
+mod variables3;
+#[path="01_variables/variables4.rs"]
+mod variables4;
+#[path="01_variables/variables5.rs"]
+mod variables5;
+#[path="01_variables/variables6.rs"]
+mod variables6;
+#[path="02_functions/functions1.rs"]
+mod functions1;
+#[path="02_functions/functions2.rs"]
+mod functions2;
+#[path="02_functions/functions3.rs"]
+mod functions3;
+#[path="02_functions/functions4.rs"]
+mod functions4;
+#[path="02_functions/functions5.rs"]
+mod functions5;
+#[path="03_if/if1.rs"]
+mod if1;
+#[path="03_if/if2.rs"]
+mod if2;
+#[path="03_if/if3.rs"]
+mod if3;
+#[path="04_primitive_types/primitive_types1.rs"]
+mod primitive_types1;
+#[path="04_primitive_types/primitive_types2.rs"]
+mod primitive_types2;
+#[path="04_primitive_types/primitive_types3.rs"]
+mod primitive_types3;
+#[path="04_primitive_types/primitive_types4.rs"]
+mod primitive_types4;
+#[path="04_primitive_types/primitive_types5.rs"]
+mod primitive_types5;
+#[path="04_primitive_types/primitive_types6.rs"]
+mod primitive_types6;
+#[path="05_vecs/vecs1.rs"]
+mod vecs1;
+#[path="05_vecs/vecs2.rs"]
+mod vecs2;
+#[path="06_move_semantics/move_semantics1.rs"]
+mod move_semantics1;
+#[path="06_move_semantics/move_semantics2.rs"]
+mod move_semantics2;
+#[path="06_move_semantics/move_semantics3.rs"]
+mod move_semantics3;
+#[path="06_move_semantics/move_semantics4.rs"]
+mod move_semantics4;
+#[path="06_move_semantics/move_semantics5.rs"]
+mod move_semantics5;
+#[path="06_move_semantics/move_semantics6.rs"]
+mod move_semantics6;
+#[path="07_structs/structs1.rs"]
+mod structs1;
+#[path="07_structs/structs2.rs"]
+mod structs2;
+#[path="07_structs/structs3.rs"]
+mod structs3;
+#[path="08_enums/enums1.rs"]
+mod enums1;
+#[path="08_enums/enums2.rs"]
+mod enums2;
+#[path="08_enums/enums3.rs"]
+mod enums3;
+#[path="09_strings/strings1.rs"]
+mod strings1;
+#[path="09_strings/strings2.rs"]
+mod strings2;
+#[path="09_strings/strings3.rs"]
+mod strings3;
+#[path="09_strings/strings4.rs"]
+mod strings4;
+#[path="10_modules/modules1.rs"]
+mod modules1;
+#[path="10_modules/modules2.rs"]
+mod modules2;
+#[path="10_modules/modules3.rs"]
+mod modules3;
+#[path="11_hashmaps/hashmaps1.rs"]
+mod hashmaps1;
+#[path="11_hashmaps/hashmaps2.rs"]
+mod hashmaps2;
+#[path="11_hashmaps/hashmaps3.rs"]
+mod hashmaps3;
+#[path="12_options/options1.rs"]
+mod options1;
+#[path="12_options/options2.rs"]
+mod options2;
+#[path="12_options/options3.rs"]
+mod options3;
+#[path="13_error_handling/errors1.rs"]
+mod errors1;
+#[path="13_error_handling/errors2.rs"]
+mod errors2;
+#[path="13_error_handling/errors3.rs"]
+mod errors3;
+#[path="13_error_handling/errors4.rs"]
+mod errors4;
+#[path="13_error_handling/errors5.rs"]
+mod errors5;
+#[path="13_error_handling/errors6.rs"]
+mod errors6;
+#[path="14_generics/generics1.rs"]
+mod generics1;
+#[path="14_generics/generics2.rs"]
+mod generics2;
+#[path="15_traits/traits1.rs"]
+mod traits1;
+#[path="15_traits/traits2.rs"]
+mod traits2;
+#[path="15_traits/traits3.rs"]
+mod traits3;
+#[path="15_traits/traits4.rs"]
+mod traits4;
+#[path="15_traits/traits5.rs"]
+mod traits5;
+#[path="16_lifetimes/lifetimes1.rs"]
+mod lifetimes1;
+#[path="16_lifetimes/lifetimes2.rs"]
+mod lifetimes2;
+#[path="16_lifetimes/lifetimes3.rs"]
+mod lifetimes3;
+#[path="17_tests/tests1.rs"]
+mod tests1;
+#[path="17_tests/tests2.rs"]
+mod tests2;
+#[path="17_tests/tests3.rs"]
+mod tests3;
+#[path="17_tests/tests4.rs"]
+mod tests4;
+#[path="18_iterators/iterators1.rs"]
+mod iterators1;
+#[path="18_iterators/iterators2.rs"]
+mod iterators2;
+#[path="18_iterators/iterators3.rs"]
+mod iterators3;
+#[path="18_iterators/iterators4.rs"]
+mod iterators4;
+#[path="18_iterators/iterators5.rs"]
+mod iterators5;
+#[path="19_smart_pointers/arc1.rs"]
+mod arc1;
+#[path="19_smart_pointers/box1.rs"]
+mod box1;
+#[path="19_smart_pointers/cow1.rs"]
+mod cow1;
+#[path="19_smart_pointers/rc1.rs"]
+mod rc1;
+#[path="20_threads/threads1.rs"]
+mod threads1;
+#[path="20_threads/threads2.rs"]
+mod threads2;
+#[path="20_threads/threads3.rs"]
+mod threads3;
+#[path="21_macros/macros1.rs"]
+mod macros1;
+#[path="21_macros/macros2.rs"]
+mod macros2;
+#[path="21_macros/macros3.rs"]
+mod macros3;
+#[path="21_macros/macros4.rs"]
+mod macros4;
+#[path="22_clippy/clippy1.rs"]
+mod clippy1;
+#[path="22_clippy/clippy2.rs"]
+mod clippy2;
+#[path="22_clippy/clippy3.rs"]
+mod clippy3;
+#[path="23_conversions/as_ref_mut.rs"]
+mod as_ref_mut;
+#[path="23_conversions/from_into.rs"]
+mod from_into;
+#[path="23_conversions/from_str.rs"]
+mod from_str;
+#[path="23_conversions/try_from_into.rs"]
+mod try_from_into;
+#[path="23_conversions/using_as.rs"]
+mod using_as;
+mod quiz1;
+mod quiz2;
+mod quiz3;
diff --git a/modularize_exercises.sh b/modularize_exercises.sh
new file mode 100644
index 00000000..d8e64faa
--- /dev/null
+++ b/modularize_exercises.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Clear the file
+truncate -s 0 exercises/exercises.rs
+
+# Write the initial line to the file
+echo '// This file was auto-generated with ../modularize_exercises.sh script' >> exercises/exercises.rs
+
+# Loop through all the files in the exercises directory and add them to the exercises.rs file
+find ./exercises -type f -name "*.rs" ! -name "exercises.rs" -print0 | sort -z | while IFS= read -r -d '' file; do
+ # Remove the 'exercises/' prefix from the file path
+ path=${file#./exercises/}
+
+ # Write the module path only if the file is in a subdirectory
+ if [[ -n "$path" && "$path" == */* ]]; then
+ echo "#[path=\"$path\"]" >> exercises/exercises.rs
+ fi
+ # Write the module name to the file
+ echo "mod $(basename "$file" .rs);" >> exercises/exercises.rs
+done
+
+echo 'Done'
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index a06f0c56..a94ad298 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -25,6 +25,9 @@ mod project;
mod run;
mod verify;
+#[path = "../exercises/exercises.rs"]
+mod exercises;
+
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
#[derive(Parser)]
#[command(version)]