Skip to main content

aethermapd/
remap_engine.rs

1//! RemapEngine for translating input key codes to output key codes
2//!
3//! This module provides the core remapping functionality that translates
4//! input key codes from grabbed devices to remapped output key codes.
5//!
6//! # Layer-Aware Remapping
7//!
8//! The RemapEngine supports multi-layer remapping with cascading fallback.
9//! Each layer can have its own set of key remappings, and higher-priority
10//! layers override lower ones. When looking up a remapping, the engine
11//! searches from the effective layer down to the base layer.
12
13use evdev::Key;
14use std::collections::HashMap;
15use std::fmt;
16use std::sync::Arc;
17use tokio::sync::RwLock;
18use tracing::{debug, info};
19
20use crate::key_parser::{KeyParser, ParseError};
21use crate::layer_manager::LayerManager;
22
23/// Pre-validated remap table for atomic profile switching
24///
25/// RemapTable is an immutable HashMap of key remappings that has been validated
26/// at creation time. It is stored in an Arc for O(1) atomic pointer swaps during
27/// profile switching, avoiding memory allocations or locks during event processing.
28///
29/// # Examples
30///
31/// ```ignore
32/// let table: RemapTable = HashMap::from([
33///     (Key::KEY_A, Key::KEY_B),
34///     (Key::KEY_CAPSLOCK, Key::KEY_LEFTCTRL),
35/// ]);
36/// let shared_table = Arc::new(table);
37/// ```
38pub type RemapTable = HashMap<Key, Key>;
39
40/// Error type for remap engine operations
41#[derive(Debug)]
42pub enum RemapError {
43    /// Invalid key name in configuration
44    InvalidKey {
45        key: String,
46        source: String,
47        parse_error: String,
48    },
49
50    /// Configuration file error
51    Config(String),
52
53    /// Key parsing error
54    ParseError(ParseError),
55}
56
57impl fmt::Display for RemapError {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        match self {
60            RemapError::InvalidKey { key, source, parse_error } => {
61                write!(f, "Invalid key name '{}' in {}: {}", key, source, parse_error)
62            }
63            RemapError::Config(msg) => write!(f, "Configuration error: {}", msg),
64            RemapError::ParseError(e) => write!(f, "Parse error: {}", e),
65        }
66    }
67}
68
69impl std::error::Error for RemapError {}
70
71impl From<ParseError> for RemapError {
72    fn from(err: ParseError) -> Self {
73        RemapError::ParseError(err)
74    }
75}
76
77/// A pre-validated remap profile for fast switching
78///
79/// RemapProfile stores key remappings that have been validated at creation time.
80/// This enables atomic profile switching via Arc pointer exchange (O(1) operation).
81///
82/// # Examples
83///
84/// ```ignore
85/// let profile = RemapProfile::new("gaming", &config)?;
86/// // remaps is Arc<RwLock<HashMap<Key, Key>>> - ready for O(1) lookup
87/// ```
88#[derive(Debug, Clone)]
89pub struct RemapProfile {
90    /// Profile name for identification
91    pub name: String,
92
93    /// Pre-validated remappings from input to output key codes
94    /// Arc enables cheap cloning for profile switching
95    pub remaps: Arc<RwLock<HashMap<Key, Key>>>,
96
97    /// Key parser used for validation (shared with RemapEngine)
98    key_parser: Arc<KeyParser>,
99}
100
101impl RemapProfile {
102    /// Create a new RemapProfile from a configuration HashMap
103    ///
104    /// This method validates all key names before creating the profile.
105    /// If any key name is invalid, the profile creation fails.
106    ///
107    /// # Arguments
108    ///
109    /// * `name` - Profile name for identification
110    /// * `config` - HashMap of key name pairs (input -> output)
111    ///
112    /// # Returns
113    ///
114    /// * `Ok(RemapProfile)` - Profile with validated remappings
115    /// * `Err(RemapError)` - Invalid key name
116    ///
117    /// # Examples
118    ///
119    /// ```ignore
120    /// let mut config = HashMap::new();
121    /// config.insert("capslock".to_string(), "leftctrl".to_string());
122    /// let profile = RemapProfile::new("work", &config)?;
123    /// ```
124    pub fn new(
125        name: String,
126        config: &HashMap<String, String>,
127    ) -> Result<Self, RemapError> {
128        let key_parser = Arc::new(KeyParser::new());
129        Self::with_key_parser(name, config, key_parser)
130    }
131
132    /// Create a new RemapProfile with a shared key parser
133    ///
134    /// This allows sharing a KeyParser instance across multiple components,
135    /// reducing memory overhead when creating many profiles.
136    ///
137    /// # Arguments
138    ///
139    /// * `name` - Profile name for identification
140    /// * `config` - HashMap of key name pairs (input -> output)
141    /// * `key_parser` - Shared KeyParser instance
142    pub fn with_key_parser(
143        name: String,
144        config: &HashMap<String, String>,
145        key_parser: Arc<KeyParser>,
146    ) -> Result<Self, RemapError> {
147        // Validate all keys first (eager validation)
148        let mut remaps = HashMap::new();
149
150        for (input_name, output_name) in config.iter() {
151            let input_key = key_parser.parse(input_name).map_err(|e| {
152                RemapError::InvalidKey {
153                    key: input_name.clone(),
154                    source: "input".to_string(),
155                    parse_error: e.to_string(),
156                }
157            })?;
158
159            let output_key = key_parser.parse(output_name).map_err(|e| {
160                RemapError::InvalidKey {
161                    key: output_name.clone(),
162                    source: "output".to_string(),
163                    parse_error: e.to_string(),
164                }
165            })?;
166
167            remaps.insert(input_key, output_key);
168        }
169
170        Ok(Self {
171            name,
172            remaps: Arc::new(RwLock::new(remaps)),
173            key_parser,
174        })
175    }
176
177    /// Get the profile name
178    pub fn name(&self) -> &str {
179        &self.name
180    }
181
182    /// Get the number of remappings in this profile
183    pub async fn remap_count(&self) -> usize {
184        self.remaps.read().await.len()
185    }
186
187    /// Check if a specific key has a remapping
188    pub async fn has_remap(&self, key_code: Key) -> bool {
189        self.remaps.read().await.contains_key(&key_code)
190    }
191
192    /// Get a clone of the remappings HashMap
193    pub async fn get_remaps(&self) -> HashMap<Key, Key> {
194        self.remaps.read().await.clone()
195    }
196
197    /// Get a reference to the shared remappings Arc
198    ///
199    /// This is used for atomic profile switching via Arc pointer exchange.
200    /// The Arc<RwLock<>> wrapper allows cheap cloning for profile switching.
201    ///
202    /// # Examples
203    ///
204    /// ```ignore
205    /// let profile = RemapProfile::new("gaming".to_string(), &config)?;
206    /// let remaps_arc = profile.remaps_arc();
207    /// // Clone Arc for atomic pointer swap (O(1) operation)
208    /// let cloned = Arc::clone(remaps_arc);
209    /// ```
210    pub fn remaps_arc(&self) -> &Arc<RwLock<HashMap<Key, Key>>> {
211        &self.remaps
212    }
213
214    /// Get the key parser
215    pub fn key_parser(&self) -> &Arc<KeyParser> {
216        &self.key_parser
217    }
218}
219
220/// RemapEngine stores and applies key code remappings
221///
222/// The engine uses a HashMap for O(1) lookup performance during the event loop.
223/// All key names are validated eagerly at config load time (fail-fast semantics).
224///
225/// # Layer-Aware Remapping
226///
227/// The engine supports per-layer remappings with cascading fallback. When querying
228/// for a remapping, it searches from the effective layer down to the base layer,
229/// allowing higher layers to override lower ones.
230///
231/// # Examples
232///
233/// ```ignore
234/// let mut engine = RemapEngine::new();
235///
236/// // Load remappings from config
237/// let mut config = HashMap::new();
238/// config.insert("KEY_A".to_string(), "KEY_B".to_string());
239/// config.insert("capslock".to_string(), "KEY_LEFTCTRL".to_string());
240/// engine.load_config(&config).await.unwrap();
241///
242/// // Remap keys during event loop
243/// if let Some(remapped) = engine.remap(Key::KEY_A).await {
244///     inject_key(remapped); // Inject KEY_B instead
245/// }
246/// ```
247pub struct RemapEngine {
248    /// Mapping from input key codes to output key codes (base layer)
249    /// Wrapped in Arc<RwLock<>> for concurrent access
250    /// Maintained for backward compatibility - maps to layer_remaps[0]
251    remaps: Arc<RwLock<HashMap<Key, Key>>>,
252
253    /// Per-layer remap tables indexed by layer_id
254    /// layer_remaps[0] = base layer, layer_remaps[1] = layer 1, etc.
255    /// Use Arc for cheap cloning during profile switches
256    layer_remaps: Vec<Arc<RwLock<HashMap<Key, Key>>>>,
257
258    /// Layer manager for tracking effective layer per device
259    layer_manager: Arc<RwLock<LayerManager>>,
260
261    /// Key parser for converting key names to evdev::Key codes
262    key_parser: Arc<KeyParser>,
263}
264
265impl RemapEngine {
266    /// Create a new RemapEngine with empty remappings
267    ///
268    /// Initializes with minimum 3 empty layer remap tables (base, layer 1, layer 2).
269    pub fn new() -> Self {
270        // Initialize with minimum 3 layers (base, layer 1, layer 2)
271        let layer_remaps = vec![
272            Arc::new(RwLock::new(HashMap::new())), // Layer 0 (base)
273            Arc::new(RwLock::new(HashMap::new())), // Layer 1
274            Arc::new(RwLock::new(HashMap::new())), // Layer 2
275        ];
276
277        Self {
278            remaps: Arc::new(RwLock::new(HashMap::new())),
279            layer_remaps,
280            layer_manager: Arc::new(RwLock::new(LayerManager::new(None))),
281            key_parser: Arc::new(KeyParser::new()),
282        }
283    }
284
285    /// Create a new RemapEngine with a specific key parser
286    ///
287    /// This allows sharing a KeyParser instance across multiple components
288    pub fn with_key_parser(key_parser: Arc<KeyParser>) -> Self {
289        let layer_remaps = vec![
290            Arc::new(RwLock::new(HashMap::new())), // Layer 0 (base)
291            Arc::new(RwLock::new(HashMap::new())), // Layer 1
292            Arc::new(RwLock::new(HashMap::new())), // Layer 2
293        ];
294
295        Self {
296            remaps: Arc::new(RwLock::new(HashMap::new())),
297            layer_remaps,
298            layer_manager: Arc::new(RwLock::new(LayerManager::new(None))),
299            key_parser,
300        }
301    }
302
303    /// Set the layer manager for this engine
304    ///
305    /// Allows external LayerManager injection (from DaemonState).
306    /// This is useful when a single LayerManager is shared across
307    /// multiple components.
308    ///
309    /// # Arguments
310    ///
311    /// * `layer_manager` - Shared layer manager instance
312    pub fn set_layer_manager(&mut self, layer_manager: Arc<RwLock<LayerManager>>) {
313        self.layer_manager = layer_manager;
314    }
315
316    /// Load remappings for a specific layer
317    ///
318    /// Parses and validates config keys using key_parser, then stores
319    /// the remappings in the specified layer's remap table.
320    ///
321    /// # Arguments
322    ///
323    /// * `layer_id` - Layer ID to load remappings into (0 = base, 1+ = additional)
324    /// * `config` - HashMap of key name pairs (input -> output)
325    ///
326    /// # Returns
327    ///
328    /// * `Ok(())` - Config loaded successfully
329    /// * `Err(RemapError)` - Invalid key name or layer ID out of bounds
330    ///
331    /// # Examples
332    ///
333    /// ```ignore
334    /// let engine = RemapEngine::new();
335    ///
336    /// let mut config = HashMap::new();
337    /// config.insert("a".to_string(), "b".to_string());
338    ///
339    /// // Load into layer 1 (gaming layer)
340    /// engine.load_layer_remap(1, &config).await?;
341    /// ```
342    pub async fn load_layer_remap(
343        &self,
344        layer_id: usize,
345        config: &HashMap<String, String>,
346    ) -> Result<(), RemapError> {
347        // Validate layer_id
348        if layer_id >= self.layer_remaps.len() {
349            return Err(RemapError::Config(format!(
350                "Layer ID {} exceeds available layers ({} layers configured)",
351                layer_id,
352                self.layer_remaps.len()
353            )));
354        }
355
356        info!("Loading key remap configuration for layer {}", layer_id);
357
358        // Eager validation: Parse ALL keys first before storing any
359        let mut parsed_remaps = HashMap::new();
360
361        for (input_name, output_name) in config.iter() {
362            // Parse input key
363            let input_key = self.key_parser.parse(input_name).map_err(|e| {
364                RemapError::InvalidKey {
365                    key: input_name.clone(),
366                    source: "input".to_string(),
367                    parse_error: e.to_string(),
368                }
369            })?;
370
371            // Parse output key
372            let output_key = self.key_parser.parse(output_name).map_err(|e| {
373                RemapError::InvalidKey {
374                    key: output_name.clone(),
375                    source: "output".to_string(),
376                    parse_error: e.to_string(),
377                }
378            })?;
379
380            // Store the parsed mapping
381            parsed_remaps.insert(input_key, output_key);
382
383            debug!("Layer {} remap: {} -> {}", layer_id, input_name, output_name);
384        }
385
386        // All keys validated successfully - now update the layer remappings
387        let mut layer_remap = self.layer_remaps[layer_id].write().await;
388        *layer_remap = parsed_remaps;
389
390        // Also update base layer (remaps field) for backward compatibility
391        if layer_id == 0 {
392            let mut remaps = self.remaps.write().await;
393            *remaps = layer_remap.clone();
394        }
395
396        info!(
397            "Loaded {} key remappings for layer {} successfully",
398            layer_remap.len(),
399            layer_id
400        );
401
402        Ok(())
403    }
404
405    /// Get the number of configured layers
406    ///
407    /// # Returns
408    ///
409    /// The count of layer remap tables available
410    pub fn layer_count(&self) -> usize {
411        self.layer_remaps.len()
412    }
413
414    /// Load remappings from a configuration HashMap
415    ///
416    /// This method performs eager validation - ALL key names are validated
417    /// before any remappings are stored. If any key name is invalid, the entire
418    /// config load fails and no changes are made to the remappings.
419    ///
420    /// # Arguments
421    ///
422    /// * `config` - HashMap of key name pairs (input -> output)
423    ///
424    /// # Returns
425    ///
426    /// * `Ok(())` - Config loaded successfully
427    /// * `Err(RemapError)` - Invalid key name or config error
428    ///
429    /// # Examples
430    ///
431    /// ```ignore
432    /// let engine = RemapEngine::new();
433    ///
434    /// let mut config = HashMap::new();
435    /// config.insert("a".to_string(), "b".to_string());
436    /// config.insert("capslock".to_string(), "leftctrl".to_string());
437    ///
438    /// engine.load_config(&config).await?;
439    /// ```
440    pub async fn load_config(
441        &self,
442        config: &HashMap<String, String>,
443    ) -> Result<(), RemapError> {
444        info!("Loading key remap configuration");
445
446        // Eager validation: Parse ALL keys first before storing any
447        let mut parsed_remaps = HashMap::new();
448
449        for (input_name, output_name) in config.iter() {
450            // Parse input key
451            let input_key = self.key_parser.parse(input_name).map_err(|e| {
452                RemapError::InvalidKey {
453                    key: input_name.clone(),
454                    source: "input".to_string(),
455                    parse_error: e.to_string(),
456                }
457            })?;
458
459            // Parse output key
460            let output_key = self.key_parser.parse(output_name).map_err(|e| {
461                RemapError::InvalidKey {
462                    key: output_name.clone(),
463                    source: "output".to_string(),
464                    parse_error: e.to_string(),
465                }
466            })?;
467
468            // Store the parsed mapping
469            parsed_remaps.insert(input_key, output_key);
470
471            debug!("Remap: {} -> {}", input_name, output_name);
472        }
473
474        // All keys validated successfully - now update the remappings
475        let mut remaps = self.remaps.write().await;
476        *remaps = parsed_remaps;
477
478        info!(
479            "Loaded {} key remappings successfully",
480            remaps.len()
481        );
482
483        Ok(())
484    }
485
486    /// Remap an input key code to its configured output
487    ///
488    /// Returns `Some(output_key)` if a remapping exists, `None` otherwise.
489    /// This allows the caller to distinguish between "no remapping" and
490    /// "remapped to a specific key".
491    ///
492    /// # Arguments
493    ///
494    /// * `key_code` - The input key code to translate
495    ///
496    /// # Returns
497    ///
498    /// * `Some(Key)` - The remapped output key code
499    /// * `None` - No remapping configured for this input
500    ///
501    /// # Examples
502    ///
503    /// ```ignore
504    /// if let Some(output_key) = engine.remap(input_key).await {
505    ///     // A remapping exists - inject the output key
506    ///     injector.key_press(output_key).await?;
507    /// } else {
508    ///     // No remapping - inject the original key
509    ///     injector.key_press(input_key).await?;
510    /// }
511    /// ```
512    pub async fn remap(&self, key_code: Key) -> Option<Key> {
513        let remaps = self.remaps.read().await;
514        let output = remaps.get(&key_code).copied();
515
516        if let Some(out) = output {
517            debug!("Remapped {:?} -> {:?}", key_code, out);
518        }
519
520        output
521    }
522
523    /// Process an input event with full event value (0=release, 1=press, 2=repeat)
524    ///
525    /// This method handles key repeat events correctly by forwarding the event value
526    /// unchanged. For simple key->key remaps, repeat events (value=2) are forwarded
527    /// as-is to ensure held keys produce repeated output.
528    ///
529    /// # Arguments
530    ///
531    /// * `key_code` - The input key code to translate
532    /// * `value` - The event value (0=release, 1=press, 2=repeat)
533    ///
534    /// # Returns
535    ///
536    /// * `Some((Key, i32))` - The remapped output key and event value
537    /// * `None` - No remapping configured for this input
538    ///
539    /// # Examples
540    ///
541    /// ```ignore
542    /// if let Some((output_key, out_value)) = engine.process_event(input_key, value).await {
543    ///     match out_value {
544    ///         0 => injector.key_release(output_key.0).await?,
545    ///         1 => injector.key_press(output_key.0).await?,
546    ///         2 => injector.key_press(output_key.0).await?, // Repeat sends press
547    ///         _ => {},
548    ///     }
549    /// }
550    /// ```
551    pub async fn process_event(&self, key_code: Key, value: i32) -> Option<(Key, i32)> {
552        let remaps = self.remaps.read().await;
553
554        if let Some(&output_key) = remaps.get(&key_code) {
555            // For simple key->key remaps, forward all event values unchanged
556            // This ensures repeat events work correctly
557            Some((output_key, value))
558        } else {
559            None
560        }
561    }
562
563    /// Get the number of active remappings
564    ///
565    /// # Returns
566    ///
567    /// The count of configured key remappings
568    pub async fn remap_count(&self) -> usize {
569        let remaps = self.remaps.read().await;
570        remaps.len()
571    }
572
573    /// Check if a specific key has a remapping configured
574    ///
575    /// # Arguments
576    ///
577    /// * `key_code` - The key code to check
578    ///
579    /// # Returns
580    ///
581    /// `true` if a remapping exists for this key, `false` otherwise
582    pub async fn has_remap(&self, key_code: Key) -> bool {
583        let remaps = self.remaps.read().await;
584        remaps.contains_key(&key_code)
585    }
586
587    /// Clear all remappings
588    ///
589    /// This removes all configured key remappings from the engine
590    pub async fn clear(&self) {
591        let mut remaps = self.remaps.write().await;
592        let count = remaps.len();
593        remaps.clear();
594        info!("Cleared {} key remappings", count);
595    }
596
597    /// Get a reference to the key parser
598    ///
599    /// This allows other components to reuse the same parser instance
600    pub fn key_parser(&self) -> &Arc<KeyParser> {
601        &self.key_parser
602    }
603
604    /// Get a reference to the remappings HashMap
605    ///
606    /// This is primarily useful for debugging and inspection
607    pub async fn get_remaps(&self) -> HashMap<Key, Key> {
608        let remaps = self.remaps.read().await;
609        remaps.clone()
610    }
611
612    /// Remap an input key code using layer-aware lookup with cascade fallback
613    ///
614    /// Gets the effective layer from layer_manager and searches for the remapping
615    /// from that layer down to the base layer. Higher layers override lower ones.
616    ///
617    /// # Arguments
618    ///
619    /// * `device_id` - Device identifier (vendor:product format)
620    /// * `key_code` - The input key code to translate
621    ///
622    /// # Returns
623    ///
624    /// * `Some(Key)` - The remapped output key code
625    /// * `None` - No remapping configured for this input in any active layer
626    ///
627    /// # Examples
628    ///
629    /// ```ignore
630    /// let engine = RemapEngine::new();
631    ///
632    /// // Load different remappings for layer 1
633    /// let mut config = HashMap::new();
634    /// config.insert("KEY_A".to_string(), "KEY_X".to_string());
635    /// engine.load_layer_remap(1, &config).await.unwrap();
636    ///
637    /// // If layer 1 is active, A -> X
638    /// if let Some(output_key) = engine.remap_layer_aware("1532:0220", Key::KEY_A).await {
639    ///     inject_key(output_key);
640    /// }
641    /// ```
642    pub async fn remap_layer_aware(&self, device_id: &str, key_code: Key) -> Option<Key> {
643        // Get the effective layer from layer_manager
644        let effective_layer = self.layer_manager.read().await.get_effective_layer(device_id).await;
645
646        // Cascade from effective layer down to base layer
647        for layer_id in (0..=effective_layer).rev() {
648            if let Some(remaps) = self.layer_remaps.get(layer_id) {
649                let remap_table = remaps.read().await;
650                if let Some(&output_key) = remap_table.get(&key_code) {
651                    debug!(
652                        "Layer-aware remap: device {} key {:?} -> {:?} (from layer {})",
653                        device_id, key_code, output_key, layer_id
654                    );
655                    return Some(output_key);
656                }
657            }
658        }
659
660        // Key not found in any layer
661        debug!(
662            "Layer-aware remap: device {} key {:?} not found in any layer (effective: {})",
663            device_id, key_code, effective_layer
664        );
665        None
666    }
667
668    /// Process an input event with layer-aware lookup
669    ///
670    /// Same logic as remap_layer_aware but returns (output_key, value) tuple
671    /// for easier integration with event processing pipelines. Forwards the
672    /// event value unchanged (preserves press/release/repeat).
673    ///
674    /// # Arguments
675    ///
676    /// * `device_id` - Device identifier (vendor:product format)
677    /// * `key_code` - The input key code to translate
678    /// * `value` - The event value (0=release, 1=press, 2=repeat)
679    ///
680    /// # Returns
681    ///
682    /// * `Some((Key, i32))` - The remapped output key and event value
683    /// * `None` - No remapping configured for this input in any active layer
684    ///
685    /// # Examples
686    ///
687    /// ```ignore
688    /// if let Some((output_key, out_value)) = engine.process_event_layer_aware(
689    ///     device_id, input_key, value
690    /// ).await {
691    ///     match out_value {
692    ///         0 => injector.key_release(output_key).await?,
693    ///         1 => injector.key_press(output_key).await?,
694    ///         2 => injector.key_press(output_key).await?,
695    ///         _ => {},
696    ///     }
697    /// }
698    /// ```
699    pub async fn process_event_layer_aware(
700        &self,
701        device_id: &str,
702        key_code: Key,
703        value: i32,
704    ) -> Option<(Key, i32)> {
705        // Get the effective layer from layer_manager
706        let effective_layer = self.layer_manager.read().await.get_effective_layer(device_id).await;
707
708        // Cascade from effective layer down to base layer
709        for layer_id in (0..=effective_layer).rev() {
710            if let Some(remaps) = self.layer_remaps.get(layer_id) {
711                let remap_table = remaps.read().await;
712                if let Some(&output_key) = remap_table.get(&key_code) {
713                    debug!(
714                        "Layer-aware event: device {} key {:?} -> {:?} (value: {}, from layer {})",
715                        device_id, key_code, output_key, value, layer_id
716                    );
717                    return Some((output_key, value));
718                }
719            }
720        }
721
722        // Key not found in any layer
723        None
724    }
725
726    /// Get a reference to the layer manager
727    ///
728    /// This allows external components to query or manipulate layer state
729    pub fn layer_manager(&self) -> &Arc<RwLock<LayerManager>> {
730        &self.layer_manager
731    }
732
733    /// Get remappings for a specific layer
734    ///
735    /// Returns a clone of the remappings HashMap for the specified layer.
736    /// This is primarily useful for debugging and inspection.
737    ///
738    /// # Arguments
739    ///
740    /// * `layer_id` - Layer ID to get remappings from
741    ///
742    /// # Returns
743    ///
744    /// * `Some(HashMap<Key, Key>)` - Layer remappings if layer exists
745    /// * `None` - Layer ID out of bounds
746    pub async fn get_layer_remaps(&self, layer_id: usize) -> Option<HashMap<Key, Key>> {
747        if let Some(remaps) = self.layer_remaps.get(layer_id) {
748            let remap_table = remaps.read().await;
749            Some(remap_table.clone())
750        } else {
751            None
752        }
753    }
754}
755
756impl Default for RemapEngine {
757    fn default() -> Self {
758        Self::new()
759    }
760}
761
762#[cfg(test)]
763mod tests {
764    use super::*;
765
766    #[tokio::test]
767    async fn test_remap_engine_creation() {
768        let engine = RemapEngine::new();
769        assert_eq!(engine.remap_count().await, 0);
770    }
771
772    #[tokio::test]
773    async fn test_load_config_basic() {
774        let engine = RemapEngine::new();
775
776        let mut config = HashMap::new();
777        config.insert("KEY_A".to_string(), "KEY_B".to_string());
778
779        let result = engine.load_config(&config).await;
780        assert!(result.is_ok());
781        assert_eq!(engine.remap_count().await, 1);
782    }
783
784    #[tokio::test]
785    async fn test_load_config_with_friendly_names() {
786        let engine = RemapEngine::new();
787
788        let mut config = HashMap::new();
789        config.insert("a".to_string(), "b".to_string());
790        config.insert("capslock".to_string(), "leftctrl".to_string());
791
792        let result = engine.load_config(&config).await;
793        assert!(result.is_ok());
794        assert_eq!(engine.remap_count().await, 2);
795    }
796
797    #[tokio::test]
798    async fn test_remap_returns_correct_key() {
799        let engine = RemapEngine::new();
800
801        let mut config = HashMap::new();
802        config.insert("KEY_A".to_string(), "KEY_B".to_string());
803
804        engine.load_config(&config).await.unwrap();
805
806        // Test that A is remapped to B
807        let result = engine.remap(Key::KEY_A).await;
808        assert_eq!(result, Some(Key::KEY_B));
809
810        // Test that unmapped keys return None
811        let result = engine.remap(Key::KEY_C).await;
812        assert_eq!(result, None);
813    }
814
815    #[tokio::test]
816    async fn test_invalid_input_key_fails_validation() {
817        let engine = RemapEngine::new();
818
819        let mut config = HashMap::new();
820        config.insert("nonexistent_key".to_string(), "KEY_B".to_string());
821
822        let result = engine.load_config(&config).await;
823        assert!(result.is_err());
824
825        match result {
826            Err(RemapError::InvalidKey { key, source, .. }) => {
827                assert_eq!(key, "nonexistent_key");
828                assert_eq!(source, "input");
829            }
830            _ => panic!("Expected InvalidKey error"),
831        }
832    }
833
834    #[tokio::test]
835    async fn test_invalid_output_key_fails_validation() {
836        let engine = RemapEngine::new();
837
838        let mut config = HashMap::new();
839        config.insert("KEY_A".to_string(), "nonexistent_key".to_string());
840
841        let result = engine.load_config(&config).await;
842        assert!(result.is_err());
843
844        match result {
845            Err(RemapError::InvalidKey { key, source, .. }) => {
846                assert_eq!(key, "nonexistent_key");
847                assert_eq!(source, "output");
848            }
849            _ => panic!("Expected InvalidKey error"),
850        }
851    }
852
853    #[tokio::test]
854    async fn test_eager_validation_no_partial_load() {
855        let engine = RemapEngine::new();
856
857        let mut config = HashMap::new();
858        // First entry is valid
859        config.insert("KEY_A".to_string(), "KEY_B".to_string());
860        // Second entry has invalid output key
861        config.insert("KEY_C".to_string(), "invalid_key".to_string());
862
863        let result = engine.load_config(&config).await;
864
865        // Should fail entirely - no partial load
866        assert!(result.is_err());
867
868        // Engine should have 0 remappings, not 1
869        assert_eq!(engine.remap_count().await, 0);
870    }
871
872    #[tokio::test]
873    async fn test_case_insensitive_config() {
874        let engine = RemapEngine::new();
875
876        let mut config = HashMap::new();
877        config.insert("key_a".to_string(), "KEY_B".to_string());
878        config.insert("CAPSLOCK".to_string(), "leftctrl".to_string());
879
880        let result = engine.load_config(&config).await;
881        assert!(result.is_ok());
882        assert_eq!(engine.remap_count().await, 2);
883
884        // Verify the remappings work
885        assert_eq!(engine.remap(Key::KEY_A).await, Some(Key::KEY_B));
886        assert_eq!(engine.remap(Key::KEY_CAPSLOCK).await, Some(Key::KEY_LEFTCTRL));
887    }
888
889    #[tokio::test]
890    async fn test_has_remap() {
891        let engine = RemapEngine::new();
892
893        assert!(!engine.has_remap(Key::KEY_A).await);
894
895        let mut config = HashMap::new();
896        config.insert("KEY_A".to_string(), "KEY_B".to_string());
897        engine.load_config(&config).await.unwrap();
898
899        assert!(engine.has_remap(Key::KEY_A).await);
900        assert!(!engine.has_remap(Key::KEY_C).await);
901    }
902
903    #[tokio::test]
904    async fn test_clear_remaps() {
905        let engine = RemapEngine::new();
906
907        let mut config = HashMap::new();
908        config.insert("KEY_A".to_string(), "KEY_B".to_string());
909        config.insert("KEY_C".to_string(), "KEY_D".to_string());
910        engine.load_config(&config).await.unwrap();
911
912        assert_eq!(engine.remap_count().await, 2);
913
914        engine.clear().await;
915        assert_eq!(engine.remap_count().await, 0);
916    }
917
918    #[tokio::test]
919    async fn test_complex_remap_scenario() {
920        let engine = RemapEngine::new();
921
922        // Simulate a typical CapsLock -> Ctrl remap configuration
923        let mut config = HashMap::new();
924        config.insert("capslock".to_string(), "leftctrl".to_string());
925        config.insert("esc".to_string(), "grave".to_string());
926
927        engine.load_config(&config).await.unwrap();
928
929        // Test CapsLock -> LeftCtrl
930        assert_eq!(engine.remap(Key::KEY_CAPSLOCK).await, Some(Key::KEY_LEFTCTRL));
931        // Test ESC -> Grave
932        assert_eq!(engine.remap(Key::KEY_ESC).await, Some(Key::KEY_GRAVE));
933    }
934
935    #[tokio::test]
936    async fn test_shared_key_parser() {
937        let parser = Arc::new(KeyParser::new());
938        let _engine = RemapEngine::with_key_parser(parser.clone());
939
940        // Verify we can use the parser
941        assert_eq!(parser.parse("a"), Ok(Key::KEY_A));
942    }
943
944    #[tokio::test]
945    async fn test_get_remaps() {
946        let engine = RemapEngine::new();
947
948        let mut config = HashMap::new();
949        config.insert("KEY_A".to_string(), "KEY_B".to_string());
950
951        engine.load_config(&config).await.unwrap();
952
953        let remaps = engine.get_remaps().await;
954        assert_eq!(remaps.len(), 1);
955        assert_eq!(remaps.get(&Key::KEY_A), Some(&Key::KEY_B));
956    }
957
958    #[tokio::test]
959    async fn test_remap_to_none_for_unmapped_keys() {
960        let engine = RemapEngine::new();
961
962        let mut config = HashMap::new();
963        config.insert("KEY_A".to_string(), "KEY_B".to_string());
964
965        engine.load_config(&config).await.unwrap();
966
967        // Mapped key returns Some
968        assert!(engine.remap(Key::KEY_A).await.is_some());
969
970        // Unmapped keys return None (not an error, just no mapping)
971        assert!(engine.remap(Key::KEY_Z).await.is_none());
972        assert!(engine.remap(Key::KEY_0).await.is_none());
973    }
974
975    // RemapProfile tests
976
977    #[tokio::test]
978    async fn test_remap_profile_creation() {
979        let mut config = HashMap::new();
980        config.insert("capslock".to_string(), "leftctrl".to_string());
981        config.insert("a".to_string(), "b".to_string());
982
983        let profile = RemapProfile::new("test-profile".to_string(), &config);
984        assert!(profile.is_ok());
985
986        let profile = profile.unwrap();
987        assert_eq!(profile.name(), "test-profile");
988        assert_eq!(profile.remap_count().await, 2);
989    }
990
991    #[tokio::test]
992    async fn test_remap_profile_invalid_key_fails() {
993        let mut config = HashMap::new();
994        config.insert("invalid_key".to_string(), "KEY_A".to_string());
995
996        let result = RemapProfile::new("bad-profile".to_string(), &config);
997        assert!(result.is_err());
998    }
999
1000    #[tokio::test]
1001    async fn test_remap_profile_arc_cloning() {
1002        let mut config = HashMap::new();
1003        config.insert("KEY_A".to_string(), "KEY_B".to_string());
1004
1005        let profile1 = RemapProfile::new("profile1".to_string(), &config).unwrap();
1006        let profile2 = profile1.clone();
1007
1008        // Both should point to same remaps (Arc::clone)
1009        assert_eq!(profile1.name(), profile2.name());
1010        assert_eq!(profile1.remap_count().await, profile2.remap_count().await);
1011    }
1012
1013    // Layer-aware remapping tests
1014
1015    #[tokio::test]
1016    async fn test_layer_remap_creation() {
1017        let engine = RemapEngine::new();
1018
1019        // Verify minimum 3 layers are created
1020        assert_eq!(engine.layer_count(), 3);
1021
1022        // All layers should be empty initially
1023        let layer_0 = engine.get_layer_remaps(0).await;
1024        let layer_1 = engine.get_layer_remaps(1).await;
1025        let layer_2 = engine.get_layer_remaps(2).await;
1026
1027        assert!(layer_0.is_some());
1028        assert!(layer_1.is_some());
1029        assert!(layer_2.is_some());
1030
1031        assert!(layer_0.unwrap().is_empty());
1032        assert!(layer_1.unwrap().is_empty());
1033        assert!(layer_2.unwrap().is_empty());
1034
1035        // Layer 3 should not exist
1036        assert!(engine.get_layer_remaps(3).await.is_none());
1037    }
1038
1039    #[tokio::test]
1040    async fn test_load_layer_remap() {
1041        let engine = RemapEngine::new();
1042
1043        // Load config into layer 1
1044        let mut config = HashMap::new();
1045        config.insert("KEY_A".to_string(), "KEY_X".to_string());
1046        config.insert("KEY_B".to_string(), "KEY_Y".to_string());
1047
1048        let result = engine.load_layer_remap(1, &config).await;
1049        assert!(result.is_ok());
1050
1051        // Verify layer 1 has the remappings
1052        let layer_1 = engine.get_layer_remaps(1).await.unwrap();
1053        assert_eq!(layer_1.len(), 2);
1054        assert_eq!(layer_1.get(&Key::KEY_A), Some(&Key::KEY_X));
1055        assert_eq!(layer_1.get(&Key::KEY_B), Some(&Key::KEY_Y));
1056
1057        // Base layer should still be empty
1058        let layer_0 = engine.get_layer_remaps(0).await.unwrap();
1059        assert!(layer_0.is_empty());
1060    }
1061
1062    #[tokio::test]
1063    async fn test_load_layer_remap_invalid_layer() {
1064        let engine = RemapEngine::new();
1065
1066        let mut config = HashMap::new();
1067        config.insert("KEY_A".to_string(), "KEY_B".to_string());
1068
1069        // Try to load into non-existent layer (only 0, 1, 2 exist)
1070        let result = engine.load_layer_remap(5, &config).await;
1071        assert!(result.is_err());
1072
1073        match result {
1074            Err(RemapError::Config(msg)) => {
1075                assert!(msg.contains("Layer ID 5"));
1076                assert!(msg.contains("exceeds available layers"));
1077            }
1078            _ => panic!("Expected Config error with layer ID message"),
1079        }
1080    }
1081
1082    #[tokio::test]
1083    async fn test_remap_layer_aware_base_layer() {
1084        let engine = RemapEngine::new();
1085
1086        // Load config into base layer (layer 0)
1087        let mut config = HashMap::new();
1088        config.insert("KEY_A".to_string(), "KEY_B".to_string());
1089
1090        engine.load_layer_remap(0, &config).await.unwrap();
1091
1092        // With no active layers, should use base layer
1093        let result = engine.remap_layer_aware("test_device", Key::KEY_A).await;
1094        assert_eq!(result, Some(Key::KEY_B));
1095
1096        // Unmapped key should return None
1097        let result = engine.remap_layer_aware("test_device", Key::KEY_C).await;
1098        assert_eq!(result, None);
1099    }
1100
1101    #[tokio::test]
1102    async fn test_remap_layer_aware_cascade() {
1103        let engine = RemapEngine::new();
1104
1105        // Load different remappings for each layer
1106        // Base layer: A -> B, C -> D
1107        let mut base_config = HashMap::new();
1108        base_config.insert("KEY_A".to_string(), "KEY_B".to_string());
1109        base_config.insert("KEY_C".to_string(), "KEY_D".to_string());
1110        engine.load_layer_remap(0, &base_config).await.unwrap();
1111
1112        // Layer 1: A -> X (overrides base)
1113        let mut layer1_config = HashMap::new();
1114        layer1_config.insert("KEY_A".to_string(), "KEY_X".to_string());
1115        engine.load_layer_remap(1, &layer1_config).await.unwrap();
1116
1117        // Activate layer 1 using hold mode
1118        engine.layer_manager.write().await.activate_hold_layer("test_device", 1).await.unwrap();
1119
1120        // A should be remapped to X (layer 1 override)
1121        let result = engine.remap_layer_aware("test_device", Key::KEY_A).await;
1122        assert_eq!(result, Some(Key::KEY_X));
1123
1124        // C should cascade to base layer (D)
1125        let result = engine.remap_layer_aware("test_device", Key::KEY_C).await;
1126        assert_eq!(result, Some(Key::KEY_D));
1127    }
1128
1129    #[tokio::test]
1130    async fn test_remap_layer_aware_priority() {
1131        let engine = RemapEngine::new();
1132
1133        // Base layer: A -> B
1134        let mut base_config = HashMap::new();
1135        base_config.insert("KEY_A".to_string(), "KEY_B".to_string());
1136        engine.load_layer_remap(0, &base_config).await.unwrap();
1137
1138        // Layer 1: A -> X
1139        let mut layer1_config = HashMap::new();
1140        layer1_config.insert("KEY_A".to_string(), "KEY_X".to_string());
1141        engine.load_layer_remap(1, &layer1_config).await.unwrap();
1142
1143        // Layer 2: A -> Y (highest priority)
1144        let mut layer2_config = HashMap::new();
1145        layer2_config.insert("KEY_A".to_string(), "KEY_Y".to_string());
1146        engine.load_layer_remap(2, &layer2_config).await.unwrap();
1147
1148        // Activate both layers 1 and 2 (layer 2 has higher priority)
1149        let lm = engine.layer_manager.write().await;
1150        lm.activate_hold_layer("test_device", 1).await.unwrap();
1151        lm.activate_hold_layer("test_device", 2).await.unwrap();
1152        drop(lm);
1153
1154        // With both layers active, A -> Y (highest priority wins)
1155        let result = engine.remap_layer_aware("test_device", Key::KEY_A).await;
1156        assert_eq!(result, Some(Key::KEY_Y));
1157
1158        // Deactivate layer 2, should fall back to layer 1
1159        engine.layer_manager.write().await.deactivate_hold_layer("test_device", 2).await.unwrap();
1160        let result = engine.remap_layer_aware("test_device", Key::KEY_A).await;
1161        assert_eq!(result, Some(Key::KEY_X));
1162
1163        // Deactivate layer 1, should fall back to base
1164        engine.layer_manager.write().await.deactivate_hold_layer("test_device", 1).await.unwrap();
1165        let result = engine.remap_layer_aware("test_device", Key::KEY_A).await;
1166        assert_eq!(result, Some(Key::KEY_B));
1167    }
1168
1169    #[tokio::test]
1170    async fn test_remap_layer_aware_no_remap() {
1171        let engine = RemapEngine::new();
1172
1173        // Load config into base layer only
1174        let mut config = HashMap::new();
1175        config.insert("KEY_A".to_string(), "KEY_B".to_string());
1176        engine.load_layer_remap(0, &config).await.unwrap();
1177
1178        // Activate layer 1 (which is empty)
1179        engine.layer_manager.write().await.activate_layer("test_device", 1).await;
1180
1181        // Key not in any layer should return None (cascades through all layers)
1182        let result = engine.remap_layer_aware("test_device", Key::KEY_Z).await;
1183        assert_eq!(result, None);
1184    }
1185
1186    #[tokio::test]
1187    async fn test_process_event_layer_aware() {
1188        let engine = RemapEngine::new();
1189
1190        // Load config into layer 1
1191        let mut config = HashMap::new();
1192        config.insert("KEY_A".to_string(), "KEY_X".to_string());
1193        engine.load_layer_remap(1, &config).await.unwrap();
1194
1195        // Activate layer 1 using hold mode
1196        engine.layer_manager.write().await.activate_hold_layer("test_device", 1).await.unwrap();
1197
1198        // Test press event (value = 1)
1199        let result = engine.process_event_layer_aware("test_device", Key::KEY_A, 1).await;
1200        assert_eq!(result, Some((Key::KEY_X, 1)));
1201
1202        // Test release event (value = 0)
1203        let result = engine.process_event_layer_aware("test_device", Key::KEY_A, 0).await;
1204        assert_eq!(result, Some((Key::KEY_X, 0)));
1205
1206        // Test repeat event (value = 2)
1207        let result = engine.process_event_layer_aware("test_device", Key::KEY_A, 2).await;
1208        assert_eq!(result, Some((Key::KEY_X, 2)));
1209
1210        // Unmapped key should return None
1211        let result = engine.process_event_layer_aware("test_device", Key::KEY_Z, 1).await;
1212        assert_eq!(result, None);
1213    }
1214
1215    #[tokio::test]
1216    async fn test_layer_manager_accessor() {
1217        let engine = RemapEngine::new();
1218
1219        // Get layer manager reference
1220        let layer_manager = engine.layer_manager();
1221
1222        // Activate a layer through the accessor
1223        layer_manager.write().await.activate_layer("test_device", 1).await;
1224
1225        // Verify layer is active
1226        let effective = layer_manager.read().await.get_effective_layer("test_device").await;
1227        assert_eq!(effective, 1);
1228    }
1229
1230    #[tokio::test]
1231    async fn test_multiple_devices_independent_layers() {
1232        let engine = RemapEngine::new();
1233
1234        // Load different configs for each layer
1235        let mut config1 = HashMap::new();
1236        config1.insert("KEY_A".to_string(), "KEY_X".to_string());
1237        engine.load_layer_remap(1, &config1).await.unwrap();
1238
1239        let mut config2 = HashMap::new();
1240        config2.insert("KEY_A".to_string(), "KEY_Y".to_string());
1241        engine.load_layer_remap(2, &config2).await.unwrap();
1242
1243        // Device 1 on layer 1, Device 2 on layer 2
1244        let lm = engine.layer_manager.write().await;
1245        lm.activate_hold_layer("device1", 1).await.unwrap();
1246        lm.activate_hold_layer("device2", 2).await.unwrap();
1247        drop(lm);
1248
1249        // Device 1 should get X (from layer 1)
1250        let result1 = engine.remap_layer_aware("device1", Key::KEY_A).await;
1251        assert_eq!(result1, Some(Key::KEY_X));
1252
1253        // Device 2 should get Y (from layer 2)
1254        let result2 = engine.remap_layer_aware("device2", Key::KEY_A).await;
1255        assert_eq!(result2, Some(Key::KEY_Y));
1256
1257        // Device 3 (no layers active) should get None (base layer empty)
1258        let result3 = engine.remap_layer_aware("device3", Key::KEY_A).await;
1259        assert_eq!(result3, None);
1260    }
1261
1262    #[tokio::test]
1263    async fn test_load_layer_remap_eager_validation() {
1264        let engine = RemapEngine::new();
1265
1266        let mut config = HashMap::new();
1267        // First entry is valid
1268        config.insert("KEY_A".to_string(), "KEY_B".to_string());
1269        // Second entry has invalid key
1270        config.insert("KEY_C".to_string(), "invalid_key".to_string());
1271
1272        // Should fail entirely - no partial load
1273        let result = engine.load_layer_remap(1, &config).await;
1274        assert!(result.is_err());
1275
1276        // Layer 1 should still be empty (no partial load)
1277        let layer_1 = engine.get_layer_remaps(1).await.unwrap();
1278        assert!(layer_1.is_empty());
1279    }
1280}