strategem-hero 0.7.2

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

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

use crate::error::Result;

pub struct Controls {
    up: KeyCode,
    down: KeyCode,
    left: KeyCode,
    right: KeyCode,
}

impl Controls {
    pub fn wasd() -> Self {
        Self {
            up: KeyCode::Char('w'),
            left: KeyCode::Char('a'),
            down: KeyCode::Char('s'),
            right: KeyCode::Char('d'),
        }
    }

    pub fn arrows() -> Self {
        Self {
            up: KeyCode::Up,
            left: KeyCode::Left,
            down: KeyCode::Down,
            right: KeyCode::Right,
        }
    }
}

impl Display for Controls {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}  - {}, {}  - {}, {}  - {}, {}  - {}",
            crate::strategem::StrategemKey::Left,
            format_key_code(&self.left),
            crate::strategem::StrategemKey::Up,
            format_key_code(&self.up),
            crate::strategem::StrategemKey::Right,
            format_key_code(&self.right),
            crate::strategem::StrategemKey::Down,
            format_key_code(&self.down),
        )
    }
}

#[derive(Debug)]
pub enum Key {
    ArrowUp,
    ArrowDown,
    ArrowLeft,
    ArrowRight,
    Escape,
}

pub fn read(controls: &Controls) -> Result<Option<Key>> {
    match crossterm::event::read()? {
        Event::Key(KeyEvent {
            code,
            kind: KeyEventKind::Press,
            ..
        }) if code.eq(&controls.up) => Ok(Some(Key::ArrowUp)),
        Event::Key(KeyEvent {
            code,
            kind: KeyEventKind::Press,
            ..
        }) if code.eq(&controls.down) => Ok(Some(Key::ArrowDown)),
        Event::Key(KeyEvent {
            code,
            kind: KeyEventKind::Press,
            ..
        }) if code.eq(&controls.left) => Ok(Some(Key::ArrowLeft)),
        Event::Key(KeyEvent {
            code,
            kind: KeyEventKind::Press,
            ..
        }) if code.eq(&controls.right) => Ok(Some(Key::ArrowRight)),
        Event::Key(KeyEvent {
            code: KeyCode::Esc,
            kind: KeyEventKind::Press,
            ..
        }) => Ok(Some(Key::Escape)),
        _ => Ok(None),
    }
}

fn format_key_code(code: &KeyCode) -> String {
    match code {
        KeyCode::Char(ch) => format!("'{ch}'"),
        KeyCode::Up => format!("ArrowUp"),
        KeyCode::Left => format!("ArrowLeft"),
        KeyCode::Down => format!("ArrowDown"),
        KeyCode::Right => format!("ArrowRight"),
        other => format!("{other:?}"),
    }
}