key-mapping 0.7.1

Library allowing for keyboard key code conversion between systems such as the DOM and HID usage-ids
Documentation
use std::{
    env, fs,
    io::{BufWriter, Write},
    path::Path,
};

use serde::Deserialize;

#[derive(Deserialize)]
struct KeyCode {
    pub usage_id: String,
    pub key_code: String,
    pub lang_code: Option<String>,
    pub visual: String,
    pub prefix: String,
    pub key_type: String,
}

impl KeyCode {
    pub fn prefix_as_pascal(&self) -> String {
        let p = self.prefix.to_lowercase();
        let parts = p.split("_");
        parts
            .map(|v| {
                let mut c = v.chars();
                let f = c.next().unwrap();
                f.to_uppercase().collect::<String>() + c.as_str()
            })
            .collect::<Vec<_>>()
            .join("")
    }
}

fn main() {
    println!("cargo::rerun-if-changed=src/key_codes.json");

    let mut rdr = fs::File::open("src/key_codes.json").unwrap();
    let key_codes: Vec<KeyCode> = serde_json::from_reader(&mut rdr).unwrap();
    let keycodes_us = key_codes
        .iter()
        .filter(|v| {
            if let Some(code) = &v.lang_code {
                return code == "US";
            }
            true
        })
        .collect::<Vec<_>>();
    let keycodes_uk = key_codes
        .iter()
        .filter(|v| {
            if let Some(code) = &v.lang_code {
                return code == "UK";
            }
            true
        })
        .collect::<Vec<_>>();

    let mappings_path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs");
    let mut mappings_fs = BufWriter::new(fs::File::create(mappings_path).unwrap());

    // build usage-id enum
    writeln!(
        &mut mappings_fs,
        "/// Keyboard keys as enum values, with usage-id representation."
    )
    .unwrap();
    writeln!(&mut mappings_fs, "#[repr(u8)]").unwrap();
    writeln!(
        &mut mappings_fs,
        "#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))]
#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]"
    )
    .unwrap();
    writeln!(&mut mappings_fs, "pub enum Keys {{").unwrap();
    for key_code in &key_codes {
        writeln!(
            &mut mappings_fs,
            "    {} = {},",
            key_code.prefix_as_pascal(),
            key_code.usage_id
        )
        .unwrap();
    }
    writeln!(&mut mappings_fs, "}}").unwrap();

    // build usage-id to Keys TryFrom
    write!(
        &mut mappings_fs,
        "impl TryFrom<u8> for Keys {{
    type Error = ();
    fn try_from(value: u8) -> Result<Self, Self::Error> {{
        match value {{\n"
    )
    .unwrap();
    for key in &key_codes {
        writeln!(
            &mut mappings_fs,
            "x if x == Self::{0} as u8 => Ok(Self::{0}),",
            key.prefix_as_pascal()
        )
        .unwrap();
    }
    write!(
        &mut mappings_fs,
        "_ => Err(()),
}}}}}}",
    )
    .unwrap();

    // build static mappings for mapped keys
    let mut builder = phf_codegen::Map::new();
    for key in &key_codes {
        let usage_id = hex::decode(key.usage_id.strip_prefix("0x").unwrap()).unwrap()[0];
        builder.entry(
            usage_id,
            format!(
                "MappedKey{{
    usage_id: {},
    dom_key: \"{}\",
    prefix: \"{}\",
    visual: r#\"{}\"#,
    key_type: MappedKeyType::{},
}}",
                key.usage_id, key.key_code, key.prefix, key.visual, key.key_type,
            ),
        );
    }
    writeln!(&mut mappings_fs, "/// usage-id to mapped key info.").unwrap();
    write!(
        &mut mappings_fs,
        "pub static MAPPED_KEYS: phf::Map<u8, MappedKey> = \n{};\n",
        builder.build(),
    )
    .unwrap();

    // build static mappings for US layout
    let mut builder = phf_codegen::Map::new();
    for key_code in &keycodes_us {
        builder.entry(&key_code.key_code, &key_code.usage_id);
    }
    writeln!(
        &mut mappings_fs,
        "/// DOM key to usage-id map for the US layout."
    )
    .unwrap();
    write!(
        &mut mappings_fs,
        "pub static DOM_KEYS_US: phf::Map<&'static str, u8> = \n{};\n",
        builder.build(),
    )
    .unwrap();

    // build static mappings for UK layout
    let mut builder = phf_codegen::Map::new();
    for key_code in &keycodes_uk {
        builder.entry(&key_code.key_code, &key_code.usage_id);
    }
    writeln!(
        &mut mappings_fs,
        "/// DOM key to usage-id map for the UK layout."
    )
    .unwrap();
    write!(
        &mut mappings_fs,
        "pub static DOM_KEYS_UK: phf::Map<&'static str, u8> = \n{};\n",
        builder.build(),
    )
    .unwrap();
}