qsk-macros 0.1.0

Remapping DSL macro to make qsk remapping more concise.
Documentation
use proc_macro2::Span;
use proc_macro_error::abort_call_site;
use syn::{LitBool, LitStr, Ident, Path, PathSegment, PathArguments};
use syn::punctuated::Punctuated;

use qsk_types;

pub struct LayerRefByName {
    pub(crate) path: Path,
    pub(crate) name: LitStr,
}

pub struct TapToggle {
    pub(crate) name: Path,
    pub(crate) layer_ref: LayerRefByName,
    pub(crate) tap_key: Path,
}

pub enum ControlCode {
    Key(Path),
    TapToggle(TapToggle),
    Exit(Path),
}

impl From<&qsk_types::ControlCode> for ControlCode {
    fn from(cc: &qsk_types::ControlCode) -> ControlCode {
        match cc {
            qsk_types::ControlCode::KeyMap(kc) => {
                ControlCode::Key(
                    keycode_path(&kc.to_string())
                )
            },
            qsk_types::ControlCode::TapToggle(layer_ref, kc) => {
                ControlCode::TapToggle(TapToggle{
                    name: control_code_path("TapToggle"),
                    layer_ref: match layer_ref {
                        qsk_types::LayerRef::ByName(name) => {
                            LayerRefByName{
                                path: path_from_vec_str(vec!["qsk_types", "LayerRef", "ByName"]),
                                name: LitStr::new(name, Span::call_site()),
                            }
                        },
                        qsk_types::LayerRef::ByIndex(_) => {
                            abort_call_site!("referencing layers by index is unsupported")
                        },
                    },
                    tap_key: keycode_path(&kc.to_string()),
                    })
            },
            qsk_types::ControlCode::Exit => {
                ControlCode::Exit(
                    control_code_path("Exit"),
                )
            },
            _ => {
                abort_call_site!("unsupported control code")
            },
        }
    }
}

pub struct KeyMap {
    pub(crate) key: Path,
    pub(crate) control_code: Vec<ControlCode>,
}

pub struct Layer {
    pub(crate) name: LitStr,
    pub(crate) active: LitBool,
    pub(crate) maps: Vec<KeyMap>,
}

impl From<&qsk_types::Layer> for Layer {
    fn from(layer: &qsk_types::Layer) -> Layer {
        Layer{
            name: LitStr::new(&layer.name, Span::call_site()),
            active: LitBool::new(layer.active, Span::call_site()),
            maps: layer
                .iter()
                .map(|(k, v)| KeyMap{
                    key: event_code_to_path(k.into()),
                    control_code: v
                        .iter()
                        .map(|cc| {
                            cc.into()
                        })
                        .collect(),
                })
                .collect(),
        }
    }
}

pub struct Ir {
    pub(crate) layers: Vec<Layer>,
}

impl From<qsk_types::LayerComposer> for Ir {
    fn from(lc: qsk_types::LayerComposer) -> Ir {
        Ir {
            layers: lc
                .iter()
                .map(|layer| layer.into())
                .collect(),
        }
    }
}

pub fn lower(model: qsk_types::LayerComposer) -> Ir {
    model.into()
}

fn event_code_to_path(ec: &qsk_types::EventCode) -> Path {
    match ec {
        qsk_types::EventCode::KeyCode(kc) => {
            keycode_path(&kc.to_string())
        },
        qsk_types::EventCode::SynCode(_) => {
            abort_call_site!("syn code not allowed in proc macro")
        },
    }
}

fn keycode_path(keycode_str: &str) -> Path {
    path_from_vec_str(vec![
                      "qsk_types", "KeyCode", keycode_str,
    ])
}

fn control_code_path(variant_str: &str) -> Path {
    path_from_vec_str(vec!["qsk_types", "ControlCode", variant_str])
}

fn path_from_vec_str(parts: Vec<&str>) -> Path {
    Path{ 
        leading_colon: None,
        segments: Punctuated::from_iter(parts.into_iter()
            .map(|part| PathSegment{
                ident: Ident::new(part, Span::call_site()),
                arguments: PathArguments::None,
            }))
    }
}