mod command;
mod key;
use std::io;
use std::iter;
use std::process;
use std::str;
use anyhow::{Context, Result};
use serde::Serialize;
use thiserror::Error;
use crate::command::CommandExt;
pub use crate::key::{Key, ParseKeyError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct Mod {
#[serde(
rename = "HIDKeyboardModifierMappingSrc",
serialize_with = "crate::key::serialize"
)]
src: Key,
#[serde(
rename = "HIDKeyboardModifierMappingDst",
serialize_with = "crate::key::serialize"
)]
dst: Key,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
pub struct Mods {
#[serde(rename = "UserKeyMapping")]
mods: Vec<Mod>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct Keyboard {
#[serde(skip)]
name: String,
#[serde(rename = "VendorID")]
vendor_id: u64,
#[serde(rename = "ProductID")]
product_id: u64,
}
impl Mod {
pub fn src(&self) -> Key {
self.src
}
pub fn dst(&self) -> Key {
self.dst
}
pub fn swapped(self) -> Self {
Self {
src: self.dst,
dst: self.src,
}
}
}
fn parse_plist(value: plist::Value) -> Option<Vec<plist::Dictionary>> {
let mut result = Vec::new();
for value in value
.into_dictionary()?
.remove("IORegistryEntryChildren")?
.into_array()?
{
let dicts: Option<Vec<_>> = value
.into_dictionary()?
.remove("IORegistryEntryChildren")?
.into_array()?
.into_iter()
.map(plist::Value::into_dictionary)
.collect();
result.extend(dicts?);
}
Some(result)
}
fn parse_keyboards(value: plist::Value) -> Result<Vec<Keyboard>> {
parse_plist(value)
.context("failed to parse plist")?
.into_iter()
.map(Keyboard::from_plist_dict)
.collect()
}
impl Keyboard {
fn from_plist_dict(mut dict: plist::Dictionary) -> Result<Self> {
let name = dict
.remove("IORegistryEntryName")
.context("expected `IORegistryEntryName`")?
.into_string()
.context("expected valid `IORegistryEntryName` value")?;
let vendor_id = dict
.remove("idVendor")
.context("expected `idVendor`")?
.as_unsigned_integer()
.context("expected valid `idVendor` value")?;
let product_id = dict
.remove("idProduct")
.context("expected `idProduct` key")?
.as_unsigned_integer()
.context("expected valid `idProduct` value")?;
Ok(Keyboard {
name,
vendor_id,
product_id,
})
}
pub fn list() -> Result<Vec<Self>> {
let text = process::Command::new("ioreg")
.args(&["-a", "-l", "-p", "IOUSB"])
.output_text()?;
let obj = plist::Value::from_reader(io::Cursor::new(text))?;
parse_keyboards(obj)
}
pub fn find<P>(predicate: P) -> Result<Option<Self>>
where
P: FnMut(&Self) -> bool,
{
Ok(Self::list()?.into_iter().find(predicate))
}
pub fn name(&self) -> &str {
&self.name
}
pub fn apply(&mut self, mods: Mods) -> Result<()> {
process::Command::new("hidutil")
.arg("property")
.arg("--matching")
.arg(&serde_json::to_string(self)?)
.arg("--set")
.arg(&serde_json::to_string(&mods)?)
.output_text()?;
Ok(())
}
pub fn reset(&mut self) -> Result<()> {
self.apply(Mods::default())
}
}
#[derive(Debug, Error)]
pub enum ParseModError {
#[error(transparent)]
Key(#[from] ParseKeyError),
#[error("failed to parse mod from `{0}`")]
Other(String),
}
impl str::FromStr for Mod {
type Err = ParseModError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let err = || ParseModError::Other(s.to_owned());
if s.is_empty() {
return Err(err());
}
let d = s.chars().next().unwrap();
let mut it = s.splitn(4, d).skip(1);
let src = it.next().ok_or_else(err)?.parse()?;
let dst = it.next().ok_or_else(err)?.parse()?;
Ok(Self { src, dst })
}
}
impl iter::FromIterator<Mod> for Mods {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = Mod>,
{
Mods {
mods: iter.into_iter().collect(),
}
}
}