serpl 0.1.33

A simple terminal UI for search and replace, ala VS Code
use std::{fs, io::Write, process::Command};

use regex::Regex;

use super::{action::Action, state::State};
use crate::{
  mode::Mode,
  redux::state::{
    Dialog, FocusedScreen, NotificationState, ReplaceTextState, SearchListState, SearchResultState, SearchTextKind,
    SearchTextState,
  },
  tabs::Tab,
};

pub fn reducer(state: State, action: Action) -> State {
  match action {
    Action::SetSearchList { search_list } => State { search_result: search_list, ..state },
    Action::SetSelectedResult { result } => State { selected_result: result, ..state },
    Action::SetSearchText { text } => {
      let is_dialog_visible = check_dialog_visible(&state);
      if is_dialog_visible {
        return state;
      }
      let search_kind = &state.search_text.kind;
      State { search_text: SearchTextState { text, kind: search_kind.clone() }, ..state }
    },
    Action::SetReplaceText { text } => {
      let is_dialog_visible = check_dialog_visible(&state);
      if is_dialog_visible {
        return state;
      }
      let replace_kind = &state.replace_text.kind;
      State { replace_text: ReplaceTextState { text, kind: replace_kind.clone() }, ..state }
    },
    Action::SetSearchTextKind { kind } => {
      let is_dialog_visible = check_dialog_visible(&state);
      if is_dialog_visible {
        return state;
      }
      State { search_text: SearchTextState { kind, text: state.search_text.text.clone() }, ..state }
    },
    Action::SetReplaceTextKind { kind } => {
      let is_dialog_visible = check_dialog_visible(&state);
      if is_dialog_visible {
        return state;
      }
      State { replace_text: ReplaceTextState { kind, text: state.replace_text.text.clone() }, ..state }
    },
    Action::SetActiveTab { tab } => {
      let is_dialog_visible = check_dialog_visible(&state);
      if is_dialog_visible {
        return state;
      }
      State {
        active_tab: tab,
        previous_focused_screen: state.focused_screen,
        focused_screen: match tab {
          Tab::Search => FocusedScreen::SearchInput,
          Tab::Replace => FocusedScreen::ReplaceInput,
          Tab::SearchResult => FocusedScreen::SearchResultList,
          Tab::Preview => FocusedScreen::Preview,
        },
        ..state
      }
    },
    Action::LoopOverTabs => {
      let is_dialog_visible = check_dialog_visible(&state);
      if is_dialog_visible {
        return state;
      }
      State {
        previous_focused_screen: state.focused_screen,
        active_tab: match state.active_tab {
          Tab::Search => Tab::Replace,
          Tab::Replace => Tab::SearchResult,
          Tab::SearchResult => Tab::Search,
          Tab::Preview => Tab::Preview,
        },
        focused_screen: match state.active_tab {
          Tab::Search => FocusedScreen::ReplaceInput,
          Tab::Replace => FocusedScreen::SearchResultList,
          Tab::SearchResult => FocusedScreen::SearchInput,
          Tab::Preview => FocusedScreen::Preview,
        },
        ..state
      }
    },
    Action::BackLoopOverTabs => {
      let is_dialog_visible = check_dialog_visible(&state);
      if is_dialog_visible {
        return state;
      }
      State {
        previous_focused_screen: state.focused_screen,
        active_tab: match state.active_tab {
          Tab::Search => Tab::SearchResult,
          Tab::Replace => Tab::Search,
          Tab::SearchResult => Tab::Replace,
          Tab::Preview => Tab::Preview,
        },
        focused_screen: match state.active_tab {
          Tab::Search => FocusedScreen::SearchResultList,
          Tab::Replace => FocusedScreen::SearchInput,
          Tab::SearchResult => FocusedScreen::ReplaceInput,
          Tab::Preview => FocusedScreen::Preview,
        },
        ..state
      }
    },
    Action::ChangeMode { mode } => State { mode, ..state },
    Action::SetGlobalLoading { global_loading } => State { global_loading, ..state },
    Action::ResetState => State::new(state.project_root.clone()),
    Action::SetNotification { message, show, ttl, color } => {
      State { notification: NotificationState { message, show, ttl, color }, ..state }
    },
    Action::SetDialog { dialog } => {
      let temporary_dialog = dialog.clone();
      State {
        dialog,
        previous_focused_screen: state.focused_screen.clone(),
        focused_screen: match temporary_dialog {
          Some(Dialog::ConfirmGitDirectory(_)) => FocusedScreen::ConfirmGitDirectoryDialog,
          Some(Dialog::ConfirmReplace(_)) => FocusedScreen::ConfirmReplaceDialog,
          Some(Dialog::HelpDialog(_)) => FocusedScreen::HelpDialog,
          _ => state.focused_screen,
        },
        ..state
      }
    },
    Action::SetFocusedScreen { screen } => {
      State {
        previous_focused_screen: state.focused_screen,
        focused_screen: screen.unwrap_or(FocusedScreen::SearchInput),
        ..state
      }
    },
  }
}

fn check_dialog_visible(state: &State) -> bool {
  match &state.dialog {
    Some(dialog) => {
      match dialog {
        Dialog::ConfirmGitDirectory(dialog) => dialog.show,
        Dialog::ConfirmReplace(dialog) => dialog.show,
        Dialog::HelpDialog(dialog) => dialog.show,
      }
    },
    None => false,
  }
}