argv-todo 0.1.0

A Vim-style terminal todo manager with nested tasks, priorities, search, and local SQLite storage
use anyhow::{Context, Result};

use super::ConfigSetting;
use crate::{
    app::App,
    config::TaskView,
    vim_motion::{InputTarget, Motion, VimAction, VimMode},
};

impl App {
    pub(in crate::app) fn open_config(&mut self) -> Result<()> {
        let selected_id = self.selected_todo().map(|todo| todo.id);
        if let Some(config) = &mut self.config {
            config.reload()?;
        }
        self.reconcile_task_view(selected_id);
        self.configuring = true;
        self.config_setting = ConfigSetting::DatabasePath;
        self.vim.set_mode(VimMode::Normal);
        self.message("");
        Ok(())
    }

    fn change_setting(&mut self, forward: bool, count: usize) -> Result<()> {
        if self.config_setting == ConfigSetting::ShowCompleted
            && self.task_view() == TaskView::Split
        {
            self.message("show_completed is always false in Split; use the Completed pane.");
            return Ok(());
        }
        let selected_id = self.selected_todo().map(|todo| todo.id);
        let config = self
            .config
            .as_mut()
            .context("Configuration is unavailable")?;
        let steps = if self.config_setting == ConfigSetting::DefaultPriority {
            if forward {
                count % 3
            } else {
                (3 - count % 3) % 3
            }
        } else {
            count % 2
        };
        if steps == 0 {
            return Ok(());
        }
        match self.config_setting {
            ConfigSetting::DatabasePath => return Ok(()),
            ConfigSetting::TaskView => config.save_task_view(config.task_view.next())?,
            ConfigSetting::DefaultPriority => {
                let mut priority = config.default_priority;
                for _ in 0..steps {
                    priority = priority.next();
                }
                config.save_default_priority(priority)?;
            }
            ConfigSetting::ShowCompleted => config.save_show_completed(!config.show_completed)?,
            ConfigSetting::SortOrder => config.save_sort_order(config.sort_order.next())?,
            ConfigSetting::ShowHints => config.save_show_hints(!config.show_hints)?,
        }
        self.reconcile_task_view(selected_id);
        self.message(format!(
            "Saved. {} = {}.",
            self.config_setting.name(),
            self.config_setting.value(self)
        ));
        Ok(())
    }

    pub(in crate::app) fn apply_config(&mut self, action: VimAction) -> Result<()> {
        if self.vim.input().is_some() {
            match action {
                VimAction::Cancel => {
                    self.vim.end_input();
                    self.editor.reset(String::new());
                    self.message("");
                }
                VimAction::Submit => {
                    self.editor.prepare_submit();
                    self.config
                        .as_mut()
                        .context("Configuration is unavailable")?
                        .save_database_path(self.editor.text())?;
                    self.vim.end_input();
                    self.editor.reset(String::new());
                    self.message("Saved. Applies on next launch.");
                }
                _ => {
                    self.editor.apply(action);
                    self.vim.set_mode(self.editor.mode());
                }
            }
            return Ok(());
        }
        match action {
            VimAction::Cancel => {
                self.configuring = false;
                self.message("");
            }
            VimAction::SwitchPane | VimAction::SwitchPaneBackward => {
                self.config_setting =
                    self.config_setting
                        .step(action == VimAction::SwitchPane, 1, true);
            }
            VimAction::Move(Motion::Up | Motion::Down, count) => {
                self.config_setting = self.config_setting.step(
                    matches!(action, VimAction::Move(Motion::Down, _)),
                    count,
                    false,
                );
            }
            VimAction::FileStart => self.config_setting = ConfigSetting::DatabasePath,
            VimAction::FileEnd => self.config_setting = ConfigSetting::ShowHints,
            VimAction::Move(Motion::Left | Motion::Right, count)
                if self.config_setting != ConfigSetting::DatabasePath =>
            {
                self.change_setting(matches!(action, VimAction::Move(Motion::Right, _)), count)?;
            }
            VimAction::Edit
            | VimAction::AddChild
            | VimAction::Toggle
            | VimAction::ToggleCollapse => {
                if self.config_setting == ConfigSetting::DatabasePath {
                    let config = self
                        .config
                        .as_ref()
                        .context("Configuration is unavailable")?;
                    self.editor.reset(config.database_path.clone());
                    self.vim.begin_input(InputTarget::DatabasePath);
                    self.message("");
                } else {
                    self.change_setting(true, 1)?;
                }
            }
            _ => {}
        }
        Ok(())
    }
}