diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 00000000..2bea5544 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,41 @@ +use clap::{Parser, Subcommand}; + +use crate::dev::DevCommand; + +/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code +#[derive(Parser)] +#[command(version)] +pub struct Args { + #[command(subcommand)] + pub command: Option, + /// Manually run the current exercise using `r` in the watch mode. + /// Only use this if Rustlings fails to detect exercise file changes. + #[arg(long)] + pub manual_run: bool, +} + +#[derive(Subcommand)] +pub enum Command { + /// Initialize the official Rustlings exercises + Init, + /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified + Run { + /// The name of the exercise + name: Option, + }, + /// Check all the exercises, marking them as done or pending accordingly. + CheckAll, + /// Reset a single exercise + Reset { + /// The name of the exercise + name: String, + }, + /// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified + Hint { + /// The name of the exercise + name: Option, + }, + /// Commands for developing (community) Rustlings exercises + #[command(subcommand)] + Dev(DevCommand), +} diff --git a/src/dev.rs b/src/dev.rs index 41fddbeb..f2be6066 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -7,7 +7,7 @@ mod new; mod update; #[derive(Subcommand)] -pub enum DevCommands { +pub enum DevCommand { /// Create a new project for community exercises New { /// The path to create the project in @@ -26,7 +26,7 @@ pub enum DevCommands { Update, } -impl DevCommands { +impl DevCommand { pub fn run(self) -> Result<()> { match self { Self::New { path, no_git } => { diff --git a/src/list.rs b/src/list.rs index a2eee9e1..c60a5299 100644 --- a/src/list.rs +++ b/src/list.rs @@ -11,9 +11,10 @@ use crossterm::{ }; use std::io::{self, StdoutLock, Write}; -use crate::app_state::AppState; - -use self::state::{Filter, ListState}; +use crate::{ + app_state::AppState, + list::state::{Filter, ListState}, +}; mod scroll_state; mod state; diff --git a/src/list/state.rs b/src/list/state.rs index 58aa4961..4e097613 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -15,11 +15,10 @@ use std::{ use crate::{ app_state::AppState, exercise::Exercise, + list::scroll_state::ScrollState, term::{CountedWrite, MaxLenWriter, progress_bar}, }; -use super::scroll_state::ScrollState; - const COL_SPACING: usize = 2; const SELECTED_ROW_ATTRIBUTES: Attributes = Attributes::none() .with(Attribute::Reverse) diff --git a/src/main.rs b/src/main.rs index c39e8629..8f31703b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result, bail}; use app_state::StateFileStatus; -use clap::{Parser, Subcommand}; +use clap::Parser; use std::{ io::{self, IsTerminal, Write}, path::Path, @@ -8,10 +8,15 @@ use std::{ }; use term::{clear_terminal, press_enter_prompt}; -use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile}; +use crate::{ + app_state::AppState, + cli::{Args, Command}, + info_file::InfoFile, +}; mod app_state; mod cargo_toml; +mod cli; mod cmd; mod dev; mod embedded; @@ -25,44 +30,6 @@ mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; -/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code -#[derive(Parser)] -#[command(version)] -struct Args { - #[command(subcommand)] - command: Option, - /// Manually run the current exercise using `r` in the watch mode. - /// Only use this if Rustlings fails to detect exercise file changes. - #[arg(long)] - manual_run: bool, -} - -#[derive(Subcommand)] -enum Subcommands { - /// Initialize the official Rustlings exercises - Init, - /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified - Run { - /// The name of the exercise - name: Option, - }, - /// Check all the exercises, marking them as done or pending accordingly. - CheckAll, - /// Reset a single exercise - Reset { - /// The name of the exercise - name: String, - }, - /// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified - Hint { - /// The name of the exercise - name: Option, - }, - /// Commands for developing (community) Rustlings exercises - #[command(subcommand)] - Dev(DevCommands), -} - fn main() -> Result { let args = Args::parse(); @@ -72,8 +39,8 @@ fn main() -> Result { 'priority_cmd: { match args.command { - Some(Subcommands::Init) => init::init().context("Initialization failed")?, - Some(Subcommands::Dev(dev_command)) => dev_command.run()?, + Some(Command::Init) => init::init().context("Initialization failed")?, + Some(Command::Dev(dev_command)) => dev_command.run()?, _ => break 'priority_cmd, } @@ -141,13 +108,13 @@ fn main() -> Result { watch::watch(&mut app_state, notify_exercise_names)?; } - Some(Subcommands::Run { name }) => { + Some(Command::Run { name }) => { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; } return run::run(&mut app_state); } - Some(Subcommands::CheckAll) => { + Some(Command::CheckAll) => { let mut stdout = io::stdout().lock(); if let Some(first_pending_exercise_ind) = app_state.check_all_exercises(&mut stdout)? { if app_state.current_exercise().done { @@ -175,19 +142,19 @@ fn main() -> Result { app_state.render_final_message(&mut stdout)?; } - Some(Subcommands::Reset { name }) => { + Some(Command::Reset { name }) => { app_state.set_current_exercise_by_name(&name)?; let exercise_path = app_state.reset_current_exercise()?; println!("The exercise {exercise_path} has been reset"); } - Some(Subcommands::Hint { name }) => { + Some(Command::Hint { name }) => { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; } println!("{}", app_state.current_exercise().hint); } // Handled in an earlier match. - Some(Subcommands::Init | Subcommands::Dev(_)) => (), + Some(Command::Init | Command::Dev(_)) => (), } Ok(ExitCode::SUCCESS) diff --git a/src/watch.rs b/src/watch.rs index 3a56b4b6..e0b5ccd0 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -13,10 +13,9 @@ use std::{ use crate::{ app_state::{AppState, ExercisesProgress}, list, + watch::{notify_event::NotifyEventHandler, state::WatchState, terminal_event::InputEvent}, }; -use self::{notify_event::NotifyEventHandler, state::WatchState, terminal_event::InputEvent}; - mod notify_event; mod state; mod terminal_event; diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs index 9c05f10d..edd9c720 100644 --- a/src/watch/notify_event.rs +++ b/src/watch/notify_event.rs @@ -12,7 +12,7 @@ use std::{ time::Duration, }; -use super::{EXERCISE_RUNNING, WatchEvent}; +use crate::watch::{EXERCISE_RUNNING, WatchEvent}; const DEBOUNCE_DURATION: Duration = Duration::from_millis(200); diff --git a/src/watch/state.rs b/src/watch/state.rs index f93ea0cf..1b285d67 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -17,10 +17,9 @@ use crate::{ clear_terminal, exercise::{OUTPUT_CAPACITY, RunnableExercise, solution_link_line}, term::progress_bar, + watch::{InputPauseGuard, WatchEvent, terminal_event::terminal_event_handler}, }; -use super::{InputPauseGuard, WatchEvent, terminal_event::terminal_event_handler}; - const HEADING_ATTRIBUTES: Attributes = Attributes::none() .with(Attribute::Bold) .with(Attribute::Underlined); diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs index 439e4730..4f0685b6 100644 --- a/src/watch/terminal_event.rs +++ b/src/watch/terminal_event.rs @@ -4,7 +4,7 @@ use std::sync::{ mpsc::{Receiver, Sender}, }; -use super::{EXERCISE_RUNNING, WatchEvent}; +use crate::watch::{EXERCISE_RUNNING, WatchEvent}; pub enum InputEvent { Next,