strategem-hero 0.8.1

Simple CLI Game following Strategem Hero mini-game from Helldivers 2
use std::fmt::Display;

use crossterm::event::{KeyCode, KeyEventKind};

use crate::{error::Result, screenln};

pub struct Menu<T: Display> {
    items: Vec<T>,
}

impl<T: Display> Menu<T> {
    pub fn builder() -> MenuBuilder<T> {
        MenuBuilder::new()
    }

    pub fn exec(&self, prompt: &str) -> Result<Option<usize>> {
        let mut idx: usize = 0;

        screenln!("----[ {prompt} ]----------")?;
        loop {
            let _screen_scope = crate::tui::screen::scope();

            self.items.iter().enumerate().for_each(|(i, item)| {
                screenln!("  [{}] {}", if i == idx { '*' } else { ' ' }, item).unwrap();
            });

            if let crossterm::event::Event::Key(ev) = crossterm::event::read()? {
                match ev {
                    crossterm::event::KeyEvent {
                        code: KeyCode::Up,
                        kind: KeyEventKind::Press,
                        ..
                    } => {
                        idx = idx.saturating_sub(1);
                    }
                    crossterm::event::KeyEvent {
                        code: KeyCode::Down,
                        kind: KeyEventKind::Press,
                        ..
                    } => {
                        let next = idx + 1;
                        idx = next.min(self.items.len() - 1);
                    }
                    crossterm::event::KeyEvent {
                        code: KeyCode::Enter,
                        kind: KeyEventKind::Press,
                        ..
                    } => return Ok(Some(idx)),
                    crossterm::event::KeyEvent {
                        code: KeyCode::Char('q'),
                        kind: KeyEventKind::Press,
                        ..
                    } => return Ok(None),

                    _ => (),
                }
            } else {
                break;
            }
        }

        Ok(None)
    }
}

pub struct MenuBuilder<T: Display> {
    items: Vec<T>,
}

impl<T: Display> MenuBuilder<T> {
    pub fn new() -> Self {
        Self { items: Vec::new() }
    }

    pub fn add_item(mut self, value: T) -> Self {
        self.items.push(value);
        self
    }

    pub fn build(self) -> Menu<T> {
        Menu { items: self.items }
    }
}