otarustlings 1.0.0

otafablab rustlings
Documentation
//! [![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 remove that and press them buttons on your keyboard (write code) until the exercise is completed.
//!
//! 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
//! only writes new code, most of the code is copy-pasted from
//! stackoverflow and reddit. Also the later exercises contain tests,
//! marked with `#[test]`, read through them to see more concrete
//! examples of what you are supposed to do.
//!
//! # 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, you 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
//!
//! 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,
}