okey-cli 0.1.2

An advanced, easy-to-use key remapper for Linux
Documentation
use std::collections::HashMap;

use smallvec::SmallVec;

use crate::config::schema::{KeyAction, LayerDefinition, LayerModifierKind};

use super::{adapter::InputResult, shared::RawKeyCode};

#[derive(Debug)]
pub struct LayerManager {
    layer_map: HashMap<RawKeyCode, LayerDefinition>,
    layer_stack: SmallVec<[RawKeyCode; 5]>,
}

impl LayerManager {
    pub fn new(definitions: HashMap<String, LayerDefinition>) -> Self {
        Self {
            layer_map: definitions
                .into_values()
                .map(|value| (value.modifier.get_modifer().value(), value))
                .collect(),
            layer_stack: SmallVec::default(),
        }
    }

    pub fn map(&mut self, action: KeyAction) -> KeyAction {
        let KeyAction::KeyCode(code) = &action else {
            return action;
        };

        for layer in self.layer_stack.iter().rev() {
            let mapped_action = self
                .layer_map
                .get(layer)
                .and_then(|layer| layer.keys.get(code));

            if let Some(action) = mapped_action {
                return action.clone();
            };
        }

        action
    }

    pub fn handle_press(&mut self, code: RawKeyCode) -> Option<InputResult> {
        if let Some(config) = self.layer_map.get(&code) {
            match config.modifier.get_modifer_kind() {
                LayerModifierKind::Toggle if self.layer_stack.contains(&code) => {
                    self.layer_stack.retain(|layer| *layer != code)
                }
                _ if !self.layer_stack.contains(&code) => self.layer_stack.push(code),
                _ => {}
            };

            Some(InputResult::None)
        } else {
            None
        }
    }

    pub fn handle_hold(&mut self, code: RawKeyCode) -> Option<InputResult> {
        if self.layer_stack.contains(&code) {
            Some(InputResult::None)
        } else {
            None
        }
    }

    pub fn handle_release(&mut self, code: RawKeyCode) -> Option<InputResult> {
        if let Some(definition) = self.layer_map.get(&code) {
            if let LayerModifierKind::Momentary = definition.modifier.get_modifer_kind() {
                self.layer_stack.retain(|layer_code| *layer_code != code)
            }

            Some(InputResult::None)
        } else {
            let result = self.layer_stack.iter().rev().find(|code| {
                self.layer_map
                    .get(code)
                    .map(|layer| layer.modifier.get_modifer_kind())
                    .is_some_and(|kind| matches!(kind, LayerModifierKind::Oneshoot))
            });

            if let Some(result) = result.cloned() {
                self.layer_stack.retain(|code| *code != result);
            }

            None
        }
    }
}