2024-04-15 19:22:54 -04:00
|
|
|
use anyhow::{bail, Context, Result};
|
2024-04-14 21:36:12 -04:00
|
|
|
use app_state::StateFileStatus;
|
2023-08-25 17:18:01 -04:00
|
|
|
use clap::{Parser, Subcommand};
|
2024-04-14 10:03:49 -04:00
|
|
|
use crossterm::{
|
|
|
|
terminal::{Clear, ClearType},
|
|
|
|
ExecutableCommand,
|
|
|
|
};
|
|
|
|
use std::{
|
|
|
|
io::{self, BufRead, Write},
|
|
|
|
path::Path,
|
|
|
|
process::exit,
|
|
|
|
};
|
2019-01-09 14:33:43 -05:00
|
|
|
|
2024-04-18 05:31:08 -04:00
|
|
|
use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit};
|
|
|
|
|
2024-04-10 20:51:02 -04:00
|
|
|
mod app_state;
|
2024-04-21 14:22:01 -04:00
|
|
|
mod cargo_toml;
|
2024-04-15 17:54:57 -04:00
|
|
|
mod dev;
|
2024-03-28 16:06:36 -04:00
|
|
|
mod embedded;
|
2019-04-11 16:41:24 -04:00
|
|
|
mod exercise;
|
2024-04-13 19:15:43 -04:00
|
|
|
mod info_file;
|
2024-03-28 20:29:41 -04:00
|
|
|
mod init;
|
2024-04-06 21:03:37 -04:00
|
|
|
mod list;
|
2024-04-09 13:37:39 -04:00
|
|
|
mod progress_bar;
|
2019-01-09 14:33:43 -05:00
|
|
|
mod run;
|
2024-04-06 19:17:53 -04:00
|
|
|
mod watch;
|
2018-05-14 12:41:58 -04:00
|
|
|
|
2024-04-15 19:22:54 -04:00
|
|
|
const CURRENT_FORMAT_VERSION: u8 = 1;
|
2024-04-21 13:26:19 -04:00
|
|
|
const DEBUG_PROFILE: bool = {
|
2024-04-17 09:55:50 -04:00
|
|
|
#[allow(unused_assignments, unused_mut)]
|
|
|
|
let mut debug_profile = false;
|
|
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
{
|
|
|
|
debug_profile = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_profile
|
|
|
|
};
|
2024-04-15 19:22:54 -04:00
|
|
|
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
|
2023-08-25 17:18:01 -04:00
|
|
|
#[derive(Parser)]
|
|
|
|
#[command(version)]
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
struct Args {
|
2023-08-25 17:18:01 -04:00
|
|
|
#[command(subcommand)]
|
|
|
|
command: Option<Subcommands>,
|
2024-04-14 11:10:53 -04:00
|
|
|
/// Manually run the current exercise using `r` or `run` in the watch mode.
|
|
|
|
/// Only use this if Rustlings fails to detect exercise file changes.
|
|
|
|
#[arg(long)]
|
|
|
|
manual_run: bool,
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
}
|
|
|
|
|
2023-08-25 17:18:01 -04:00
|
|
|
#[derive(Subcommand)]
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
enum Subcommands {
|
2024-03-28 20:29:41 -04:00
|
|
|
/// Initialize Rustlings
|
|
|
|
Init,
|
2024-04-10 20:51:02 -04:00
|
|
|
/// Run a single exercise. Runs the next pending exercise if the exercise name is not specified.
|
2023-08-25 17:18:01 -04:00
|
|
|
Run {
|
|
|
|
/// The name of the exercise
|
2024-04-10 20:51:02 -04:00
|
|
|
name: Option<String>,
|
2023-08-25 17:18:01 -04:00
|
|
|
},
|
2024-03-28 17:11:16 -04:00
|
|
|
/// Reset a single exercise
|
2023-08-25 17:18:01 -04:00
|
|
|
Reset {
|
|
|
|
/// The name of the exercise
|
|
|
|
name: String,
|
|
|
|
},
|
2024-04-18 11:17:39 -04:00
|
|
|
/// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified.
|
2023-08-25 17:18:01 -04:00
|
|
|
Hint {
|
|
|
|
/// The name of the exercise
|
2024-04-18 11:17:39 -04:00
|
|
|
name: Option<String>,
|
2023-08-25 17:18:01 -04:00
|
|
|
},
|
2024-04-15 17:54:57 -04:00
|
|
|
#[command(subcommand)]
|
|
|
|
Dev(DevCommands),
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
}
|
|
|
|
|
2024-03-24 22:46:56 -04:00
|
|
|
fn main() -> Result<()> {
|
2023-08-25 17:18:01 -04:00
|
|
|
let args = Args::parse();
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
|
2024-04-21 13:34:55 -04:00
|
|
|
if !DEBUG_PROFILE && Path::new("dev/rustlings-repo.txt").exists() {
|
|
|
|
bail!("{OLD_METHOD_ERR}");
|
|
|
|
}
|
|
|
|
|
2024-04-11 19:24:01 -04:00
|
|
|
which::which("cargo").context(CARGO_NOT_FOUND_ERR)?;
|
2019-11-11 11:15:14 -05:00
|
|
|
|
2024-04-15 21:15:14 -04:00
|
|
|
match args.command {
|
|
|
|
Some(Subcommands::Init) => {
|
2024-04-21 13:26:19 -04:00
|
|
|
if DEBUG_PROFILE {
|
|
|
|
bail!("Disabled in the debug build");
|
2024-04-18 05:28:28 -04:00
|
|
|
}
|
|
|
|
|
2024-04-17 09:55:50 -04:00
|
|
|
return init::init().context("Initialization failed");
|
2024-04-15 21:15:14 -04:00
|
|
|
}
|
2024-04-17 09:55:50 -04:00
|
|
|
Some(Subcommands::Dev(dev_command)) => return dev_command.run(),
|
2024-04-15 21:15:14 -04:00
|
|
|
_ => (),
|
2024-04-14 20:11:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if !Path::new("exercises").is_dir() {
|
2024-04-11 19:24:01 -04:00
|
|
|
println!("{PRE_INIT_MSG}");
|
2024-03-28 20:29:41 -04:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2024-04-17 09:55:50 -04:00
|
|
|
let info_file = InfoFile::parse()?;
|
|
|
|
|
|
|
|
if info_file.format_version > CURRENT_FORMAT_VERSION {
|
|
|
|
bail!(FORMAT_VERSION_HIGHER_ERR);
|
|
|
|
}
|
|
|
|
|
2024-04-14 10:03:49 -04:00
|
|
|
let (mut app_state, state_file_status) = AppState::new(
|
|
|
|
info_file.exercises,
|
|
|
|
info_file.final_message.unwrap_or_default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Some(welcome_message) = info_file.welcome_message {
|
|
|
|
match state_file_status {
|
|
|
|
StateFileStatus::NotRead => {
|
|
|
|
let mut stdout = io::stdout().lock();
|
|
|
|
stdout.execute(Clear(ClearType::All))?;
|
|
|
|
|
|
|
|
let welcome_message = welcome_message.trim();
|
|
|
|
write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?;
|
|
|
|
stdout.flush()?;
|
|
|
|
|
|
|
|
io::stdin().lock().read_until(b'\n', &mut Vec::new())?;
|
2024-04-14 10:07:17 -04:00
|
|
|
|
|
|
|
stdout.execute(Clear(ClearType::All))?;
|
2024-04-14 10:03:49 -04:00
|
|
|
}
|
|
|
|
StateFileStatus::Read => (),
|
|
|
|
}
|
|
|
|
}
|
2024-04-06 19:17:53 -04:00
|
|
|
|
2024-04-04 21:04:53 -04:00
|
|
|
match args.command {
|
2024-04-13 19:15:43 -04:00
|
|
|
None => {
|
2024-04-14 11:10:53 -04:00
|
|
|
let notify_exercise_paths: Option<&'static [&'static str]> = if args.manual_run {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
// For the the notify event handler thread.
|
|
|
|
// Leaking is not a problem because the slice lives until the end of the program.
|
|
|
|
Some(
|
|
|
|
app_state
|
|
|
|
.exercises()
|
|
|
|
.iter()
|
|
|
|
.map(|exercise| exercise.path)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.leak(),
|
|
|
|
)
|
|
|
|
};
|
2024-04-13 19:15:43 -04:00
|
|
|
|
|
|
|
loop {
|
2024-04-15 17:54:57 -04:00
|
|
|
match watch::watch(&mut app_state, notify_exercise_paths)? {
|
2024-04-13 19:15:43 -04:00
|
|
|
WatchExit::Shutdown => break,
|
|
|
|
// It is much easier to exit the watch mode, launch the list mode and then restart
|
|
|
|
// the watch mode instead of trying to pause the watch threads and correct the
|
|
|
|
// watch state.
|
2024-04-15 17:54:57 -04:00
|
|
|
WatchExit::List => list::list(&mut app_state)?,
|
2024-04-13 19:15:43 -04:00
|
|
|
}
|
2024-04-09 20:12:50 -04:00
|
|
|
}
|
2024-04-13 19:15:43 -04:00
|
|
|
}
|
2024-04-04 21:04:53 -04:00
|
|
|
Some(Subcommands::Run { name }) => {
|
2024-04-10 20:51:02 -04:00
|
|
|
if let Some(name) = name {
|
|
|
|
app_state.set_current_exercise_by_name(&name)?;
|
|
|
|
}
|
2024-04-15 17:54:57 -04:00
|
|
|
run::run(&mut app_state)?;
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
}
|
2024-04-04 21:04:53 -04:00
|
|
|
Some(Subcommands::Reset { name }) => {
|
2024-04-10 20:51:02 -04:00
|
|
|
app_state.set_current_exercise_by_name(&name)?;
|
2024-04-18 06:41:17 -04:00
|
|
|
let exercise_path = app_state.reset_current_exercise()?;
|
|
|
|
println!("The exercise {exercise_path} has been reset");
|
2022-08-17 10:31:53 -04:00
|
|
|
}
|
2024-04-04 21:04:53 -04:00
|
|
|
Some(Subcommands::Hint { name }) => {
|
2024-04-18 11:17:39 -04:00
|
|
|
if let Some(name) = name {
|
|
|
|
app_state.set_current_exercise_by_name(&name)?;
|
|
|
|
}
|
2024-04-10 20:51:02 -04:00
|
|
|
println!("{}", app_state.current_exercise().hint);
|
feat: Replace clap with argh
I’ve been wanting to do this for a while, but always procrastinated on it. We’ve been using Clap since the 2.0 rewrite, but Clap is known to be a fairly heavy library. Since Rustlings is usually peoples’ first contact with a Rust compilation, I think it’s in our best interests that this complation is as fast as possible. In effect, replacing Clap with the smaller, structopt-style `argh` reduces the amount of crates needing to be compiled from 82 to 60.
I also think this makes the code way easier to read, we don’t need to use Clap’s methods anymore, but can switch over to using pure Rust methods, e.g., switches are booleans, options are Option<String>s or the like, and subcommands are just structs.
2021-04-20 06:46:49 -04:00
|
|
|
}
|
2024-04-15 21:15:14 -04:00
|
|
|
// Handled in an earlier match.
|
|
|
|
Some(Subcommands::Init | Subcommands::Dev(_)) => (),
|
2018-11-26 05:10:38 -05:00
|
|
|
}
|
2024-03-24 22:46:56 -04:00
|
|
|
|
|
|
|
Ok(())
|
2018-05-06 12:59:50 -04:00
|
|
|
}
|
2024-04-11 19:24:01 -04:00
|
|
|
|
2024-04-21 13:34:55 -04:00
|
|
|
const OLD_METHOD_ERR: &str = "You are trying to run Rustlings using the old method before v6.
|
|
|
|
The new method doesn't include cloning the Rustlings' repository.
|
|
|
|
Please follow the instructions in the README:
|
|
|
|
https://github.com/rust-lang/rustlings#getting-started";
|
|
|
|
|
2024-04-11 19:24:01 -04:00
|
|
|
const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`.
|
|
|
|
Did you already install Rust?
|
|
|
|
Try running `cargo --version` to diagnose the problem.";
|
|
|
|
|
2024-04-15 19:22:54 -04:00
|
|
|
const FORMAT_VERSION_HIGHER_ERR: &str =
|
|
|
|
"The format version specified in the `info.toml` file is higher than the last one supported.
|
|
|
|
It is possible that you have an outdated version of Rustlings.
|
|
|
|
Try to install the latest Rustlings version first.";
|
|
|
|
|
2024-04-11 19:24:01 -04:00
|
|
|
const PRE_INIT_MSG: &str = r"
|
2024-04-14 20:11:27 -04:00
|
|
|
Welcome to...
|
2024-04-11 19:24:01 -04:00
|
|
|
_ _ _
|
|
|
|
_ __ _ _ ___| |_| (_)_ __ __ _ ___
|
|
|
|
| '__| | | / __| __| | | '_ \ / _` / __|
|
|
|
|
| | | |_| \__ \ |_| | | | | | (_| \__ \
|
|
|
|
|_| \__,_|___/\__|_|_|_| |_|\__, |___/
|
|
|
|
|___/
|
|
|
|
|
|
|
|
The `exercises` directory wasn't found in the current directory.
|
|
|
|
If you are just starting with Rustlings, run the command `rustlings init` to initialize it.";
|