vitaly 0.1.32

VIA/Vial API client and cli tool for guiless keyboard configuration.
Documentation
use crate::common;
use crate::keymap;
use crate::protocol;
use anyhow::{Result, anyhow};
use hidapi::{DeviceInfo, HidApi};
use serde_json::Value;
use std::fs;

pub fn run(
    api: &HidApi,
    device: &DeviceInfo,
    meta_file: &Option<String>,
    file: &String,
    preview: bool,
) -> Result<()> {
    let device_path = device.path();
    let dev = api.open_path(device_path)?;
    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 layout_str = fs::read_to_string(file)?;
    let root_json: Value = serde_json::from_str(&layout_str)?;
    let root = root_json
        .as_object()
        .ok_or(anyhow!("config file root is not an object"))?;

    let layout_options = &meta["layouts"]["labels"];
    let layout_state = match &root_json["layout_options"] {
        Value::Null => 0,
        Value::Number(num) => {
            let n = num
                .as_i64()
                .ok_or(anyhow!("layout_options should be a number"))?;
            if n == -1 { 0 } else { n as u32 }
        }
        _ => {
            return Err(
                common::CommandError("layout_option should be a number".to_string()).into(),
            );
        }
    };
    let options = protocol::LayoutOptions::from_json(layout_state, layout_options)?;

    let buttons = keymap::keymap_to_buttons(&meta["layouts"]["keymap"], &options)?;

    let layers = root
        .get("layout")
        .ok_or(anyhow!("config file has no layout defined"))?
        .as_array()
        .ok_or(anyhow!("layout should be an array"))?;

    let keys = protocol::Keymap::from_json(
        rows,
        cols,
        capabilities.layer_count,
        layers,
        capabilities.vial_version,
    )?;
    let encoder_layout = match &root.get("encoder_layout") {
        Some(value) => protocol::load_encoders_from_json(value, capabilities.vial_version)?,
        None => Vec::new(),
    };
    let combos = match capabilities.combo_count {
        0 => Vec::new(),
        _ => protocol::load_combos_from_json(
            root.get("combo").ok_or(anyhow!("combo is not defined"))?,
            capabilities.vial_version,
        )?,
    };
    let tap_dances = match capabilities.tap_dance_count {
        0 => Vec::new(),
        _ => protocol::load_tap_dances_from_json(
            root.get("tap_dance")
                .ok_or(anyhow!("tad_dance is not defined"))?,
            capabilities.vial_version,
        )?,
    };
    let macros = protocol::load_macros_from_json(
        root.get("macro").ok_or(anyhow!("macro is not defined"))?,
        capabilities.vial_version,
    )?;
    let key_overrides = match capabilities.key_override_count {
        0 => Vec::new(),
        _ => protocol::load_key_overrides_from_json(
            root.get("key_override")
                .ok_or(anyhow!("key_override are not defined"))?,
            capabilities.vial_version,
        )?,
    };

    let alt_repeats = match capabilities.alt_repeat_key_count {
        0 => Vec::new(),
        _ => protocol::load_alt_repeats_from_json(
            root.get("alt_repeat_key")
                .ok_or(anyhow!("alt_repeat_key are not defined"))?,
            capabilities.vial_version,
        )?,
    };

    if !preview {
        println!();
        if !macros.is_empty() && capabilities.vial_version > 0 {
            let status = protocol::get_locked_status(&dev)?;
            if status.locked {
                return Err(common::CommandError("Keyboard is locked, macroses can't be updated, keyboard might be unlocked with subcommand 'lock -u'".to_string()).into());
            }
        }

        protocol::set_layout_options(&dev, layout_state)?;

        if capabilities.vial_version >= protocol::VIAL_PROTOCOL_QMK_SETTINGS {
            let qmk_settings = protocol::load_qmk_settings_from_json(
                root.get("settings")
                    .ok_or(anyhow!("settings are not defined"))?,
            )?;
            for (key, value) in qmk_settings.iter() {
                protocol::set_qmk_value(&dev, *key, value.get())?;
            }
            println!("Settings restored");
        }

        protocol::set_macros(&dev, &capabilities, &macros)?;
        println!("Macros restored");

        for ko in key_overrides {
            protocol::set_key_override(&dev, &ko)?;
        }
        println!("Key overrides restored");

        for ar in alt_repeats {
            protocol::set_alt_repeat(&dev, &ar)?;
        }
        println!("Alt repeat keys restored");

        for combo in combos {
            protocol::set_combo(&dev, &combo)?;
        }
        println!("Combos restored");

        for td in tap_dances {
            protocol::set_tap_dance(&dev, &td)?;
        }
        println!("TapDances restored");

        for layer_number in 0..capabilities.layer_count {
            let layer_encoders = if encoder_layout.len() > layer_number.into() {
                &encoder_layout[layer_number as usize]
            } else {
                &Vec::new()
            };
            for encoder in layer_encoders {
                protocol::set_encoder(&dev, layer_number, encoder.index, 0, encoder.ccw)?;
                protocol::set_encoder(&dev, layer_number, encoder.index, 1, encoder.cw)?;
            }
        }
        println!("Encoders restored");

        protocol::set_keymap(&dev, &keys)?;
        println!("Keys restored. All done!!!");

        //
    } else {
        if !options.is_empty() {
            println!("Layout options:\n{}", options);
        }
        for layer_number in 0..capabilities.layer_count {
            let encoders = if encoder_layout.len() > layer_number.into() {
                &encoder_layout[layer_number as usize]
            } else {
                &Vec::new()
            };
            common::render_layer(
                &keys,
                encoders,
                &buttons,
                layer_number,
                capabilities.vial_version,
                &meta.get("customKeycodes"),
            )?
        }

        if !combos.is_empty() {
            println!("Combos:");
            for combo in &combos {
                if !combo.is_empty() {
                    combo.dump(capabilities.vial_version)?;
                    println!();
                }
            }
            println!();
        }

        if !macros.is_empty() {
            println!("Macros:");
            for m in &macros {
                if !m.is_empty() {
                    m.dump(capabilities.vial_version)?;
                    println!();
                }
            }
            println!();
        }

        if !tap_dances.is_empty() {
            println!("TapDances:");
            for tap_dance in &tap_dances {
                if !tap_dance.is_empty() {
                    tap_dance.dump(capabilities.vial_version)?;
                    println!();
                }
            }
            println!();
        }

        if !key_overrides.is_empty() {
            println!("KeyOverrides:");
            for key_override in &key_overrides {
                if !key_override.is_empty() {
                    key_override.dump(capabilities.vial_version)?;
                    println!();
                }
            }
            println!();
        }

        if !alt_repeats.is_empty() {
            println!("AltRepeatKeys:");
            for alt_repeat in &alt_repeats {
                if !alt_repeat.is_empty() {
                    alt_repeat.dump(capabilities.vial_version)?;
                    println!();
                }
            }
            println!();
        }

        if capabilities.vial_version >= protocol::VIAL_PROTOCOL_QMK_SETTINGS {
            let qmk_settings = protocol::load_qmk_settings_from_json(
                root.get("settings")
                    .ok_or(anyhow!("settings are not defined"))?,
            )?;
            let settings_defs = protocol::load_qmk_definitions()?;
            println!("Settings:");
            for group in settings_defs["tabs"]
                .as_array()
                .ok_or(anyhow!("tabs should be an array"))?
            {
                let group_name = group["name"]
                    .as_str()
                    .ok_or(anyhow!("name shoule be a string"))?;
                let mut header_shown = false;
                for field in group["fields"]
                    .as_array()
                    .ok_or(anyhow!("fields should be a an array"))?
                {
                    let title = field["title"]
                        .as_str()
                        .ok_or(anyhow!("title should be a string"))?;
                    let qsid = field["qsid"]
                        .as_u64()
                        .ok_or(anyhow!("qsid should be a number"))?
                        as u16;
                    if let Some(value) = qmk_settings.get(&qsid) {
                        if !header_shown {
                            println!("{}:", group_name);
                            header_shown = true;
                        }
                        match field["type"]
                            .as_str()
                            .ok_or(anyhow!("type should be a string"))?
                        {
                            "integer" => {
                                println!("\t{}) {} = {}", qsid, title, value.get());
                            }
                            "boolean" => match field["bit"].as_u64() {
                                None => {
                                    println!("\t{}) {} = {}", qsid, title, value.get() != 0);
                                }
                                Some(bit) => {
                                    println!(
                                        "\t{}) {} = {}",
                                        qsid,
                                        title,
                                        value.get_bool(bit as u8)
                                    );
                                }
                            },
                            t => {
                                return Err(common::CommandError(format!(
                                    "Unknown setting type {:?}",
                                    t
                                ))
                                .into());
                            }
                        }
                    }
                }
                if header_shown {
                    println!();
                }
            }
        }
    }
    Ok(())
}