keymaps 1.2.0

A rust crate which provides a standardized encoding for key codes
Documentation
// keymaps - A rust crate which provides a standardized encoding for key codes
//
// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This file is part of the keymaps crate.
//
// You should have received a copy of the License along with this program.
// If not, see <https://www.gnu.org/licenses/lgpl-3.0.txt>.

use std::{fmt, str::FromStr};

use serde::{
    Deserialize, Deserializer, Serialize, Serializer,
    de::{self, Visitor},
};

use super::{Key, Keys};

impl Serialize for Key {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.to_string().as_str())
    }
}
impl Serialize for Keys {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.to_string().as_str())
    }
}

struct KeyVisitor;

impl Visitor<'_> for KeyVisitor {
    type Value = Key;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("an Key string representation (e.g., <C-c>)")
    }

    fn visit_borrowed_str<E>(self, v: &'_ str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Key::from_str(v).map_err(de::Error::custom)
    }
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Key::from_str(v).map_err(de::Error::custom)
    }
    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Key::from_str(&v).map_err(de::Error::custom)
    }
}

impl<'de> Deserialize<'de> for Key {
    fn deserialize<D>(deserializer: D) -> Result<Key, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(KeyVisitor)
    }
}

struct KeysVisitor;

impl Visitor<'_> for KeysVisitor {
    type Value = Keys;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("an Key string representation (e.g., <C-c>)")
    }

    fn visit_borrowed_str<E>(self, v: &'_ str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Keys::from_str(v).map_err(de::Error::custom)
    }
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Keys::from_str(v).map_err(de::Error::custom)
    }
    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Keys::from_str(&v).map_err(de::Error::custom)
    }
}

impl<'de> Deserialize<'de> for Keys {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(KeysVisitor)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use serde::{Deserialize, Serialize};

    use crate::key_repr::{Key, Keys};

    #[derive(Serialize, Deserialize, PartialEq, Debug)]
    struct KeysMap {
        map: HashMap<Keys, String>,
    }
    #[derive(Serialize, Deserialize, PartialEq, Debug)]
    struct KeyMap {
        map: HashMap<Key, String>,
    }

    #[test]
    fn test_long_modifiers() {
        let mut map = HashMap::new();
        map.insert("<Alt+Ctrl+Meta+Shift-Q>".parse().unwrap(), "hi".to_owned());
        let input = KeysMap { map };

        let json = serde_json::to_string(&input)
            .map_err(|e| panic!("{}", e))
            .unwrap();
        eprintln!("Map is:\n{}", &json);

        let output = serde_json::from_str(&json)
            .map_err(|e| panic!("{}", e))
            .unwrap();

        assert_eq!(input, output);
    }

    #[test]
    fn test_json_roundtrip() {
        let mut map = HashMap::new();
        map.insert("<CA-B>gh".parse().unwrap(), "hi".to_owned());
        let input = KeysMap { map };

        let json = serde_json::to_string(&input)
            .map_err(|e| panic!("{}", e))
            .unwrap();
        eprintln!("Map is:\n{}", &json);

        let output = serde_json::from_str(&json)
            .map_err(|e| panic!("{}", e))
            .unwrap();

        assert_eq!(input, output);
    }
}