diff --git a/src/config/action.rs b/src/config/action.rs new file mode 100644 index 0000000..d108f22 --- /dev/null +++ b/src/config/action.rs @@ -0,0 +1,18 @@ +use crate::config::keypress::KeyPress; + +use serde::Deserialize; +use std::fmt::{Debug, Formatter}; + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum Action { + KeyPress(KeyPress), +} + +impl Debug for Action { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Action::KeyPress(key_press) => key_press.fmt(f), + } + } +} diff --git a/src/config/keymap.rs b/src/config/keymap.rs index bd3e841..5a55f94 100644 --- a/src/config/keymap.rs +++ b/src/config/keymap.rs @@ -1,6 +1,7 @@ -use crate::config::keypress::{parse_keypress, KeyPress}; +use crate::config::action::Action; +use crate::config::keypress::KeyPress; use crate::config::wm_class::WMClass; -use serde::de::{value, Error, MapAccess, Visitor}; +use serde::de::{Error, MapAccess, Visitor}; use serde::{Deserialize, Deserializer}; use std::collections::HashMap; use std::fmt; @@ -10,37 +11,37 @@ use std::fmt; pub struct Keymap { pub name: String, #[serde(deserialize_with = "keymap_remap")] - pub remap: HashMap>, + pub remap: HashMap>, pub wm_class: Option, } -// TODO: Add Action trait - -fn keymap_remap<'de, D>(deserializer: D) -> Result>, D::Error> +fn keymap_remap<'de, D>(deserializer: D) -> Result>, D::Error> where D: Deserializer<'de>, { struct KeymapRemap; impl<'de> Visitor<'de> for KeymapRemap { - type Value = HashMap>; + type Value = HashMap>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("map from string to string, array, or map") + formatter.write_str("map from string to strings or maps") } - fn visit_map(self, map: M) -> Result + fn visit_map(self, mut map: M) -> Result where M: MapAccess<'de>, { - let remap: HashMap = - Deserialize::deserialize(value::MapAccessDeserializer::new(map))?; let mut keymap = HashMap::new(); - for (from, to) in remap.iter() { - let from_keymap = parse_keypress(&from).map_err(M::Error::custom)?; - let to_keymap = parse_keypress(&to).map_err(M::Error::custom)?; - keymap.insert(from_keymap, vec![to_keymap]); + while let Some(keypress) = map.next_key::()? { + let actions = if let Ok(to) = map.next_value::() { + vec![to] + } else { + let error: Box = "map values must be strings or maps".into(); + return Err(error).map_err(M::Error::custom); + }; + keymap.insert(keypress, actions); } Ok(keymap) diff --git a/src/config/keypress.rs b/src/config/keypress.rs index 6bf6c39..75dc1bd 100644 --- a/src/config/keypress.rs +++ b/src/config/keypress.rs @@ -1,6 +1,10 @@ use crate::config::key::parse_key; use evdev::Key; -use std::error::Error; +use serde::de; +use serde::de::Visitor; +use serde::{Deserialize, Deserializer}; +use std::error; +use std::fmt::Formatter; #[derive(Debug, Eq, PartialEq, Hash)] pub struct KeyPress { @@ -18,7 +22,33 @@ enum Modifier { Windows, } -pub fn parse_keypress(input: &str) -> Result> { +impl<'de> Deserialize<'de> for KeyPress { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct KeyPressVisitor; + + impl<'de> Visitor<'de> for KeyPressVisitor { + type Value = KeyPress; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("string or map") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + parse_keypress(value).map_err(de::Error::custom) + } + } + + deserializer.deserialize_any(KeyPressVisitor) + } +} + +fn parse_keypress(input: &str) -> Result> { let keys: Vec<&str> = input.split("-").collect(); if let Some((key, modifiers)) = keys.split_last() { let mut shift = false; @@ -32,12 +62,18 @@ pub fn parse_keypress(input: &str) -> Result> { Some(Modifier::Control) => control = true, Some(Modifier::Alt) => alt = true, Some(Modifier::Windows) => windows = true, - None => return Err(format!("unknown modifier: {}", modifier).into()) + None => return Err(format!("unknown modifier: {}", modifier).into()), } } // TODO: invalidate modifier keys in `key`? - Ok(KeyPress { key: parse_key(key)?, shift, control, alt, windows }) + Ok(KeyPress { + key: parse_key(key)?, + shift, + control, + alt, + windows, + }) } else { Err(format!("empty keypress: {}", input).into()) } diff --git a/src/config/mod.rs b/src/config/mod.rs index 6ceae23..e200238 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,4 @@ +mod action; mod key; mod keymap; mod keypress;