mist 1.19.0

minimal, improved speedrun timer
use anyhow::Result;
use mist_core::{config::KeybindsRaw, error::MistError::Str};
use sdl2::keyboard::Keycode;
use sdl2::mouse::MouseButton;

#[derive(Debug, PartialEq)]
pub enum Binding {
    Key(Keycode),
    Mouse(MouseButton),
}

impl From<Keycode> for Binding {
    fn from(a: Keycode) -> Binding {
        Binding::Key(a)
    }
}
impl From<MouseButton> for Binding {
    fn from(a: MouseButton) -> Binding {
        Binding::Mouse(a)
    }
}

#[derive(Debug)]
pub struct Keybinds {
    pub pause: Binding,
    pub reset: Binding,
    pub start_split: Binding,
    pub skip_split: Binding,
    pub un_split: Binding,
    pub prev_comp: Binding,
    pub next_comp: Binding,
    pub load_splits: Binding,
    pub load_config: Binding,
    pub dump_state: Binding,
    pub load_state: Binding,
}

impl Keybinds {
    pub fn from_raw(raw: &KeybindsRaw) -> Result<Self> {
        Ok(Keybinds {
            pause: Keycode::from_name(&raw.pause)
                .map(|key| key.into())
                .or(parse_mouse(&raw.pause))
                .ok_or(Str("Pause keybind could not be parsed.".into()))?,
            reset: Keycode::from_name(&raw.reset)
                .map(|key| key.into())
                .or(parse_mouse(&raw.reset))
                .ok_or(Str("Reset keybind could not be parsed.".into()))?,
            start_split: Keycode::from_name(&raw.start_split)
                .map(|key| key.into())
                .or(parse_mouse(&raw.start_split))
                .ok_or(Str("start/split keybind could not be parsed.".into()))?,
            skip_split: Keycode::from_name(&raw.skip_split)
                .map(|key| key.into())
                .or(parse_mouse(&raw.skip_split))
                .ok_or(Str("Skip split keybind could not be parsed".into()))?,
            un_split: Keycode::from_name(&raw.un_split)
                .map(|key| key.into())
                .or(parse_mouse(&raw.un_split))
                .ok_or(Str("Unsplit keybind could not be parsed".into()))?,
            prev_comp: Keycode::from_name(&raw.prev_comp)
                .map(|key| key.into())
                .or(parse_mouse(&raw.prev_comp))
                .ok_or(Str("Prev comparison keybind could not be parsed".into()))?,
            next_comp: Keycode::from_name(&raw.next_comp)
                .map(|key| key.into())
                .or(parse_mouse(&raw.next_comp))
                .ok_or(Str("Next comparison keybind could not be parsed".into()))?,
            load_splits: Keycode::from_name(&raw.load_splits)
                .map(|key| key.into())
                .or(parse_mouse(&raw.load_splits))
                .ok_or(Str("Load splits keybind could not be parsed".into()))?,
            load_config: Keycode::from_name(&raw.load_config)
                .map(|key| key.into())
                .or(parse_mouse(&raw.load_config))
                .ok_or(Str("Load config keybind could not be parsed".into()))?,
            dump_state: Keycode::from_name(&raw.dump_state)
                .map(|key| key.into())
                .or(parse_mouse(&raw.dump_state))
                .ok_or(Str("Dump state keybind could not be parsed".into()))?,
            load_state: Keycode::from_name(&raw.load_state)
                .map(|key| key.into())
                .or(parse_mouse(&raw.load_state))
                .ok_or(Str("Load state keybind could not be parsed".into()))?,
        })
    }
}

fn parse_mouse(i: &str) -> Option<Binding> {
    match i {
        // not the exact names of the enum, because left and right are already arrow keys
        "LeftMouse" => Some(MouseButton::Left.into()),
        "RightMouse" => Some(MouseButton::Right.into()),
        "MiddleMouse" => Some(MouseButton::Middle.into()),
        "X1" => Some(MouseButton::X1.into()),
        "X2" => Some(MouseButton::X2.into()),
        _ => None,
    }
}