mod hex;
mod hid;
use std::fmt::Write;
use anyhow::{bail, Result};
use clap::{AppSettings, Parser};
use crate::hex::Hex;
use crate::hid::{Device, Mod};
#[derive(Debug, Parser)]
#[clap(
disable_colored_help = true,
setting = AppSettings::DeriveDisplayOrder
)]
struct Opt {
#[clap(long, conflicts_with_all = &["reset", "dump", "swap", "map"])]
list: bool,
#[clap(long, conflicts_with_all = &["list", "swap", "map"])]
reset: bool,
#[clap(long)]
dump: bool,
#[clap(short, long, value_name = "SRC:DST")]
swap: Vec<Mod>,
#[clap(short, long, value_name = "SRC:DST")]
map: Vec<Mod>,
#[clap(long, value_name = "NAME")]
name: Option<String>,
#[clap(long, value_name = "VENDOR-ID")]
vendor_id: Option<Hex>,
#[clap(long, value_name = "PRODUCT-ID")]
product_id: Option<Hex>,
}
impl Opt {
fn mods(&self) -> Vec<Mod> {
let mut mods = Vec::new();
for m in self.swap.iter().copied() {
mods.push(m);
mods.push(m.swapped());
}
mods.extend(&self.map);
mods
}
}
fn main() -> Result<()> {
let opt = Opt::parse();
if opt.list {
list()
} else {
apply(&opt)
}
}
fn list() -> Result<()> {
print!("{}", tabulate(hid::list()?));
Ok(())
}
fn apply(opt: &Opt) -> Result<()> {
let mut devices = hid::list()?;
let total = devices.len();
let mods = opt.mods();
if let Some(name) = &opt.name {
devices.retain(|d| d.name == *name);
if devices.len() == 0 {
bail!("failed to find device matching name `{}`", name)
}
}
if let Some(Hex(vendor_id)) = opt.vendor_id {
devices.retain(|d| d.vendor_id == vendor_id);
if devices.len() == 0 {
bail!("failed to find device matching vendor id `{}`", vendor_id)
}
}
if let Some(Hex(product_id)) = opt.product_id {
devices.retain(|d| d.product_id == product_id);
if devices.len() == 0 {
bail!("failed to find device matching product id `{}`", product_id)
}
}
let d = if devices.len() == 1 {
Some(devices.remove(0))
} else if devices.len() != total {
bail!("multiple devices matching filter:\n{}", tabulate(devices))
} else {
None
};
if opt.dump {
if opt.reset {
println!("{}", hid::dump(&d, &[])?);
} else if mods.len() > 0 {
println!("{}", hid::dump(&d, &mods)?);
}
} else {
if let Some(d) = &d {
println!(
"Selected:\n Vendor ID: 0x{:x}\n Product ID: 0x{:x}\n Name: {}\n",
d.vendor_id, d.product_id, d.name
);
}
if opt.reset {
hid::apply(&d, &[])?;
println!("Reset all modifications");
} else if mods.len() > 0 {
hid::apply(&d, &mods)?;
println!("Applied the following modifications:");
for m in mods {
println!(" {:?} -> {:?}", m.src(), m.dst());
}
} else {
println!("No modifications to apply");
}
}
Ok(())
}
fn tabulate(devices: Vec<Device>) -> String {
let mut s = String::from("Vendor ID Product ID Name\n");
s.push_str("--------- ---------- ----------------------------------\n");
for d in devices {
writeln!(
s,
"{:<9} {:<10} {}",
format!("0x{:x}", d.vendor_id),
format!("0x{:x}", d.product_id),
d.name,
)
.unwrap();
}
s
}