diff --git a/src/app_state.rs b/src/app_state.rs index 2ab75268..98687818 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -7,9 +7,15 @@ use crossterm::{ use std::{ fs::{self, File}, io::{Read, StdoutLock, Write}, + path::Path, + process::{Command, Stdio}, }; -use crate::{exercise::Exercise, info_file::ExerciseInfo}; +use crate::{ + embedded::{WriteStrategy, EMBEDDED_FILES}, + exercise::Exercise, + info_file::ExerciseInfo, +}; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -31,6 +37,7 @@ pub struct AppState { n_done: u16, final_message: String, file_buf: Vec, + official_exercises: bool, } impl AppState { @@ -111,6 +118,7 @@ impl AppState { n_done: 0, final_message, file_buf: Vec::with_capacity(2048), + official_exercises: !Path::new("info.toml").exists(), }; let state_file_status = slf.update_from_file(); @@ -172,6 +180,53 @@ impl AppState { Ok(()) } + fn reset_path(&self, path: &str) -> Result<()> { + if self.official_exercises { + return EMBEDDED_FILES + .write_exercise_to_disk(path, WriteStrategy::Overwrite) + .with_context(|| format!("Failed to reset the exercise {path}")); + } + + let output = Command::new("git") + .arg("stash") + .arg("push") + .arg("--") + .arg(path) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .output() + .with_context(|| format!("Failed to run `git stash push -- {path}`"))?; + + if !output.status.success() { + bail!( + "`git stash push -- {path}` didn't run successfully: {}", + String::from_utf8_lossy(&output.stderr), + ); + } + + Ok(()) + } + + pub fn reset_current_exercise(&mut self) -> Result<&'static str> { + let path = self.current_exercise().path; + self.set_pending(self.current_exercise_ind)?; + self.reset_path(path)?; + + Ok(path) + } + + pub fn reset_exercise_by_ind(&mut self, exercise_ind: usize) -> Result<&'static str> { + if exercise_ind >= self.exercises.len() { + bail!(BAD_INDEX_ERR); + } + + let path = self.exercises[exercise_ind].path; + self.set_pending(exercise_ind)?; + self.reset_path(path)?; + + Ok(path) + } + fn next_pending_exercise_ind(&self) -> Option { if self.current_exercise_ind == self.exercises.len() - 1 { // The last exercise is done. diff --git a/src/exercise.rs b/src/exercise.rs index 7f924f90..eb7b725e 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,17 +1,13 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, fs, path::Path, - process::{Command, Output, Stdio}, + process::{Command, Output}, }; -use crate::{ - embedded::{WriteStrategy, EMBEDDED_FILES}, - info_file::Mode, - DEVELOPING_OFFICIAL_RUSTLINGS, -}; +use crate::{info_file::Mode, DEVELOPING_OFFICIAL_RUSTLINGS}; pub struct TerminalFileLink<'a> { path: &'a str, @@ -87,34 +83,6 @@ impl Exercise { } } - pub fn reset(&self) -> Result<()> { - if Path::new("info.toml").exists() { - let output = Command::new("git") - .arg("stash") - .arg("push") - .arg("--") - .arg(self.path) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .output() - .with_context(|| format!("Failed to run `git stash push -- {}`", self.path))?; - - if !output.status.success() { - bail!( - "`git stash push -- {}` didn't run successfully: {}", - self.path, - String::from_utf8_lossy(&output.stderr), - ); - } - } else { - EMBEDDED_FILES - .write_exercise_to_disk(self.path, WriteStrategy::Overwrite) - .with_context(|| format!("Failed to reset the exercise {self}"))?; - } - - Ok(()) - } - pub fn terminal_link(&self) -> StyledContent> { style(TerminalFileLink { path: self.path }) .underlined() diff --git a/src/list/state.rs b/src/list/state.rs index 2a1fef18..0253bb97 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -217,23 +217,22 @@ impl<'a> UiState<'a> { return Ok(self); }; - let (ind, exercise) = self + let ind = self .app_state .exercises() .iter() .enumerate() .filter_map(|(ind, exercise)| match self.filter { - Filter::Done => exercise.done.then_some((ind, exercise)), - Filter::Pending => (!exercise.done).then_some((ind, exercise)), - Filter::None => Some((ind, exercise)), + Filter::Done => exercise.done.then_some(ind), + Filter::Pending => (!exercise.done).then_some(ind), + Filter::None => Some(ind), }) .nth(selected) .context("Invalid selection index")?; - exercise.reset()?; + let exercise_path = self.app_state.reset_exercise_by_ind(ind)?; self.message - .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; - self.app_state.set_pending(ind)?; + .write_fmt(format_args!("The exercise {exercise_path} has been reset"))?; Ok(self.with_updated_rows()) } diff --git a/src/main.rs b/src/main.rs index c8940af2..04697379 100644 --- a/src/main.rs +++ b/src/main.rs @@ -158,10 +158,8 @@ fn main() -> Result<()> { } Some(Subcommands::Reset { name }) => { app_state.set_current_exercise_by_name(&name)?; - let exercise = app_state.current_exercise(); - exercise.reset()?; - println!("The exercise {exercise} has been reset!"); - app_state.set_pending(app_state.current_exercise_ind())?; + let exercise_path = app_state.reset_current_exercise()?; + println!("The exercise {exercise_path} has been reset"); } Some(Subcommands::Hint { name }) => { app_state.set_current_exercise_by_name(&name)?;