diff --git a/merlin/actions/mapper.py b/merlin/actions/mapper.py new file mode 100644 index 0000000..aa51184 --- /dev/null +++ b/merlin/actions/mapper.py @@ -0,0 +1,20 @@ +"""ActionMapper — translates gesture event names to action names via config.""" + +from __future__ import annotations + +from typing import Optional + +from merlin.config import MerlinConfig + + +class ActionMapper: + def __init__(self, config: MerlinConfig) -> None: + self._mappings = dict(config.mappings) + + def map(self, gesture: str) -> Optional[str]: + """Return the action name for a gesture, or None if unmapped.""" + return self._mappings.get(gesture) + + def reload(self, config: MerlinConfig) -> None: + """Replace mappings from a new config (e.g. after user edits in UI).""" + self._mappings = dict(config.mappings) diff --git a/tests/test_actions/test_mapper.py b/tests/test_actions/test_mapper.py new file mode 100644 index 0000000..d575ddf --- /dev/null +++ b/tests/test_actions/test_mapper.py @@ -0,0 +1,28 @@ +from merlin.actions.mapper import ActionMapper +from merlin.config import MerlinConfig + + +def test_maps_known_gesture(): + config = MerlinConfig() + mapper = ActionMapper(config) + assert mapper.map("left_blink") == "left_click" + + +def test_returns_none_for_unknown_gesture(): + config = MerlinConfig() + mapper = ActionMapper(config) + assert mapper.map("nonexistent_gesture") is None + + +def test_custom_mapping_overrides_default(): + config = MerlinConfig(mappings={"left_blink": "key_enter"}) + mapper = ActionMapper(config) + assert mapper.map("left_blink") == "key_enter" + + +def test_reload_picks_up_new_config(): + config = MerlinConfig() + mapper = ActionMapper(config) + config.mappings["head_nod"] = "double_click" + mapper.reload(config) + assert mapper.map("head_nod") == "double_click"