use proc_macro::{TokenStream, TokenTree};
use std::iter::FromIterator;
#[proc_macro]
pub fn trigger_guide(input: TokenStream) -> TokenStream {
let mut output: Vec<String> = vec!["unsafe { &[".to_string()];
for sequence in input {
match sequence {
TokenTree::Group(sequence) => {
for combo in sequence.stream() {
match combo {
TokenTree::Group(combo) => {
let mut combo_output: Vec<String> = Vec::new();
let mut prefix_output: Vec<String> = Vec::new();
let mut elem_count = 0;
let mut byte_count = 0;
let byte_max_count = 6;
for elem in combo.stream() {
match elem.clone() {
TokenTree::Ident(ident) => {
match ident.to_string().as_str() {
"TriggerCondition" => {
elem_count += 1;
prefix_output = Vec::new();
}
"Switch" | "Animation" => {
byte_count = 6;
}
"AnalogDistance" | "AnalogVelocity"
| "AnalogAcceleration" | "AnalogJerk" => {
byte_count = 6;
}
"HidLed" | "Layer" | "Rotation" => {
byte_count = 5;
}
"Sleep" | "Resume" | "Inactive" | "Active" => {
byte_count = 4;
}
_ => {
panic!("Unknown elem ident: {:?}", ident);
}
}
}
TokenTree::Punct(_) => {}
TokenTree::Group(group) => {
for n in 0..byte_count {
combo_output.append(&mut prefix_output.clone());
combo_output.push(group.to_string());
combo_output.push(".bytes()[".to_string());
combo_output.push(n.to_string());
combo_output.push("],".to_string());
}
for _ in byte_count..byte_max_count {
combo_output.push("0,".to_string());
}
}
_ => {}
}
prefix_output.push(elem.to_string());
}
output.push(elem_count.to_string());
output.push(",".to_string());
output.append(&mut combo_output);
}
TokenTree::Punct(_) => {}
_ => {
panic!("Invalid combo element: {:?}", combo);
}
}
}
}
TokenTree::Punct(_) => {}
_ => {
panic!("Invalid sequence element: {:?}", sequence);
}
}
}
output.push("0 ] }".to_string());
String::from_iter(output).parse().unwrap()
}
#[proc_macro]
pub fn result_guide(input: TokenStream) -> TokenStream {
let mut output: Vec<String> = vec!["unsafe { &[".to_string()];
for sequence in input {
match sequence {
TokenTree::Group(sequence) => {
for combo in sequence.stream() {
match combo {
TokenTree::Group(combo) => {
let mut combo_output: Vec<String> = Vec::new();
let mut prefix_output: Vec<String> = Vec::new();
let mut elem_count = 0;
let mut byte_count = 0;
let byte_max_count = 8;
for elem in combo.stream() {
match elem.clone() {
TokenTree::Ident(ident) => {
match ident.to_string().as_str() {
"Capability" => {
elem_count += 1;
prefix_output = Vec::new();
}
"LayerClear" | "McuFlashMode" | "NoOp" => {
byte_count = 4;
}
"HidKeyboard"
| "HidProtocol"
| "HidLed"
| "HidSystemControl"
| "LayerRotate"
| "PixelAnimationControl"
| "PixelFadeLayer"
| "PixelGammaControl" => {
byte_count = 5;
}
"HidioOpenUrl"
| "HidioUnicodeString"
| "HidConsumerControl"
| "HidKeyboardState"
| "LayerState"
| "PixelAnimationIndex"
| "PixelLedControl"
| "Rotate" => {
byte_count = 6;
}
"PixelFadeIndex" | "PixelFadeSet" | "PixelTest" => {
byte_count = 7;
}
"HidioUnicodeState" => {
byte_count = 8;
}
_ => {
panic!("Unknown elem ident: {:?}", ident);
}
}
}
TokenTree::Punct(_) => {}
TokenTree::Group(group) => {
for n in 0..byte_count {
combo_output.append(&mut prefix_output.clone());
combo_output.push(group.to_string());
combo_output.push(".bytes()[".to_string());
combo_output.push(n.to_string());
combo_output.push("],".to_string());
}
for _ in byte_count..byte_max_count {
combo_output.push("0,".to_string());
}
}
_ => {}
}
prefix_output.push(elem.to_string());
}
output.push(elem_count.to_string());
output.push(",".to_string());
output.append(&mut combo_output);
}
TokenTree::Punct(_) => {}
_ => {
panic!("Invalid combo element: {:?}", combo);
}
}
}
}
TokenTree::Punct(_) => {}
_ => {
panic!("Invalid sequence element: {:?}", sequence);
}
}
}
output.push("0 ] }".to_string());
String::from_iter(output).parse().unwrap()
}
enum LayerLookupState {
Layer,
LayerComma,
Type,
TypeComma,
Index,
IndexComma,
Triggers,
TriggersComma,
}
#[proc_macro]
pub fn layer_lookup(input: TokenStream) -> TokenStream {
let mut state = LayerLookupState::Layer;
let mut triggers: Vec<u16> = Vec::new();
let mut output: Vec<String> = vec!["&".to_string(), "[".to_string()];
for token in input {
match state {
LayerLookupState::Layer => {
output.push(token.to_string());
state = LayerLookupState::LayerComma;
}
LayerLookupState::LayerComma => {
output.push(token.to_string());
state = LayerLookupState::Type;
}
LayerLookupState::Type => {
output.push(token.to_string());
state = LayerLookupState::TypeComma;
}
LayerLookupState::TypeComma => {
output.push(token.to_string());
state = LayerLookupState::Index;
}
LayerLookupState::Index => {
match token {
TokenTree::Literal(literal) => {
let val = if literal.to_string().contains("0x") {
u16::from_str_radix(literal.to_string().trim_start_matches("0x"), 16)
.unwrap()
} else {
literal.to_string().parse::<u16>().unwrap()
};
for num in val.to_le_bytes() {
output.push(format!("{}, ", num));
}
}
_ => {
panic!("Invalid token, expected index token: {:?}", token);
}
}
state = LayerLookupState::IndexComma;
}
LayerLookupState::IndexComma => {
state = LayerLookupState::Triggers;
}
LayerLookupState::Triggers => {
match token {
TokenTree::Group(group) => {
for subtoken in group.stream() {
match subtoken.clone() {
TokenTree::Punct(_) => {}
TokenTree::Literal(literal) => {
if literal.to_string().contains("0x") {
triggers.push(
u16::from_str_radix(
literal.to_string().trim_start_matches("0x"),
16,
)
.unwrap(),
);
} else {
triggers.push(literal.to_string().parse::<u16>().unwrap());
}
}
_ => {
panic!("Invalid trigger list token: {:?}", subtoken);
}
}
}
output.push(format!("{},", triggers.len()));
if !triggers.is_empty() {
for trigger in triggers {
for num in trigger.to_le_bytes() {
output.push(format!("{},", num));
}
}
triggers = Vec::new();
}
state = LayerLookupState::TriggersComma;
}
_ => {
panic!("Invalid trigger token group: {:?}", token);
}
}
}
LayerLookupState::TriggersComma => {
state = LayerLookupState::Layer;
}
}
}
output.push("]".to_string());
String::from_iter(output).parse().unwrap()
}