1use std::collections::HashMap;
2
3use pest::Parser;
4use pest_derive::Parser;
5
6use crate::{KeyInfo, KeyboardTomlConfig, LayoutConfig};
7
8#[derive(Parser)]
10#[grammar = "keymap.pest"]
11struct ConfigParser;
12
13const MAX_ALIAS_RESOLUTION_DEPTH: usize = 10;
15
16impl KeyboardTomlConfig {
17 pub fn get_layout_config(&self) -> Result<(LayoutConfig, Vec<Vec<KeyInfo>>), String> {
19 let aliases = self.aliases.clone().unwrap_or_default();
20 let layers = self.layer.clone().unwrap_or_default();
21 let mut layout = self.layout.clone().expect("layout config is required");
22
23 for key in aliases.keys() {
28 if key.chars().any(char::is_whitespace) {
29 return Err(format!(
30 "keyboard.toml: Alias key '{}' must not contain whitespace characters",
31 key
32 ));
33 }
34 }
35
36 let mut final_layers = Vec::<Vec<Vec<String>>>::new();
37 let mut key_info: Vec<Vec<KeyInfo>> =
38 vec![vec![KeyInfo::default(); layout.cols as usize]; layout.rows as usize];
39 let mut sequence_to_grid: Option<Vec<(u8, u8)>> = None;
40 if let Some(matrix_map) = &layout.matrix_map {
41 let mut sequence_number = 0u32;
43 let mut grid_to_sequence: Vec<Vec<Option<u32>>> =
44 vec![vec![None; layout.cols as usize]; layout.rows as usize];
45 match Self::parse_matrix_map(matrix_map) {
46 Ok(info) => {
47 let mut coords = Vec::<(u8, u8)>::new();
48 for (row, col, hand) in &info {
49 if *row >= layout.rows || *col >= layout.cols {
50 return Err(format!(
51 "keyboard.toml: Coordinate ({},{}) in `layout.matrix_map` is out of bounds: ([0..{}], [0..{}]) is the expected range",
52 row,
53 col,
54 layout.rows - 1,
55 layout.cols - 1
56 ));
57 }
58 if grid_to_sequence[*row as usize][*col as usize].is_some() {
59 return Err(format!(
60 "keyboard.toml: Duplicate coordinate ({},{}) found in `layout.matrix_map`",
61 row, col
62 ));
63 } else {
64 coords.push((*row, *col));
66 grid_to_sequence[*row as usize][*col as usize] = Some(sequence_number);
67 key_info[*row as usize][*col as usize] = KeyInfo { hand: *hand };
68 }
69 sequence_number += 1;
70 }
71 sequence_to_grid = Some(coords);
72 }
73 Err(parse_err) => {
74 return Err(format!("keyboard.toml: Error in `layout.matrix_map`: {}", parse_err));
76 }
77 }
78 } else if !layers.is_empty() {
79 return Err("layout.matrix_map is need to be defined to process [[layer]] based key maps".to_string());
80 }
81 if let Some(sequence_to_grid) = &sequence_to_grid {
82 let mut layer_names = HashMap::<String, u32>::new();
84 for (layer_number, layer) in layers.iter().enumerate() {
85 if let Some(name) = &layer.name {
86 if layer_names.contains_key(name) {
87 return Err(format!(
88 "keyboard.toml: Duplicate layer name '{}' found in `layout.keymap`",
89 name
90 ));
91 }
92 layer_names.insert(name.clone(), layer_number as u32);
93 }
94 }
95 if layers.len() > layout.layers as usize {
96 return Err("keyboard.toml: Number of [[layer]] entries is larger than layout.layers".to_string());
97 }
98 let layer_names = layer_names;
102 for (layer_number, layer) in layers.iter().enumerate() {
103 match Self::keymap_parser(&layer.keys, &aliases, &layer_names) {
106 Ok(key_action_sequence) => {
107 let mut legacy_keymap =
108 vec![vec!["No".to_string(); layout.cols as usize]; layout.rows as usize];
109 for (sequence_number, key_action) in key_action_sequence.into_iter().enumerate() {
110 if sequence_number >= sequence_to_grid.len() {
111 return Err(format!(
112 "keyboard.toml: {} layer #{} contains too many entries (must match layout.matrix_map)",
113 &layer.name.clone().unwrap_or_default(),
114 layer_number
115 ));
116 }
117 let (row, col) = sequence_to_grid[sequence_number];
118 legacy_keymap[row as usize][col as usize] = key_action.clone();
119 }
120 final_layers.push(legacy_keymap);
121 }
122 Err(parse_err) => {
123 return Err(format!("keyboard.toml: Error in `layout.keymap`: {}", parse_err));
124 }
125 }
126 }
127 }
128 if let Some(keymap) = &mut layout.keymap {
130 final_layers.append(keymap);
131 }
132 if final_layers.len() <= layout.layers as usize {
135 for _ in final_layers.len()..layout.layers as usize {
136 final_layers.push(vec![vec!["_".to_string(); layout.cols as usize]; layout.rows as usize]);
138 }
139 } else {
140 return Err(format!(
141 "keyboard.toml: The actual number of layers is larger than {} [layout.layers]: {} [[Layer]] entries + {} layers in layout.keymap",
142 layout.layers,
143 layers.len(),
144 layout.keymap.as_ref().map(|keymap| keymap.len()).unwrap_or_default()
145 ));
146 }
147 if final_layers.iter().any(|r| r.len() as u8 != layout.rows) {
149 return Err("keyboard.toml: Row number in keymap doesn't match with [layout.row]".to_string());
150 }
151 if final_layers
153 .iter()
154 .any(|r| r.iter().any(|c| c.len() as u8 != layout.cols))
155 {
156 return Err("keyboard.toml: Col number in keymap doesn't match with [layout.col]".to_string());
157 }
158
159 let mut encoder_map: Vec<Vec<[String; 2]>> = vec![];
161 for layer in &layers {
162 let mut encoders = layer.encoders.clone().unwrap_or_default();
163 for [cw, ccw] in &mut encoders {
164 *cw = Self::alias_resolver(cw, &aliases)?;
165 *ccw = Self::alias_resolver(ccw, &aliases)?;
166 }
167 encoder_map.push(encoders);
168 }
169 if let Some(deprecated_encoder_map) = &mut layout.encoder_map {
170 encoder_map.append(deprecated_encoder_map);
171 }
172
173 Ok((
174 LayoutConfig {
175 rows: layout.rows,
176 cols: layout.cols,
177 layers: layout.layers,
178 keymap: final_layers,
179 encoder_map,
180 },
181 key_info,
182 ))
183 }
184
185 fn parse_matrix_map(matrix_map: &str) -> Result<Vec<(u8, u8, char)>, String> {
188 match ConfigParser::parse(Rule::matrix_map, matrix_map) {
189 Ok(pairs) => {
190 let mut key_info = Vec::new();
191 for pair in pairs {
193 if pair.as_rule() == Rule::matrix_map {
195 for inner_pair in pair.into_inner() {
196 match inner_pair.as_rule() {
197 Rule::keypos_info => {
198 let mut items = inner_pair.into_inner(); let row_str = items.next().ok_or("Missing row coordinate")?.as_str();
201 let col_str = items.next().ok_or("Missing col coordinate")?.as_str();
202
203 let row = row_str
204 .parse::<u8>()
205 .map_err(|e| format!("Failed to parse row '{}': {}", row_str, e))?;
206 let col = col_str
207 .parse::<u8>()
208 .map_err(|e| format!("Failed to parse col '{}': {}", col_str, e))?;
209
210 let mut hand = 'C'; for part in items {
213 match part.as_rule() {
214 Rule::left_hand => hand = 'L',
215 Rule::right_hand => hand = 'R',
216 _ => {}
217 }
218 }
219
220 key_info.push((row, col, hand));
221 }
222 Rule::EOI | Rule::WHITESPACE => {
223 }
225 _ => {
226 return Err(format!(
228 "Unexpected rule encountered during layout.matrix_map processing: {:?}",
229 inner_pair.as_rule()
230 ));
231 }
232 }
233 }
234 }
235 }
236 Ok(key_info)
237 }
238 Err(e) => Err(format!("Invalid layout.matrix_map format: {}", e)),
239 }
240 }
241
242 fn alias_resolver(keys: &str, aliases: &HashMap<String, String>) -> Result<String, String> {
243 let mut current_keys = keys.to_string();
244
245 let mut iterations = 0;
246
247 loop {
248 let mut next_keys = String::with_capacity(current_keys.capacity());
249 let mut made_replacement = false;
250 let mut last_index = 0; while let Some(at_index) = current_keys[last_index..].find('@') {
253 let start_index = last_index + at_index;
254
255 next_keys.push_str(¤t_keys[last_index..start_index]);
257
258 if let Some(first_char) = current_keys.as_bytes().get(start_index + 1) {
260 if !first_char.is_ascii_whitespace() {
261 let mut end_index = start_index + 2;
263 while let Some(c) = current_keys.as_bytes().get(end_index) {
264 if c.is_ascii_whitespace() {
265 break;
266 } else {
267 end_index += 1;
268 }
269 }
270
271 let alias_key = ¤t_keys[start_index + 1..end_index];
273
274 match aliases.get(alias_key) {
276 Some(value) => {
277 next_keys.push_str(value);
278 made_replacement = true;
279 }
280 None => return Err(format!("Undefined alias: {}", alias_key)),
281 }
282 last_index = end_index; } else {
284 next_keys.push('@');
286 last_index = start_index + 1;
287 }
288 } else {
289 next_keys.push('@');
291 last_index = start_index + 1;
292 break; }
294 }
295
296 next_keys.push_str(¤t_keys[last_index..]);
298
299 iterations += 1;
301 if iterations >= MAX_ALIAS_RESOLUTION_DEPTH {
302 return Err(format!(
303 "Alias resolution exceeded maximum depth ({}), potential infinite loop detected in '{}'",
304 MAX_ALIAS_RESOLUTION_DEPTH, keys
305 )); }
307
308 if !made_replacement {
309 break; }
311
312 current_keys = next_keys;
314 }
315
316 Ok(current_keys)
317 }
318
319 fn layer_name_resolver(
320 prefix: &str,
321 pair: pest::iterators::Pair<Rule>,
322 layer_names: &HashMap<String, u32>,
323 ) -> Result<String, String> {
324 let mut action = prefix.to_string() + "(";
325
326 for inner_pair in pair.into_inner() {
327 match inner_pair.as_rule() {
328 Rule::layer_name => {
330 let layer_name = inner_pair.as_str().to_string();
332 if let Some(layer_number) = layer_names.get(&layer_name) {
333 action += layer_number.to_string().as_str();
334 } else {
335 return Err(format!("Invalid layer name: {}", layer_name));
336 }
337 }
338 Rule::layer_number => {
339 action += inner_pair.as_str();
340 }
341 _ => {
342 action += ", ";
344 action += inner_pair.as_str();
345 }
346 }
347 }
348 action += ")";
349
350 Ok(action)
351 }
352
353 fn keymap_parser(
354 layer_keys: &str,
355 aliases: &HashMap<String, String>,
356 layer_names: &HashMap<String, u32>,
357 ) -> Result<Vec<String>, String> {
358 let layer_keys = Self::alias_resolver(layer_keys, aliases)?;
360
361 let mut key_action_sequence = Vec::new();
362
363 match ConfigParser::parse(Rule::key_map, &layer_keys) {
365 Ok(pairs) => {
366 for pair in pairs {
368 if pair.as_rule() == Rule::key_map {
370 for inner_pair in pair.into_inner() {
371 match inner_pair.as_rule() {
372 Rule::no_action => {
373 let action = inner_pair.as_str().to_string();
374 key_action_sequence.push(action);
375 }
376
377 Rule::transparent_action => {
378 let action = inner_pair.as_str().to_string();
379 key_action_sequence.push(action);
380 }
381
382 Rule::simple_keycode => {
383 let action = inner_pair.as_str().to_string();
384 key_action_sequence.push(action);
385 }
386
387 Rule::shifted_action => {
388 let action = inner_pair.as_str().to_string();
389 key_action_sequence.push(action);
390 }
391
392 Rule::osm_action => {
393 let action = inner_pair.as_str().to_string();
394 key_action_sequence.push(action);
395 }
396
397 Rule::wm_action => {
398 let action = inner_pair.as_str().to_string();
399 key_action_sequence.push(action);
400 }
401
402 Rule::df_action => {
404 key_action_sequence.push(Self::layer_name_resolver("DF", inner_pair, layer_names)?);
405 }
406 Rule::mo_action => {
407 key_action_sequence.push(Self::layer_name_resolver("MO", inner_pair, layer_names)?);
408 }
409 Rule::lm_action => {
410 key_action_sequence.push(Self::layer_name_resolver("LM", inner_pair, layer_names)?);
411 }
412 Rule::lt_action => {
413 key_action_sequence.push(Self::layer_name_resolver("LT", inner_pair, layer_names)?);
414 }
416 Rule::osl_action => {
417 key_action_sequence.push(Self::layer_name_resolver(
418 "OSL",
419 inner_pair,
420 layer_names,
421 )?);
422 }
423 Rule::tt_action => {
424 key_action_sequence.push(Self::layer_name_resolver("TT", inner_pair, layer_names)?);
425 }
426 Rule::tg_action => {
427 key_action_sequence.push(Self::layer_name_resolver("TG", inner_pair, layer_names)?);
428 }
429 Rule::to_action => {
430 key_action_sequence.push(Self::layer_name_resolver("TO", inner_pair, layer_names)?);
431 }
432
433 Rule::mt_action => {
435 let action = inner_pair.as_str().to_string();
436 key_action_sequence.push(action);
437 }
438 Rule::th_action => {
439 let action = inner_pair.as_str().to_string();
440 key_action_sequence.push(action);
441 }
442
443 Rule::morse_action => {
444 let action = inner_pair.as_str().to_string();
445 key_action_sequence.push(action);
446 }
447
448 Rule::trigger_macro_action => {
449 let action = inner_pair.as_str().to_string();
450 key_action_sequence.push(action);
451 }
452
453 Rule::EOI | Rule::WHITESPACE => {
454 }
456 _ => {
457 panic!(
459 "Unexpected rule encountered during layer.keys processing:{:?}",
460 inner_pair.as_rule()
461 );
462 }
463 }
464 }
465 }
466 }
467 }
468 Err(e) => {
469 panic!("Invalid keymap format: {}", e);
470 }
471 }
472
473 Ok(key_action_sequence)
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480
481 #[test]
482 fn test_no_action_parsing() {
483 let test_cases = vec![
485 ("No ", vec!["No"]),
486 ("No\n", vec!["No"]),
487 ("No\t", vec!["No"]),
488 ("No A", vec!["No", "A"]),
489 ("A No B", vec!["A", "No", "B"]),
490 ("No No No", vec!["No", "No", "No"]),
491 ];
492
493 for (input, expected) in test_cases {
494 let result = ConfigParser::parse(Rule::key_map, input);
495 assert!(result.is_ok(), "Failed to parse: {}", input);
496
497 let mut actions = Vec::new();
498 for pair in result.unwrap() {
499 if pair.as_rule() == Rule::key_map {
500 for inner_pair in pair.into_inner() {
501 match inner_pair.as_rule() {
502 Rule::no_action | Rule::simple_keycode => {
503 actions.push(inner_pair.as_str().to_string());
504 }
505 Rule::EOI | Rule::WHITESPACE => {}
506 _ => {}
507 }
508 }
509 }
510 }
511
512 assert_eq!(actions, expected, "Input: {}", input);
513 }
514 }
515
516 #[test]
517 fn test_no_vs_no_prefixed_keycodes() {
518 let test_cases = vec![
520 ("No", Rule::no_action),
521 ("NoUsSlash", Rule::simple_keycode),
522 ("NonUsSlash", Rule::simple_keycode),
523 ("NoReturn", Rule::simple_keycode),
524 ("NoBrake", Rule::simple_keycode),
525 ];
526
527 for (input, expected_rule) in test_cases {
528 let result = ConfigParser::parse(Rule::key_map, input);
529 assert!(result.is_ok(), "Failed to parse: {}", input);
530
531 let mut found_rule = None;
532 for pair in result.unwrap() {
533 if pair.as_rule() == Rule::key_map {
534 for inner_pair in pair.into_inner() {
535 match inner_pair.as_rule() {
536 Rule::no_action | Rule::simple_keycode => {
537 found_rule = Some(inner_pair.as_rule());
538 }
539 _ => {}
540 }
541 }
542 }
543 }
544
545 assert_eq!(
546 found_rule,
547 Some(expected_rule),
548 "Input: {} should be parsed as {:?}",
549 input,
550 expected_rule
551 );
552 }
553 }
554
555 #[test]
556 fn test_keymap_parser_with_no_actions() {
557 let aliases = HashMap::new();
558 let layer_names = HashMap::new();
559
560 let keymap = "A B No C No NoUsSlash NonUsSlash D No";
562 let result = KeyboardTomlConfig::keymap_parser(keymap, &aliases, &layer_names);
563
564 assert!(result.is_ok());
565 let actions = result.unwrap();
566 assert_eq!(
567 actions,
568 vec!["A", "B", "No", "C", "No", "NoUsSlash", "NonUsSlash", "D", "No"]
569 );
570 }
571
572 #[test]
573 fn test_morse_action_parsing() {
574 let aliases = HashMap::new();
575 let layer_names = HashMap::new();
576
577 let keymap = "A TD(0) B TD(1) C TD(255)";
579 let result = KeyboardTomlConfig::keymap_parser(keymap, &aliases, &layer_names);
580
581 assert!(result.is_ok());
582 let actions = result.unwrap();
583 assert_eq!(actions, vec!["A", "TD(0)", "B", "TD(1)", "C", "TD(255)"]);
584 }
585
586 #[test]
587 fn test_macro_trigger_action_parsing() {
588 let aliases = std::collections::HashMap::new();
589 let layer_names = std::collections::HashMap::new();
590
591 let keymap = "A Macro(0) B MACRO(1) C macro(255)";
593 let result = KeyboardTomlConfig::keymap_parser(keymap, &aliases, &layer_names);
594
595 assert!(result.is_ok());
596 let actions = result.unwrap();
597 assert_eq!(actions, vec!["A", "Macro(0)", "B", "MACRO(1)", "C", "macro(255)"]);
598 }
599
600 #[test]
601 fn test_morse_action_grammar() {
602 let test_cases = vec![
604 ("TD(0)", Rule::morse_action),
605 ("TD(1)", Rule::morse_action),
606 ("TD(255)", Rule::morse_action),
607 ("td(0)", Rule::morse_action), ("td(1)", Rule::morse_action),
609 ("MORSE(0)", Rule::morse_action),
610 ("MORSE(1)", Rule::morse_action),
611 ("MORSE(255)", Rule::morse_action),
612 ("Morse(0)", Rule::morse_action), ("morse(1)", Rule::morse_action),
614 ];
615
616 for (input, expected_rule) in test_cases {
617 let result = ConfigParser::parse(Rule::key_map, input);
618 assert!(result.is_ok(), "Failed to parse: {}", input);
619
620 let mut found_rule = None;
621 for pair in result.unwrap() {
622 if pair.as_rule() == Rule::key_map {
623 for inner_pair in pair.into_inner() {
624 match inner_pair.as_rule() {
625 Rule::morse_action => {
626 found_rule = Some(inner_pair.as_rule());
627 }
628 _ => {}
629 }
630 }
631 }
632 }
633
634 assert_eq!(
635 found_rule,
636 Some(expected_rule),
637 "Input: {} should be parsed as {:?}",
638 input,
639 expected_rule
640 );
641 }
642 }
643
644 #[test]
645 fn test_macro_grammar() {
646 let test_cases = vec![
648 ("Macro(0)", Rule::trigger_macro_action),
649 ("Macro(1)", Rule::trigger_macro_action),
650 ("Macro(255)", Rule::trigger_macro_action),
651 ("MACRO(0)", Rule::trigger_macro_action), ("MACRO(1)", Rule::trigger_macro_action),
653 ("macro(0)", Rule::trigger_macro_action), ("macro(1)", Rule::trigger_macro_action),
655 ("macro(255)", Rule::trigger_macro_action),
656 ];
657
658 for (input, expected_rule) in test_cases {
659 let result = ConfigParser::parse(Rule::key_map, input);
660 assert!(result.is_ok(), "Failed to parse: {}", input);
661
662 let mut found_rule = None;
663 for pair in result.unwrap() {
664 if pair.as_rule() == Rule::key_map {
665 for inner_pair in pair.into_inner() {
666 match inner_pair.as_rule() {
667 Rule::trigger_macro_action => {
668 found_rule = Some(inner_pair.as_rule());
669 }
670 _ => {}
671 }
672 }
673 }
674 }
675
676 assert_eq!(
677 found_rule,
678 Some(expected_rule),
679 "Input: {} should be parsed as {:?}",
680 input,
681 expected_rule
682 );
683 }
684 }
685}