keyboard-codes 0.2.0

Cross-platform keyboard key code mapping and conversion
Documentation
//! keyboard-codes: Cross-platform keyboard key code mapping and conversion
//!
//! This crate provides comprehensive keyboard key definitions and cross-platform
//! code mapping for Windows, Linux, and macOS. It supports standard keys,
//! modifiers, custom key mapping, and bidirectional conversion between key names
//! and platform-specific codes.
//!
//! # Examples
//!
//! ```rust
//! use keyboard_codes::{Key, Modifier, Platform, KeyCodeMapper};
//!
//! // Parse key from string
//! let key: Key = "Enter".parse().unwrap();
//! assert_eq!(key, Key::Enter);
//!
//! // Convert key to platform-specific code
//! let windows_code = key.to_code(Platform::Windows);
//! let linux_code = key.to_code(Platform::Linux);
//!
//! // Parse key from code
//! let key_from_code = Key::from_code(0x0D, Platform::Windows).unwrap();
//! assert_eq!(key_from_code, Key::Enter);
//! ```
//!
//! # Features
//!
//! - `serde`: Enables serialization/deserialization support
//! - `phf`: Uses perfect hash functions for faster lookups

#![deny(missing_docs)]
#![warn(clippy::all)]

/// Error types for keyboard parsing and mapping
pub mod error;
/// Key code mapping implementations
pub mod mapping;
/// Advanced keyboard input parsing with alias support
pub mod parser;
/// Core type definitions for keyboard keys and platforms
pub mod types;
/// Utility functions and helpers
pub mod utils;

// Re-export main types for convenient access
pub use error::KeyParseError;
pub use mapping::custom::{CustomKey, CustomKeyMap};
pub use types::{Key, KeyCodeMapper, Modifier, Platform};

// Re-export core parsing functions
pub use mapping::standard::{parse_key_ignore_case, parse_modifier_ignore_case};

// Re-export advanced parser functionality
pub use parser::{
    parse_input, parse_modifier_with_aliases, parse_shortcut_flexible, parse_shortcut_sequence,
    parse_shortcut_with_aliases, Shortcut,
};

use std::str::FromStr;

// Implement FromStr for Key using the standard mappings
impl FromStr for Key {
    type Err = KeyParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        mapping::standard::parse_key_from_str(s)
    }
}

// Implement FromStr for Modifier using the standard mappings
impl FromStr for Modifier {
    type Err = KeyParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        mapping::standard::parse_modifier_from_str(s)
    }
}

/// Get the current platform based on compilation target
pub fn current_platform() -> Platform {
    #[cfg(target_os = "windows")]
    return Platform::Windows;

    #[cfg(target_os = "linux")]
    return Platform::Linux;

    #[cfg(target_os = "macos")]
    return Platform::MacOS;

    #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
    panic!("Unsupported platform");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_key_from_str() {
        assert_eq!("Escape".parse::<Key>().unwrap(), Key::Escape);
        assert_eq!("A".parse::<Key>().unwrap(), Key::A);
        assert!("UnknownKey".parse::<Key>().is_err());
    }

    #[test]
    fn test_modifier_from_str() {
        assert_eq!("Shift".parse::<Modifier>().unwrap(), Modifier::Shift);
        assert_eq!("Control".parse::<Modifier>().unwrap(), Modifier::Control);
        assert!("UnknownModifier".parse::<Modifier>().is_err());
    }

    #[test]
    fn test_current_platform() {
        let platform = current_platform();
        assert!(matches!(
            platform,
            Platform::Windows | Platform::Linux | Platform::MacOS
        ));
    }

    #[test]
    fn test_parse_modifier_with_aliases() {
        assert_eq!(
            parse_modifier_with_aliases("ctrl").unwrap(),
            Modifier::Control
        );
        assert_eq!(parse_modifier_with_aliases("Cmd").unwrap(), Modifier::Meta);
        assert_eq!(parse_modifier_with_aliases("win").unwrap(), Modifier::Meta);
        assert_eq!(
            parse_modifier_with_aliases("lctrl").unwrap(),
            Modifier::LeftControl
        );
    }

    #[test]
    fn test_parse_shortcut_with_aliases() {
        let shortcut = parse_shortcut_with_aliases("ctrl+shift+a").unwrap();
        assert_eq!(shortcut.modifiers, vec![Modifier::Control, Modifier::Shift]);
        assert_eq!(shortcut.key, Key::A);

        let shortcut = parse_shortcut_with_aliases("cmd+q").unwrap();
        assert_eq!(shortcut.modifiers, vec![Modifier::Meta]);
        assert_eq!(shortcut.key, Key::Q);
    }

    #[test]
    fn test_parse_input() {
        let shortcut = parse_input("a").unwrap();
        assert!(shortcut.is_simple());
        assert_eq!(shortcut.key, Key::A);

        let shortcut = parse_input("ctrl+a").unwrap();
        assert_eq!(shortcut.modifiers, vec![Modifier::Control]);
        assert_eq!(shortcut.key, Key::A);
    }
}