use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use super::settings::{GestureOwner, Lighting, SmartShift, deserialize_gesture_owner};
use crate::binding::{Action, Binding, ButtonId, GestureDirection};
use crate::device::{Capabilities, DeviceKind, DeviceModelInfo};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeviceIdentity {
pub display_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model_info: Option<DeviceModelInfo>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub codename: Option<String>,
pub kind: DeviceKind,
pub capabilities: Capabilities,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(from = "RawDeviceConfig")]
pub struct DeviceConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gesture_owner: Option<GestureOwner>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identity: Option<DeviceIdentity>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub bindings: BTreeMap<ButtonId, Binding>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub per_app_bindings: BTreeMap<String, BTreeMap<ButtonId, Action>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dpi_presets: Vec<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dpi: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lighting: Option<Lighting>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub smartshift: Option<SmartShift>,
#[serde(default, skip_serializing_if = "is_false")]
pub invert_scroll: bool,
}
#[allow(
clippy::trivially_copy_pass_by_ref,
reason = "serde's skip_serializing_if requires a fn(&T) -> bool signature"
)]
fn is_false(b: &bool) -> bool {
!*b
}
#[derive(Deserialize)]
struct RawDeviceConfig {
#[serde(default, deserialize_with = "deserialize_gesture_owner")]
gesture_owner: Option<GestureOwner>,
#[serde(default)]
identity: Option<DeviceIdentity>,
#[serde(default)]
bindings: BTreeMap<ButtonId, Binding>,
#[serde(default)]
button_bindings: BTreeMap<ButtonId, Action>,
#[serde(default)]
gesture_bindings: BTreeMap<GestureDirection, Action>,
#[serde(default)]
per_app_bindings: BTreeMap<String, BTreeMap<ButtonId, Action>>,
#[serde(default)]
dpi_presets: Vec<u32>,
#[serde(default)]
dpi: Option<u32>,
#[serde(default)]
lighting: Option<Lighting>,
#[serde(default)]
smartshift: Option<SmartShift>,
#[serde(default)]
invert_scroll: bool,
}
impl From<RawDeviceConfig> for DeviceConfig {
fn from(raw: RawDeviceConfig) -> Self {
let mut bindings = raw.bindings;
if !raw.gesture_bindings.is_empty() {
bindings
.entry(ButtonId::GestureButton)
.or_insert_with(|| Binding::Gesture(raw.gesture_bindings));
}
for (button, action) in raw.button_bindings {
if button == ButtonId::GestureButton {
continue;
}
bindings.entry(button).or_insert(Binding::Single(action));
}
DeviceConfig {
gesture_owner: raw.gesture_owner,
identity: raw.identity,
bindings,
per_app_bindings: raw.per_app_bindings,
dpi_presets: raw.dpi_presets,
dpi: raw.dpi,
lighting: raw.lighting,
smartshift: raw.smartshift,
invert_scroll: raw.invert_scroll,
}
}
}