1
0
Fork 0
mirror of https://github.com/notohh/rustlings.git synced 2025-06-28 13:57:42 -04:00

Port to Clap

This commit is contained in:
mo8it 2023-08-25 23:18:01 +02:00 committed by liv
parent 2d1da2ab57
commit 5a93f2a4f1
3 changed files with 208 additions and 135 deletions

View file

@ -2,7 +2,7 @@ use crate::exercise::{Exercise, ExerciseList};
use crate::project::RustAnalyzerProject;
use crate::run::{reset, run};
use crate::verify::verify;
use argh::FromArgs;
use clap::{Parser, Subcommand};
use console::Emoji;
use notify::DebouncedEvent;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
@ -25,111 +25,69 @@ mod project;
mod run;
mod verify;
// In sync with crate version
const VERSION: &str = "5.5.1";
#[derive(FromArgs, PartialEq, Debug)]
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
#[derive(Parser)]
#[command(version)]
struct Args {
/// show outputs from the test exercises
#[argh(switch)]
/// Show outputs from the test exercises
#[arg(long)]
nocapture: bool,
/// show the executable version
#[argh(switch, short = 'v')]
version: bool,
#[argh(subcommand)]
nested: Option<Subcommands>,
#[command(subcommand)]
command: Option<Subcommands>,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
#[derive(Subcommand)]
enum Subcommands {
Verify(VerifyArgs),
Watch(WatchArgs),
Run(RunArgs),
Reset(ResetArgs),
Hint(HintArgs),
List(ListArgs),
Lsp(LspArgs),
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "verify")]
/// Verifies all exercises according to the recommended order
struct VerifyArgs {}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "watch")]
/// Reruns `verify` when files were edited
struct WatchArgs {
/// show hints on success
#[argh(switch)]
success_hints: bool,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "run")]
/// Runs/Tests a single exercise
struct RunArgs {
#[argh(positional)]
/// the name of the exercise
name: String,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "reset")]
/// Resets a single exercise using "git stash -- <filename>"
struct ResetArgs {
#[argh(positional)]
/// the name of the exercise
name: String,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "hint")]
/// Returns a hint for the given exercise
struct HintArgs {
#[argh(positional)]
/// the name of the exercise
name: String,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "lsp")]
/// Enable rust-analyzer for exercises
struct LspArgs {}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "list")]
/// Lists the exercises available in Rustlings
struct ListArgs {
#[argh(switch, short = 'p')]
/// show only the paths of the exercises
paths: bool,
#[argh(switch, short = 'n')]
/// show only the names of the exercises
names: bool,
#[argh(option, short = 'f')]
/// provide a string to match exercise names
/// comma separated patterns are acceptable
filter: Option<String>,
#[argh(switch, short = 'u')]
/// display only exercises not yet solved
unsolved: bool,
#[argh(switch, short = 's')]
/// display only exercises that have been solved
solved: bool,
/// Verify all exercises according to the recommended order
Verify,
/// Rerun `verify` when files were edited
Watch {
/// Show hints on success
#[arg(long)]
success_hints: bool,
},
/// Run/Test a single exercise
Run {
/// The name of the exercise
name: String,
},
/// Reset a single exercise using "git stash -- <filename>"
Reset {
/// The name of the exercise
name: String,
},
/// Return a hint for the given exercise
Hint {
/// The name of the exercise
name: String,
},
/// List the exercises available in Rustlings
List {
/// Show only the paths of the exercises
#[arg(short, long)]
paths: bool,
/// Show only the names of the exercises
#[arg(short, long)]
names: bool,
/// Provide a string to match exercise names.
/// Comma separated patterns are accepted
#[arg(short, long)]
filter: Option<String>,
/// Display only exercises not yet solved
#[arg(short, long)]
unsolved: bool,
/// Display only exercises that have been solved
#[arg(short, long)]
solved: bool,
},
/// Enable rust-analyzer for exercises
Lsp,
}
fn main() {
let args: Args = argh::from_env();
let args = Args::parse();
if args.version {
println!("v{VERSION}");
std::process::exit(0);
}
if args.nested.is_none() {
if args.command.is_none() {
println!("\n{WELCOME}\n");
}
@ -153,17 +111,24 @@ fn main() {
let exercises = toml::from_str::<ExerciseList>(toml_str).unwrap().exercises;
let verbose = args.nocapture;
let command = args.nested.unwrap_or_else(|| {
let command = args.command.unwrap_or_else(|| {
println!("{DEFAULT_OUT}\n");
std::process::exit(0);
});
match command {
Subcommands::List(subargs) => {
if !subargs.paths && !subargs.names {
Subcommands::List {
paths,
names,
filter,
unsolved,
solved,
} => {
if !paths && !names {
println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status");
}
let mut exercises_done: u16 = 0;
let filters = subargs.filter.clone().unwrap_or_default().to_lowercase();
let filters = filter.clone().unwrap_or_default().to_lowercase();
exercises.iter().for_each(|e| {
let fname = format!("{}", e.path.display());
let filter_cond = filters
@ -177,14 +142,14 @@ fn main() {
"Pending"
};
let solve_cond = {
(e.looks_done() && subargs.solved)
|| (!e.looks_done() && subargs.unsolved)
|| (!subargs.solved && !subargs.unsolved)
(e.looks_done() && solved)
|| (!e.looks_done() && unsolved)
|| (!solved && !unsolved)
};
if solve_cond && (filter_cond || subargs.filter.is_none()) {
let line = if subargs.paths {
if solve_cond && (filter_cond || filter.is_none()) {
let line = if paths {
format!("{fname}\n")
} else if subargs.names {
} else if names {
format!("{}\n", e.name)
} else {
format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name)
@ -214,30 +179,30 @@ fn main() {
std::process::exit(0);
}
Subcommands::Run(subargs) => {
let exercise = find_exercise(&subargs.name, &exercises);
Subcommands::Run { name } => {
let exercise = find_exercise(&name, &exercises);
run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1));
}
Subcommands::Reset(subargs) => {
let exercise = find_exercise(&subargs.name, &exercises);
Subcommands::Reset { name } => {
let exercise = find_exercise(&name, &exercises);
reset(exercise).unwrap_or_else(|_| std::process::exit(1));
}
Subcommands::Hint(subargs) => {
let exercise = find_exercise(&subargs.name, &exercises);
Subcommands::Hint { name } => {
let exercise = find_exercise(&name, &exercises);
println!("{}", exercise.hint);
}
Subcommands::Verify(_subargs) => {
Subcommands::Verify => {
verify(&exercises, (0, exercises.len()), verbose, false)
.unwrap_or_else(|_| std::process::exit(1));
}
Subcommands::Lsp(_subargs) => {
Subcommands::Lsp => {
let mut project = RustAnalyzerProject::new();
project
.get_sysroot_src()
@ -256,7 +221,7 @@ fn main() {
}
}
Subcommands::Watch(_subargs) => match watch(&exercises, verbose, _subargs.success_hints) {
Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) {
Err(e) => {
println!(
"Error: Could not watch your progress. Error message was {:?}.",