#![warn(missing_docs, dead_code)]
#![warn(clippy::all, clippy::pedantic, clippy::cargo)]
mod de;
pub mod f32;
pub mod f64;
mod utils;
use num_traits::real::Real;
use serde::Deserialize;
use de::{KleKeyboard, KleLayoutIterator};
use utils::FontSize;
pub type Color = rgb::RGBA8;
const NUM_LEGENDS: usize = 12;
pub(crate) mod color {
use crate::Color;
pub(crate) const BACKGROUND: Color = Color::new(0xEE, 0xEE, 0xEE, 0xFF); pub(crate) const KEY: Color = Color::new(0xCC, 0xCC, 0xCC, 0xFF); pub(crate) const LEGEND: Color = Color::new(0x00, 0x00, 0x00, 0xFF); }
#[derive(Debug, Clone, PartialEq)]
pub struct Legend {
pub text: String,
pub size: usize,
pub color: Color,
}
impl Default for Legend {
fn default() -> Self {
Self {
text: String::default(),
size: usize::from(FontSize::default()),
color: color::LEGEND,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Switch {
pub mount: String,
pub brand: String,
pub typ: String,
}
#[derive(Debug, Clone, PartialEq)]
#[allow(clippy::struct_excessive_bools)]
pub struct Key<T = f64>
where
T: Real,
{
pub legends: [Option<Legend>; NUM_LEGENDS],
pub color: Color,
pub x: T,
pub y: T,
pub width: T,
pub height: T,
pub x2: T,
pub y2: T,
pub width2: T,
pub height2: T,
pub rotation: T,
pub rx: T,
pub ry: T,
pub profile: String,
pub switch: Switch,
pub ghosted: bool,
pub stepped: bool,
pub homing: bool,
pub decal: bool,
}
impl<T> Default for Key<T>
where
T: Real,
{
fn default() -> Self {
Self {
legends: std::array::from_fn(|_| None),
color: color::KEY,
x: T::zero(),
y: T::zero(),
width: T::one(),
height: T::one(),
x2: T::zero(),
y2: T::zero(),
width2: T::one(),
height2: T::one(),
rotation: T::zero(),
rx: T::zero(),
ry: T::zero(),
profile: String::new(),
switch: Switch::default(),
ghosted: false,
stepped: false,
homing: false,
decal: false,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Background {
pub name: String,
pub style: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Metadata {
pub background_color: Color,
pub background: Background,
pub radii: String,
pub name: String,
pub author: String,
pub switch: Switch,
pub plate_mount: bool,
pub pcb_mount: bool,
pub notes: String,
}
impl Default for Metadata {
fn default() -> Self {
Self {
background_color: color::BACKGROUND,
background: Background::default(),
radii: String::new(),
name: String::new(),
author: String::new(),
switch: Switch::default(),
plate_mount: false,
pcb_mount: false,
notes: String::new(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Keyboard<T = f64>
where
T: Real,
{
pub metadata: Metadata,
pub keys: Vec<Key<T>>,
}
impl<'de, T> Deserialize<'de> for Keyboard<T>
where
T: Real + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let KleKeyboard { meta, layout } = KleKeyboard::deserialize(deserializer)?;
Ok(Self {
metadata: meta.into(),
keys: KleLayoutIterator::new(layout).collect(),
})
}
}
#[derive(Debug, Clone)]
pub struct KeyIterator<T = f64>(KleLayoutIterator<T>)
where
T: Real;
impl<'de, T> Deserialize<'de> for KeyIterator<T>
where
T: Real + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let KleKeyboard { meta: _, layout } = KleKeyboard::deserialize(deserializer)?;
Ok(Self(KleLayoutIterator::new(layout)))
}
}
impl<T> Iterator for KeyIterator<T>
where
T: Real,
{
type Item = Key<T>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[cfg(test)]
mod tests {
use isclose::assert_is_close;
use super::*;
#[test]
fn test_legend_default() {
let legend = Legend::default();
assert_eq!(legend.text, "");
assert_eq!(legend.size, 3);
assert_eq!(legend.color, Color::new(0, 0, 0, 255));
}
#[test]
fn test_key_default() {
let key = <Key>::default();
for leg in key.legends {
assert!(leg.is_none());
}
assert_eq!(key.color, Color::new(204, 204, 204, 255));
assert_is_close!(key.x, 0.0);
assert_is_close!(key.y, 0.0);
assert_is_close!(key.width, 1.0);
assert_is_close!(key.height, 1.0);
assert_is_close!(key.x2, 0.0);
assert_is_close!(key.y2, 0.0);
assert_is_close!(key.width2, 1.0);
assert_is_close!(key.height2, 1.0);
assert_is_close!(key.rotation, 0.0);
assert_is_close!(key.rx, 0.0);
assert_is_close!(key.ry, 0.0);
assert_eq!(key.profile, "");
assert_eq!(key.switch.mount, "");
assert_eq!(key.switch.brand, "");
assert_eq!(key.switch.typ, "");
assert!(!key.ghosted);
assert!(!key.stepped);
assert!(!key.homing);
assert!(!key.decal);
}
#[test]
fn test_metadata_default() {
let meta = Metadata::default();
assert_eq!(meta.background_color, Color::new(238, 238, 238, 255));
assert_eq!(meta.background.name, "");
assert_eq!(meta.background.style, "");
assert_eq!(meta.radii, "");
assert_eq!(meta.name, "");
assert_eq!(meta.author, "");
assert_eq!(meta.switch.mount, "");
assert_eq!(meta.switch.brand, "");
assert_eq!(meta.switch.typ, "");
assert!(!meta.plate_mount);
assert!(!meta.pcb_mount);
assert_eq!(meta.notes, "");
}
#[test]
fn test_keyboard_deserialize() {
let kb: Keyboard = serde_json::from_str(
r#"[
{
"name": "test",
"unknown": "key"
},
[
{
"a": 4,
"unknown2": "key"
},
"A",
"B",
"C"
],
[
"D"
]
]"#,
)
.unwrap();
assert_eq!(kb.metadata.name, "test");
assert_eq!(kb.keys.len(), 4);
let kb: Keyboard = serde_json::from_str(r#"[["A"]]"#).unwrap();
assert_eq!(kb.metadata.name, "");
assert_eq!(kb.keys.len(), 1);
let kb: Keyboard = serde_json::from_str(r#"[{"notes": "'tis a test"}]"#).unwrap();
assert_eq!(kb.metadata.notes, "'tis a test");
assert_eq!(kb.keys.len(), 0);
assert!(serde_json::from_str::<Keyboard>("null").is_err());
}
#[test]
fn test_key_iterator_deserialize() {
let keys: Vec<_> = serde_json::from_str::<KeyIterator>(
r#"[
{
"name": "test",
"unknown": "key"
},
[
{
"a": 4,
"unknown2": "key"
},
"A",
"B",
"C"
],
[
"D"
]
]"#,
)
.unwrap()
.collect();
assert_eq!(keys.len(), 4);
assert_eq!(keys[2].legends[0].as_ref().unwrap().text, "C");
let keys: Vec<_> = serde_json::from_str::<KeyIterator>(r#"[["A"]]"#)
.unwrap()
.collect();
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].legends[0].as_ref().unwrap().text, "A");
let keys: Vec<_> = serde_json::from_str::<KeyIterator>(r#"[{"notes": "'tis a test"}]"#)
.unwrap()
.collect();
assert_eq!(keys.len(), 0);
assert!(serde_json::from_str::<KeyIterator>("null").is_err());
}
}