tix-tracker 0.11.0

A terminal issue tracker: markdown + YAML frontmatter issues with a kanban TUI and CLI
Documentation
use crossterm::event::KeyCode;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RolloverDialog {
    pub plans: Vec<(String, String, usize)>,
    pub choice: DialogChoice,
    pub dismissed: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DialogChoice {
    DoRollover,
    Ignore,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DialogEvent {
    None,
    ConfirmDo,
    ConfirmIgnore,
    Dismiss,
}

impl RolloverDialog {
    pub fn new(plans: Vec<(String, String, usize)>) -> Self {
        Self {
            plans,
            choice: DialogChoice::DoRollover,
            dismissed: false,
        }
    }

    pub fn is_active(&self) -> bool {
        !self.plans.is_empty() && !self.dismissed
    }

    pub fn handle_key(&mut self, key: KeyCode) -> DialogEvent {
        if !self.is_active() {
            return DialogEvent::Dismiss;
        }
        match key {
            KeyCode::Left | KeyCode::Right | KeyCode::Tab => {
                self.choice = match self.choice {
                    DialogChoice::DoRollover => DialogChoice::Ignore,
                    DialogChoice::Ignore => DialogChoice::DoRollover,
                };
                DialogEvent::None
            }
            KeyCode::Esc => {
                self.dismissed = true;
                DialogEvent::Dismiss
            }
            KeyCode::Enter => {
                self.dismissed = true;
                match self.choice {
                    DialogChoice::DoRollover => DialogEvent::ConfirmDo,
                    DialogChoice::Ignore => DialogEvent::ConfirmIgnore,
                }
            }
            _ => DialogEvent::None,
        }
    }
}

#[cfg(test)]
mod tests {
    #[allow(clippy::wildcard_imports)]
    use super::*;

    #[test]
    fn dialog_choice_toggles() {
        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
        assert_eq!(dialog.choice, DialogChoice::DoRollover);
        dialog.handle_key(KeyCode::Tab);
        assert_eq!(dialog.choice, DialogChoice::Ignore);
    }

    #[test]
    fn enter_confirms_active_choice() {
        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
        assert_eq!(dialog.handle_key(KeyCode::Enter), DialogEvent::ConfirmDo);

        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
        dialog.handle_key(KeyCode::Right);
        assert_eq!(
            dialog.handle_key(KeyCode::Enter),
            DialogEvent::ConfirmIgnore
        );
    }

    #[test]
    fn esc_dismisses_dialog() {
        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
        assert_eq!(dialog.handle_key(KeyCode::Esc), DialogEvent::Dismiss);
        assert!(!dialog.is_active());
    }
}