1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! [![gitlab]](https://gitlab.com/otafablab/otarustlings) [![crates-io]](https://crates.io/crates/otarustlings) [![docs-rs]](https://docs.rs/otarustlings) [![homepage]](https://otafablab.gitlab.io/rust-lukiokurssi/)
//!
//! [gitlab]: https://img.shields.io/badge/gitlab-8da0cb?style=for-the-badge&labelColor=555555&logo=gitlab
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
//! [homepage]: https://img.shields.io/badge/course-AF0973?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBmaWxsPSIjZjVmNWY1IiB4bWxuczp2PSJodHRwczovL3ZlY3RhLmlvL25hbm8iPjxwYXRoIGQ9Ik0yMjkuNDkgMjc3LjQxMmMtNC45NDYgMS43NzctNy4zNzcgNS4xODYtOS44OTEgMTAuMjcxYTEwLjc5OCAxNC4wNzcgMCAwIDAtLjQzMiAzLjczNiAxMC43OTggMTQuMDc3IDAgMCAwIDEwLjc5NyAxNC4wNzYgMTAuNzk4IDE0LjA3NyAwIDAgMCAxMC43OTktMTQuMDc2IDEwLjc5OCAxNC4wNzcgMCAwIDAtMTAuNzk5LTE0LjA3OCAxMC43OTggMTQuMDc3IDAgMCAwLS40NzUuMDd6bTgzLjc3Ni0uODUzbC0uMTI1LjAzMWMtMy4zNS45MTgtNS44NzggMi4yOTMtNy45OCA0LjI5MWExMC43OTggMTQuMDc3IDAgMCAwLTMuMDkgOS43MzQgMTAuNzk4IDE0LjA3NyAwIDAgMCAxMC43OTcgMTQuMDc4IDEwLjc5OCAxNC4wNzcgMCAwIDAgMTAuNzk5LTE0LjA3OCAxMC43OTggMTQuMDc3IDAgMCAwLTEwLjQtMTQuMDU3eiIvPjxwYXRoIGQ9Ik0xODkuMjEzIDE0OC45MzljLTMuMTgxLjA0NS01Ljc3IDIuNjE3LTYuNDkgMTAuMjE1LTEuNzgyIDE4Ljc5Ny0xLjQ2NCAyMS45NDQtMTYuMTM5IDE1LjQ2MXMtMTkuMzUtOS45OTYtMTguMzExIDkuMDE0LjEzIDE4LjY4Ny0xNS4zOTMgMTQuMjk5LTE5LjE2LTIuNTQ2LTEzLjg1NCAxNC40MzIgMS41MTcgMTguOTgyLTE1LjAzMSAxNi41MS0xMy43MDggNy41OTEtMTAuNjQ2IDEzLjk4OCA2LjI4NiAxMS40MTcgNC45MDYgMTYuOTgyLTEwLjQwMiA1LjUwNC0xOS41MzMgNi4xMTMtNi43NDMgNi41NzEtNy4yNCA5LjI4NyAxNC45OTYgMTMuNjMyIDEyLjI0OCAyMC41MTRjLTMuMjY3LS4wMy0yOS4wMTQtMjEuMjM0LTI3Ljc2Mi0yNS4zOTFzNi4wNjYtMS4wNzQgMTMuOTE4LTE2LjE2MiA4LjcwNS0yNC4wNTYgMy45OTQtNDEuODc1LTI4Ljc1NC0zMi4xODktMjguNzU0LTMyLjE4OS0zLjI1OCAzNi42ODEtMi4wODQgNTguNTY2Yy0xOS40NzktMTkuMDMyLTE4LjU5Mi01MS4zNDYtMTguNTkyLTUxLjM0NnMtMTEuODYgNi45NTUtMjAuNDc1IDE4LjY4MiAxLjczOCAzMy40NDEgMS43MzggMzMuNDQxYzExLjU5NCAxNy40MzYgMTcuNzc2IDIxLjU1MSAyOC44OTMgMjguNjY4IDUuNzA3IDE5LjExNSAyNC42MjIgMzQuODQ1IDM1LjM3OSA0Mi42MjctNi40NjYgNi43MDktMjIuNTM4IDE3LjIxNy0yMi4yNyAyNi42NDguMjY4IDkuNDEgNzguNDQ1IDgzLjM4MyA3OC40NDUgODMuMzgzczEuMDM2LTMuMjg3LTkuMDItMjEuNzI5Yy0xMC4wMTgtMTguMzcxLTI3LjQxMy0zNy40MDEtMzIuNzU4LTU1Ljg4NS0yLjMyMS04LjAyOCAzLjg0Mi0xMC44OTYgMy44NDItMTAuODk2czI5LjU5OSA0NC40NTMgNDcuMzI4IDYxYy0zLjAwMi0yMC4xMTUtMTkuNzYxLTM3Ljc4My0yNC4wOC01Mi42NjYgMzMuODQ4IDI0LjcxMiAxNDYuMTM5IDI4LjYzNiAxNjYuMTQzIDI5LjE0NmwuMDctLjA4NmMxLjQzNC0xLjcwOCAyLjkwNi0zLjM4NCA0LjQ1MS00Ljk5NCAxLjY4MS0xLjc3MyAzLjQtMy41MTggNS4xNjQtNS4yMTdzMy41NzQtMy4zNTIgNS40MzQtNC45NDNhMTAyLjA1IDEwMi4wNSAwIDAgMSA1Ljc0LTQuNTc0IDg1LjA3IDg1LjA3IDAgMCAxIDYuMDg2LTQuMTA1IDYyLjI4IDYyLjI4IDAgMCAxIDQuMjU2LTIuMzQ0IDQ0LjA2IDQ0LjA2IDAgMCAxIDQuNDcxLTEuOTQxYzEuNTIzLS41NTggMy4wNzYtMS4wMiA0LjY1OC0xLjM1czMuMTk0LS41MyA0LjgyOC0uNTY4YzEuNjgxLS4wMjggMy4zNjYuMDIyIDUuMDQxLjE3YTQwLjI1IDQwLjI1IDAgMCAxIDQuOTg0Ljc0OCAzNC4zMyAzNC4zMyAwIDAgMSA0LjY5MSAxLjM0YzEuNTI3LjU1NSAzLjAxOCAxLjIxMyA0LjQ2MyAxLjk1OWE0Mi42IDQyLjYgMCAwIDEgNC4xOTkgMi40ODYgNDYuODYgNDYuODYgMCAwIDEgMy45MDIgMi45MjggODYuOTkgODYuOTkgMCAwIDEgNC4zNDQgMy44NjFjMS40MDcgMS4zMzEgMi43NzYgMi43IDQuMTI5IDQuMDg4bDQuNTc2IDQuNzc1YzQuODMyLS44NyAxMy45ODEtMi43MzggMjEuMjAzLTQuMTQ1LTQuMzkxIDQuNjgzLTEwLjc4MyA4LjY1OC0xMC43ODMgOC42NTgtNS41MSAyLjA2Mi04LjQ1NSAyLjQ5Ni0xMS42NTIgMi42MTMtLjYxOC0xLjQ1OC0uOTYyLTMuNDk2LTIuMzI3LTYuMDk1YTMyLjMzIDMyLjMzIDAgMCAwLTUuOTYyLTcuOTE1Yy0xLjQxMS0xLjM2MS0yLjgyMS0yLjY1LTQuMjU0LTMuODUycy0yLjg5LTIuMzE1LTQuMzk2LTMuMzI2LTMuMDY0LTEuOTIyLTQuNjk3LTIuNzE1YTM0LjUzIDM0LjUzIDAgMCAwLTUuMTUyLTIuMDEyIDM2LjI1IDM2LjI1IDAgMCAwLTUuNzY4LTEuMjIzYy0yLjA0My0uMjY3LTQuMjE0LS4zODctNi41MzktLjM0NmEyMi43NiAyMi43NiAwIDAgMC0zLjUyNy4zNDhjLTEuMTg4LjIwOC0yLjM4NS41MDQtMy41ODYuODc3cy0yLjQwNy44MjMtMy42MTEgMS4zMzhhNDcuOTMgNDcuOTMgMCAwIDAtMy42MDQgMS43M2MtMi4zOTIgMS4yNjktNC43NTQgMi43NTItNy4wNDUgNC4zNjFzLTQuNTExIDMuMzQ0LTYuNjEzIDUuMTExLTQuMDg3IDMuNTY3LTUuOTEgNS4zMTFhMTQyLjEgMTQyLjEgMCAwIDAtNC45NDEgNC45NjljLTIuNTE1IDMuNTQxLTUuNzA1IDYuNjgtNy4yNDYgMTEuMjE5IDE2LjkwNi03LjMwNiAzOC41ODctMy41NTggNTcuMTg5IDIuOTIxLTI1LjAwOSAzMi4xNDQtNTcuMzQ0IDI3Ljg2MS01Ny4zNDQgMjcuODYxczEwLjc3IDI1LjI1NiA1MS45MDggMTEuMjMyYzIyLjU1Ny03LjY4OSAzMy44OS0yNC43NzcgNDEuMTk1LTMwLjg2NyAyMi4xODYtMTAuMTE4IDQzLjY5OS0yNi4wOTYgNTUuMzAxLTQ0LjY0MyA4LjE4Ny01LjY3MSA5LjctNC44MzkgMTcuMDEyLTEwLjg5Ni0yLjYyMSA5Ljk2Mi00LjQ5NyAxNC45NjItMTEuNTE4IDI5LjQ4OC03LjIwOCAxNC45MTQtMTMuNTM5IDMyLjUwNi0xMy41MzkgMzIuNTA2bDI2LjY2Ni0zNi43NThjMy45LTUuNjgxIDEyLjE2Mi0xOC4yNzcgMTUuMDgyLTI0LjE2MiA4Ljc5NiAyLjg2Mi0xLjQ4MiAyNi41MTktMTUuNTE0IDU5LjE5NS05Ljg4NCAyMy4wMTctNy40OTQgMjguNTc0LTcuNDk0IDI4LjU3NGw0OC45MjYtNzAuOTg0YzEzLjA2Ni0yMC41MDEgMTcuNDE5LTE4Ljk0Ni04LjMwOS0zNy4xNTItMjUuNjYyLTE4LjE2MS0yNS42NzgtMTUuNzc0LTE4Ljc0OC0yNS4zMzQgNC41ODYtNi4zMjYgMjAuNzEzLTIxLjMyMi0zLjIwNS0yMy4wMzktMjMuMDc1LTEuNjU3LTE0Ljc3OC0xMC43NTMtMTAuMzk4LTE4Ljc4NSA3Ljg1Ny0xNC40MTEgMi43OTQtMTYuNDE3LS45ODYtMTcuMTE5LTMuODA5LS43MDctMTIuMzM5LS41NS0yMC45NjEuNDcxbC0xMi45NjkgMTA3LjM1Mi0zMi42NDEgNi40OTYgMTguMTQxLTE0NS4wMzktMi4wMDguMTAyLTMuNjk1LjY4NmMtMS4zNDguMjQ4LTIuNjkuNDc2LTMuODU0Ljc3My0yMy4xMTUgMy4wODItNDYuMTg5IDcuNjUtNjkuMzIyIDEwLjU5Mi0yLjI3OS4yNTEtNC41NS41OC02LjgyNC44NzlzLTQuNTUyLjU2OC02Ljg0NC42OTdjLTEuOTc1LjEtMy45NjQuMTA5LTUuOTQ5LjAxYTUxLjMzIDUxLjMzIDAgMCAxLTUuOTI0LS42MzNjLTEuOTU4LS4zMjgtMy44OTItLjc4LTUuNzg1LTEuMzY3cy0zLjc0NS0xLjMxLTUuNTM1LTIuMTg2bC00MC40NDEtMjAuNjkxLTQwLjM0LTIwLjg5NiAyLjQ4OC0xMS4zMTRjLTMuMjcxLTIuODAzLTcuNjM3LTUuOTk4LTExLjI0Mi01Ljk0N3ptMTMxLjYyMSAxMjYuNTc4YzQuMjMxLjA4OCA4LjM4NSAxLjQ4NCAxMi40MiA1LjAyOSA2LjQ1NSA1LjY3MiA4LjkxMiAxMi43MDYgOS4zOTEgMjIuMzAxcy0xLjkyMiAxNi41NTctOC44NDQgMjAuNjUtMTYuMDEgNy4xNjEtMjMuMjExIDMuOTE4LTEwLjk3LTguOTc0LTEyLjczLTE0LjQ5OC0xLjc0LTE2LjEyNiAxLjYwNy0yMy4zNzljMS43MjEtMy43MyAzLjQ2OC02LjU0NCA1LjY5My04LjY1OCAyLjAxNS0yLjc0MyA0Ljc5NC00LjMwOSA3LjcwNy00LjM0MmE4LjE2IDguMTYgMCAwIDEgLjM5OC4wMTljMi41NDQtLjY4OCA1LjA3LTEuMDkzIDcuNTY4LTEuMDQxem0tODEuNTI3LjE2OGM0LjkwNi0uMTAzIDkuOTU5IDEuMDM5IDEzLjk5NCA0LjU4NCA2LjQ1NSA1LjY3MiA5LjE1IDEzLjIyNyAxMC4xMDcgMjMuMDk2cy0yLjc3OSAxNy4wOTctOC42OTMgMjIuMDU5LTEzLjk1OCA2LjMyNy0yMi41NzQgMy42Ny0xNC44ODItMTEuMjk4LTE2LjY0My0xNi44MjItLjY0LTE0LjQxMSAyLjcwNy0yMS42NjRsMS4zOTUtMi45MjRjMS4yNTQtNS44NDYgNS4yNC05Ljk4NSA5Ljg5MS0xMC4yNzEuNDQ2LS4xNi45MDctLjMxMSAxLjM5Ni0uNDQ1IDIuNTg2LS43MDggNS40NzYtMS4yMTkgOC40Mi0xLjI4MXpNMzIxLjA3IDY4LjZjLTY3LjQzLjQ1OC0xMzcuNTQ0LjExNS0yMTIuOTkgMy41OTIgMzcuMTU1IDE5LjkyIDgyLjU3MiAzNC4yNTQgMTA5Ljc4MyA0Ny40My03LjA4NiAxMi42NzgtMTAuNTkyIDI0Ljc4OC0xNC43OTEgMzcuNTlsLS42NzYtLjYwNC0xLjg0IDguMzgxIDM5LjEzOSAyMC4yNzMgMzkuMjI3IDIwLjA5YzEuNjM1LjgyNSAzLjMyNCAxLjU1IDUuMDUzIDIuMTQ2czMuNTAxIDEuMDY1IDUuMzA3IDEuMzgzYTQ2LjMxIDQ2LjMxIDAgMCAwIDQuNzMuNjg0YzEuNTg2LjE0OCAzLjE4LjIxNiA0Ljc3My4yMTdzMy4xODgtLjA2NyA0Ljc3Ny0uMTk3IDMuMTczLS4zMjIgNC43NDYtLjU2NmwzMi44OTMtNC43NSAzMi44NC01LjExM2MzLjc1LS41ODIgNy40NDktMS4zNzEgMTEuMTY0LTIuMDg4IDEuODU3LS4zNTggMy43MTktLjY5OSA1LjU5NC0uOTg2Ljg2Ni0uMTMzIDEuNzQ3LS4yIDIuNjE5LS4zMDctLjU3My0yMy4wMDEtMi43ODEtNTIuMTE3LTIuNzgxLTUyLjExN2wxMy42MDItLjU2OC0yNS4yMjMgMTk3LjM0NCAyNy45MzgtNS41NTkgMjIuNjc2LTE5NC42NjRjMTYuMDg0LS43NDggNjkuNTQ3LTQuMTE5IDgyLjMwNy02LjkzOS01NS40NTMtMjAuNTg5LTEyNS42NzItNDIuODExLTE5MC44NjUtNjQuNjd6Ii8+PC9zdmc+

//! `otarustlings` exercise platform. Clone of
//! [rustlings](https://github.com/rust-lang/rustlings).
//!
//! The point of `otarustlings` is to learn Rust with hands on exercises and
//! small projects.
//!
//! # Updating & Installation
//!
//! To install or update otarustlings, use:
//!
//! ```console
//! cargo install --force otarustlings
//! ```
//!
//! # How to
//!
//! First, initialize the exercises:
//!
//! ```console
//! otarustlings init
//! ```
//!
//! > This creates the folder `exercises` with the latest exercises within.
//!
//! Next, start testing the exercise you wish to solve:
//!
//! ```console
//! otarustlings start
//! ```
//!
//! > You can also start a specific exercise with:
//! >
//! > ```console
//! > otarustlings start week4
//! > ```
//!
//! Finally, open the exercise in your favorite text editor, i.e. Emacs, and
//! solve the exercise in the way which feels natural.
//!
//! Once the exercise is compiles and passes the tests, it is marked with a
//! check mark `✓` in the menu. With `ENTER` you can select the next exercise
//! directly.
//!
//! # How to solve the exercises
//!
//! The semantics of the exercises are usually written as comments. If you see a
//! `_` in an invalid position, you need to replace it with something else. If
//! you see a `// TODO` or `todo!()` you are supposed to do what's called
//! _programming_, or _coding_, at the comment.
//!
//! Crate-like exercises, which are inside a folder, may have some additional
//! files which you need to read or to modify in order to solve the exercise.
//!
//! Don't be afraid to search for solutions online. No real programmer could do
//! their work without the internet and a search engine.
//!
//! # Subcommands
//!
//! ## `init`
//!
//! To create the initial exercise directory structure and write the exercises,
//! use:
//!
//! ```console
//! otarustlings init
//! ```
//!
//! The command creates a directory called `exercises` in the current directory.
//!
//! > Note: `init` does not overwrite old exercises.
//!
//! ## `start`
//!
//! To start testing your changes, use:
//!
//! ```console
//! otarustlings start
//! ```
//!
//! The command must be issued in the parent directory of `exercises`, i.e. the
//! same directory [`init`](#init) was called in.
//!
//! The first thing that is shown is a menu of the exercises. Using up and down
//! arrows, one can change the selection and pressing enter will start testing
//! it.
//!
//! All changes to the file are monitored and tested. The test output is shown
//! in the terminal.
//!
//! To return to menu, press `Q` or `ESC`.
//!
//! To quit `otarustlings`, press `Q` or `ESC` in the menu.
//!
//! > `CTRL-C` can also be used to exit `otarustlings`.
//!
//! ## `test`
//!
//! You can use `otarustlings` to directly test a particular exercise without
//! having to interact with it through the **terminal user interface**.
//!
//! ```console
//! otarustlings test week4/B5
//! ```
//!
//! > The same path qualifier works in [`start`](#start) too.
//!
//! ## `state`
//!
//! **NOTE:** Only modify **THE `state`** if necessary!
//!
//! To reset (remove) `exercises/state.toml` you can run:
//!
//! ```console
//! otarustlings state reset
//! ```
//!
//! # Development
//!
//! `otarustlings` is updated each week to add new exercises.
//!
//! Read the source code, star and fork the project and submit issues to [the
//! repository.](https://gitlab.com/otafablab/otarustlings)
use std::{path::PathBuf, sync::mpsc::Sender};

use crossterm::event::Event;
use state::Exercise;
use thiserror::Error;

pub mod exercise;
pub mod menu;
pub mod state;
pub mod tester;
pub mod utils;

/// Error which composes all the crate's errors together.
#[derive(Error, Debug)]
#[error("otarustlings error: {0:#}")]
pub enum Error {
    /// A [`exercise::CompileError`].
    CompileError(#[from] exercise::CompileError),

    /// A [`exercise::RunError`].
    RunError(#[from] exercise::RunError),

    /// An [`notify::Error`]
    NotifyError(#[from] notify::Error),

    /// An unknown error.
    #[error("unknown otarustlings error")]
    Unknown,
}

/// Result with error being of type [`enum@Error`]
pub type Result<T> = std::result::Result<T, Error>;

/// Arguments that are passed to `rustc` to print in color
pub(crate) const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];

/// A message from the notify and input threads to the compile thread
#[derive(Debug, Clone)]
pub enum Message {
    Notify(PathBuf),
    TestExercise,
    SelectExercise(Exercise),
    ExitExercise,
    Terminate,
    KeyEvent(Event, Sender<bool>),
    Draw,
}