1use pest::Parser;
2use pest_derive::Parser;
3
4use crate::{KeyboardTomlConfig, LayoutConfig};
5use std::collections::HashMap;
6
7#[derive(Parser)]
9#[grammar = "keymap.pest"]
10struct ConfigParser;
11
12const MAX_ALIAS_RESOLUTION_DEPTH: usize = 10;
14
15impl KeyboardTomlConfig {
16 pub fn get_layout_config(&self) -> Result<LayoutConfig, String> {
18 let aliases = self.aliases.clone().unwrap_or_default();
19 let layers = self.layer.clone().unwrap_or_default();
20 let mut layout = self.layout.clone().expect("layout config is required");
21 for key in aliases.keys() {
26 if key.chars().any(char::is_whitespace) {
27 return Err(format!(
28 "keyboard.toml: Alias key '{}' must not contain whitespace characters",
29 key
30 ));
31 }
32 }
33 let mut final_layers = Vec::<Vec<Vec<String>>>::new();
34 let mut sequence_to_grid: Option<Vec<(u8, u8)>> = None;
35 if let Some(matrix_map) = &layout.matrix_map {
36 let mut sequence_number = 0u32;
38 let mut grid_to_sequence: Vec<Vec<Option<u32>>> =
39 vec![vec![None; layout.cols as usize]; layout.rows as usize];
40 match Self::parse_matrix_map(matrix_map) {
41 Ok(coords) => {
42 for (row, col) in &coords {
43 if *row >= layout.rows || *col >= layout.cols {
44 return Err(format!(
45 "keyboard.toml: Coordinate ({},{}) in `layout.matrix_map` is out of bounds: ([0..{}], [0..{}]) is the expected range",
46 row, col, layout.rows-1, layout.cols-1
47 ));
48 }
49 if grid_to_sequence[*row as usize][*col as usize].is_some() {
50 return Err(format!(
51 "keyboard.toml: Duplicate coordinate ({},{}) found in `layout.matrix_map`",
52 row, col
53 ));
54 } else {
55 grid_to_sequence[*row as usize][*col as usize] = Some(sequence_number);
56 }
57 sequence_number += 1;
58 }
59 sequence_to_grid = Some(coords);
60 }
61 Err(parse_err) => {
62 return Err(format!("keyboard.toml: Error in `layout.matrix_map`: {}", parse_err));
64 }
65 }
66 } else if !layers.is_empty() {
67 return Err("layout.matrix_map is need to be defined to process [[layer]] based key maps".to_string());
68 }
69 if let Some(sequence_to_grid) = &sequence_to_grid {
70 let mut layer_names = HashMap::<String, u32>::new();
72 for (layer_number, layer) in layers.iter().enumerate() {
73 if let Some(name) = &layer.name {
74 if layer_names.contains_key(name) {
75 return Err(format!(
76 "keyboard.toml: Duplicate layer name '{}' found in `layout.keymap`",
77 name
78 ));
79 }
80 layer_names.insert(name.clone(), layer_number as u32);
81 }
82 }
83 if layers.len() > layout.layers as usize {
84 return Err("keyboard.toml: Number of [[layer]] entries is larger than layout.layers".to_string());
85 }
86 let layer_names = layer_names;
90 for (layer_number, layer) in layers.iter().enumerate() {
91 match Self::keymap_parser(&layer.keys, &aliases, &layer_names) {
94 Ok(key_action_sequence) => {
95 let mut legacy_keymap =
96 vec![vec!["No".to_string(); layout.cols as usize]; layout.rows as usize];
97 for (sequence_number, key_action) in key_action_sequence.into_iter().enumerate() {
98 if sequence_number >= sequence_to_grid.len() {
99 return Err(format!(
100 "keyboard.toml: {} layer #{} contains too many entries (must match layout.matrix_map)", &layer.name.clone().unwrap_or_default(), layer_number));
101 }
102 let (row, col) = sequence_to_grid[sequence_number];
103 legacy_keymap[row as usize][col as usize] = key_action.clone();
104 }
105 final_layers.push(legacy_keymap);
106 }
107 Err(parse_err) => {
108 return Err(format!("keyboard.toml: Error in `layout.keymap`: {}", parse_err));
109 }
110 }
111 }
112 }
113 if let Some(keymap) = &mut layout.keymap {
115 final_layers.append(keymap);
116 }
117 if final_layers.len() <= layout.layers as usize {
120 for _ in final_layers.len()..layout.layers as usize {
121 final_layers.push(vec![vec!["_".to_string(); layout.cols as usize]; layout.rows as usize]);
123 }
124 } else {
125 return Err(format!(
126 "keyboard.toml: The actual number of layers is larger than {} [layout.layers]: {} [[Layer]] entries + {} layers in layout.keymap",
127 layout.layers, layers.len(), layout.keymap.as_ref().map(|keymap| keymap.len()).unwrap_or_default()
128 ));
129 }
130 if final_layers.iter().any(|r| r.len() as u8 != layout.rows) {
132 return Err("keyboard.toml: Row number in keymap doesn't match with [layout.row]".to_string());
133 }
134 if final_layers
136 .iter()
137 .any(|r| r.iter().any(|c| c.len() as u8 != layout.cols))
138 {
139 return Err("keyboard.toml: Col number in keymap doesn't match with [layout.col]".to_string());
140 }
141 Ok(LayoutConfig {
142 rows: layout.rows,
143 cols: layout.cols,
144 layers: layout.layers,
145 keymap: final_layers,
146 })
147 }
148
149 fn parse_matrix_map(matrix_map: &str) -> Result<Vec<(u8, u8)>, String> {
152 match ConfigParser::parse(Rule::matrix_map, matrix_map) {
153 Ok(pairs) => {
154 let mut coordinates = Vec::new();
155 for pair in pairs {
157 if pair.as_rule() == Rule::matrix_map {
159 for inner_pair in pair.into_inner() {
160 match inner_pair.as_rule() {
161 Rule::coordinate => {
162 let mut coord_parts = inner_pair.into_inner(); let row_str = coord_parts.next().ok_or("Missing row coordinate")?.as_str();
165 let col_str = coord_parts.next().ok_or("Missing col coordinate")?.as_str();
166
167 let row = row_str
168 .parse::<u8>()
169 .map_err(|e| format!("Failed to parse row '{}': {}", row_str, e))?;
170 let col = col_str
171 .parse::<u8>()
172 .map_err(|e| format!("Failed to parse col '{}': {}", col_str, e))?;
173
174 coordinates.push((row, col));
175 }
176 Rule::EOI | Rule::WHITESPACE => {
177 }
179 _ => {
180 return Err(format!(
182 "Unexpected rule encountered during layout.matrix_map processing: {:?}",
183 inner_pair.as_rule()
184 ));
185 }
186 }
187 }
188 }
189 }
190 Ok(coordinates)
191 }
192 Err(e) => Err(format!("Invalid layout.matrix_map format: {}", e)),
193 }
194 }
195
196 fn alias_resolver(keys: &str, aliases: &HashMap<String, String>) -> Result<String, String> {
197 let mut current_keys = keys.to_string();
198
199 let mut iterations = 0;
200
201 loop {
202 let mut next_keys = String::with_capacity(current_keys.capacity());
203 let mut made_replacement = false;
204 let mut last_index = 0; while let Some(at_index) = current_keys[last_index..].find('@') {
207 let start_index = last_index + at_index;
208
209 next_keys.push_str(¤t_keys[last_index..start_index]);
211
212 if let Some(first_char) = current_keys.as_bytes().get(start_index + 1) {
214 if !first_char.is_ascii_whitespace() {
215 let mut end_index = start_index + 2;
217 while let Some(c) = current_keys.as_bytes().get(end_index) {
218 if c.is_ascii_whitespace() {
219 break;
220 } else {
221 end_index += 1;
222 }
223 }
224
225 let alias_key = ¤t_keys[start_index + 1..end_index];
227
228 match aliases.get(alias_key) {
230 Some(value) => {
231 next_keys.push_str(value);
232 made_replacement = true;
233 }
234 None => return Err(format!("Undefined alias: {}", alias_key)),
235 }
236 last_index = end_index; } else {
238 next_keys.push('@');
240 last_index = start_index + 1;
241 }
242 } else {
243 next_keys.push('@');
245 last_index = start_index + 1;
246 break; }
248 }
249
250 next_keys.push_str(¤t_keys[last_index..]);
252
253 iterations += 1;
255 if iterations >= MAX_ALIAS_RESOLUTION_DEPTH {
256 return Err(format!(
257 "Alias resolution exceeded maximum depth ({}), potential infinite loop detected in '{}'",
258 MAX_ALIAS_RESOLUTION_DEPTH, keys
259 )); }
261
262 if !made_replacement {
263 break; }
265
266 current_keys = next_keys;
268 }
269
270 Ok(current_keys)
271 }
272
273 fn layer_name_resolver(
274 prefix: &str,
275 pair: pest::iterators::Pair<Rule>,
276 layer_names: &HashMap<String, u32>,
277 ) -> Result<String, String> {
278 let mut action = prefix.to_string() + "(";
279
280 for inner_pair in pair.into_inner() {
281 match inner_pair.as_rule() {
282 Rule::layer_name => {
284 let layer_name = inner_pair.as_str().to_string();
286 if let Some(layer_number) = layer_names.get(&layer_name) {
287 action += layer_number.to_string().as_str();
288 } else {
289 return Err(format!("Invalid layer name: {}", layer_name));
290 }
291 }
292 Rule::layer_number => {
293 action += inner_pair.as_str();
294 }
295 _ => {
296 action += ", ";
298 action += inner_pair.as_str();
299 }
300 }
301 }
302 action += ")";
303
304 Ok(action)
305 }
306
307 fn keymap_parser(
308 layer_keys: &str,
309 aliases: &HashMap<String, String>,
310 layer_names: &HashMap<String, u32>,
311 ) -> Result<Vec<String>, String> {
312 let layer_keys = Self::alias_resolver(layer_keys, aliases)?;
314
315 let mut key_action_sequence = Vec::new();
316
317 match ConfigParser::parse(Rule::key_map, &layer_keys) {
319 Ok(pairs) => {
320 for pair in pairs {
322 if pair.as_rule() == Rule::key_map {
324 for inner_pair in pair.into_inner() {
325 match inner_pair.as_rule() {
326 Rule::no_action => {
327 let action = inner_pair.as_str().to_string();
328 key_action_sequence.push(action);
329 }
330
331 Rule::transparent_action => {
332 let action = inner_pair.as_str().to_string();
333 key_action_sequence.push(action);
334 }
335
336 Rule::simple_keycode => {
337 let action = inner_pair.as_str().to_string();
338 key_action_sequence.push(action);
339 }
340
341 Rule::shifted_action => {
342 let action = inner_pair.as_str().to_string();
343 key_action_sequence.push(action);
344 }
345
346 Rule::osm_action => {
347 let action = inner_pair.as_str().to_string();
348 key_action_sequence.push(action);
349 }
350
351 Rule::wm_action => {
352 let action = inner_pair.as_str().to_string();
353 key_action_sequence.push(action);
354 }
355
356 Rule::df_action => {
358 key_action_sequence.push(Self::layer_name_resolver("DF", inner_pair, layer_names)?);
359 }
360 Rule::mo_action => {
361 key_action_sequence.push(Self::layer_name_resolver("MO", inner_pair, layer_names)?);
362 }
363 Rule::lm_action => {
364 key_action_sequence.push(Self::layer_name_resolver("LM", inner_pair, layer_names)?);
365 }
366 Rule::lt_action => {
367 key_action_sequence.push(Self::layer_name_resolver("LT", inner_pair, layer_names)?);
368 }
370 Rule::osl_action => {
371 key_action_sequence.push(Self::layer_name_resolver(
372 "OSL",
373 inner_pair,
374 layer_names,
375 )?);
376 }
377 Rule::tt_action => {
378 key_action_sequence.push(Self::layer_name_resolver("TT", inner_pair, layer_names)?);
379 }
380 Rule::tg_action => {
381 key_action_sequence.push(Self::layer_name_resolver("TG", inner_pair, layer_names)?);
382 }
383 Rule::to_action => {
384 key_action_sequence.push(Self::layer_name_resolver("TO", inner_pair, layer_names)?);
385 }
386
387 Rule::mt_action => {
389 let action = inner_pair.as_str().to_string();
390 key_action_sequence.push(action);
391 }
392 Rule::th_action => {
393 let action = inner_pair.as_str().to_string();
394 key_action_sequence.push(action);
395 }
396
397 Rule::EOI | Rule::WHITESPACE => {
398 }
400 _ => {
401 return Err(format!(
403 "Unexpected rule encountered during layer.keys processing:{:?}",
404 inner_pair.as_rule()
405 ));
406 }
407 }
408 }
409 }
410 }
411 }
412 Err(e) => {
413 return Err(format!("Invalid keymap format: {}", e));
414 }
415 }
416
417 Ok(key_action_sequence)
418 }
419}