Some abstraction for further extension

This commit is contained in:
Takashi Kokubun 2021-12-17 17:29:10 -08:00
parent 10845ed7c0
commit 74ee0f36da
No known key found for this signature in database
GPG Key ID: 6FFC433B12EE23DD
4 changed files with 75 additions and 19 deletions

18
src/config/action.rs Normal file
View File

@ -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),
}
}
}

View File

@ -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 crate::config::wm_class::WMClass;
use serde::de::{value, Error, MapAccess, Visitor}; use serde::de::{Error, MapAccess, Visitor};
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
@ -10,37 +11,37 @@ use std::fmt;
pub struct Keymap { pub struct Keymap {
pub name: String, pub name: String,
#[serde(deserialize_with = "keymap_remap")] #[serde(deserialize_with = "keymap_remap")]
pub remap: HashMap<KeyPress, Vec<KeyPress>>, pub remap: HashMap<KeyPress, Vec<Action>>,
pub wm_class: Option<WMClass>, pub wm_class: Option<WMClass>,
} }
// TODO: Add Action trait fn keymap_remap<'de, D>(deserializer: D) -> Result<HashMap<KeyPress, Vec<Action>>, D::Error>
fn keymap_remap<'de, D>(deserializer: D) -> Result<HashMap<KeyPress, Vec<KeyPress>>, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
struct KeymapRemap; struct KeymapRemap;
impl<'de> Visitor<'de> for KeymapRemap { impl<'de> Visitor<'de> for KeymapRemap {
type Value = HashMap<KeyPress, Vec<KeyPress>>; type Value = HashMap<KeyPress, Vec<Action>>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 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<M>(self, map: M) -> Result<Self::Value, M::Error> fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where where
M: MapAccess<'de>, M: MapAccess<'de>,
{ {
let remap: HashMap<String, String> =
Deserialize::deserialize(value::MapAccessDeserializer::new(map))?;
let mut keymap = HashMap::new(); let mut keymap = HashMap::new();
for (from, to) in remap.iter() { while let Some(keypress) = map.next_key::<KeyPress>()? {
let from_keymap = parse_keypress(&from).map_err(M::Error::custom)?; let actions = if let Ok(to) = map.next_value::<Action>() {
let to_keymap = parse_keypress(&to).map_err(M::Error::custom)?; vec![to]
keymap.insert(from_keymap, vec![to_keymap]); } else {
let error: Box<dyn std::error::Error> = "map values must be strings or maps".into();
return Err(error).map_err(M::Error::custom);
};
keymap.insert(keypress, actions);
} }
Ok(keymap) Ok(keymap)

View File

@ -1,6 +1,10 @@
use crate::config::key::parse_key; use crate::config::key::parse_key;
use evdev::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)] #[derive(Debug, Eq, PartialEq, Hash)]
pub struct KeyPress { pub struct KeyPress {
@ -18,7 +22,33 @@ enum Modifier {
Windows, Windows,
} }
pub fn parse_keypress(input: &str) -> Result<KeyPress, Box<dyn Error>> { impl<'de> Deserialize<'de> for KeyPress {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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<E>(self, value: &str) -> Result<KeyPress, E>
where
E: de::Error,
{
parse_keypress(value).map_err(de::Error::custom)
}
}
deserializer.deserialize_any(KeyPressVisitor)
}
}
fn parse_keypress(input: &str) -> Result<KeyPress, Box<dyn error::Error>> {
let keys: Vec<&str> = input.split("-").collect(); let keys: Vec<&str> = input.split("-").collect();
if let Some((key, modifiers)) = keys.split_last() { if let Some((key, modifiers)) = keys.split_last() {
let mut shift = false; let mut shift = false;
@ -32,12 +62,18 @@ pub fn parse_keypress(input: &str) -> Result<KeyPress, Box<dyn Error>> {
Some(Modifier::Control) => control = true, Some(Modifier::Control) => control = true,
Some(Modifier::Alt) => alt = true, Some(Modifier::Alt) => alt = true,
Some(Modifier::Windows) => windows = 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`? // 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 { } else {
Err(format!("empty keypress: {}", input).into()) Err(format!("empty keypress: {}", input).into())
} }

View File

@ -1,3 +1,4 @@
mod action;
mod key; mod key;
mod keymap; mod keymap;
mod keypress; mod keypress;