use crate::common;
use crate::keymap;
use crate::protocol;
use anyhow::{Result, anyhow};
use hidapi::{DeviceInfo, HidApi};
use serde_json::{Value, json};
use std::collections::HashMap;
use std::fs;
pub fn run(
api: &HidApi,
device: &DeviceInfo,
meta_file: &Option<String>,
file: &String,
) -> Result<()> {
let device_path = device.path();
let dev = api.open_path(device_path)?;
let uid: u64 = protocol::load_uid(&dev)?;
let capabilities = protocol::scan_capabilities(&dev)?;
let meta = common::load_meta(&dev, &capabilities, meta_file)?;
let cols = meta["matrix"]["cols"]
.as_u64()
.ok_or(anyhow!("matrix/cols not found in meta"))? as u8;
let rows = meta["matrix"]["rows"]
.as_u64()
.ok_or(anyhow!("matrix/rows not found in meta"))? as u8;
let encoders_count = keymap::get_encoders_count(&meta["layouts"]["keymap"])?;
let mut encoders = Vec::new();
for layer_number in 0..capabilities.layer_count {
let mut layer_encoders = Vec::new();
for encoder_index in 0..encoders_count {
layer_encoders.push(protocol::load_encoder(&dev, layer_number, encoder_index)?);
}
encoders.push(layer_encoders);
}
let keys = protocol::load_layers_keys(&dev, capabilities.layer_count, rows, cols)?;
let combos = match capabilities.combo_count {
0 => Vec::new(),
_ => protocol::load_combos(&dev, capabilities.combo_count)?,
};
let tap_dances = match capabilities.tap_dance_count {
0 => Vec::new(),
_ => protocol::load_tap_dances(&dev, capabilities.tap_dance_count)?,
};
let macros = protocol::load_macros(
&dev,
capabilities.macro_count,
capabilities.macro_buffer_size,
)?;
let key_overrides = match capabilities.key_override_count {
0 => Vec::new(),
_ => protocol::load_key_overrides(&dev, capabilities.key_override_count)?,
};
let alt_repeats = match capabilities.alt_repeat_key_count {
0 => Vec::new(),
_ => protocol::load_alt_repeats(&dev, capabilities.alt_repeat_key_count)?,
};
let qmk_settings = if capabilities.vial_version >= protocol::VIAL_PROTOCOL_QMK_SETTINGS {
protocol::load_qmk_settings(&dev)?
} else {
HashMap::new()
};
let layout_options = match &meta["layouts"]["labels"] {
Value::Null => -1,
_ => protocol::load_layout_options(&dev)? as i64,
};
let mut result = json!({
"version": 1,
"via_protocol": capabilities.via_version,
"uid": uid,
"layout": keys.to_json(capabilities.vial_version)?,
"layout_options": layout_options,
});
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"encoder_layout".to_string(),
Value::Array(protocol::encoders_to_json(
&encoders,
capabilities.vial_version,
)?),
);
if capabilities.vial_version > 0 {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"vial_protocol".to_string(),
capabilities.vial_version.into(),
);
}
if !alt_repeats.is_empty() {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"alt_repeat_key".to_string(),
Value::Array(protocol::alt_repeats_to_json(
&alt_repeats,
capabilities.vial_version,
)?),
);
}
if !key_overrides.is_empty() {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"key_override".to_string(),
Value::Array(protocol::key_overrides_to_json(
&key_overrides,
capabilities.vial_version,
)?),
);
}
if !combos.is_empty() {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"combo".to_string(),
Value::Array(protocol::combos_to_json(
&combos,
capabilities.vial_version,
)?),
);
}
if !tap_dances.is_empty() {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"tap_dance".to_string(),
Value::Array(protocol::tap_dances_to_json(
&tap_dances,
capabilities.vial_version,
)?),
);
}
if !macros.is_empty() {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"macro".to_string(),
Value::Array(protocol::macros_to_json(
¯os,
capabilities.vial_version,
)?),
);
} else {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert("macro".to_string(), Value::Array([].to_vec()));
}
if !qmk_settings.is_empty() {
result
.as_object_mut()
.ok_or(anyhow!("broken root"))?
.insert(
"settings".to_string(),
protocol::qmk_settings_to_json(&qmk_settings)?,
);
}
fs::write(file, result.to_string())?;
println!("\nConfiguration saved to file {}", file);
Ok(())
}