diff --git a/.changes/serde.md b/.changes/serde.md new file mode 100644 index 0000000..b3764c3 --- /dev/null +++ b/.changes/serde.md @@ -0,0 +1,5 @@ +--- +"global-hotkey": "patch" +--- + +Add `serde` feature flag and implement `Deserialize` and `Serialize` for `GlobalHotKeyEvent`, `HotKeyState` and `HotKey` types. diff --git a/.changes/to_string.md b/.changes/to_string.md new file mode 100644 index 0000000..f685bd0 --- /dev/null +++ b/.changes/to_string.md @@ -0,0 +1,5 @@ +--- +"global-hotkey": "patch" +--- + +Add `HotKey::into_string` method and implement `Display` for `HotKey`. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c60bf40..64a3046 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,4 +36,4 @@ jobs: sudo apt-get install -y libgtk-3-dev libxdo-dev - uses: dtolnay/rust-toolchain@stable - - run: cargo test + - run: cargo test --all-features diff --git a/Cargo.toml b/Cargo.toml index 784e28b..e34e655 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,15 @@ repository = "https://github.com/amrbashir/global-hotkey" documentation = "https://docs.rs/global-hotkey" categories = [ "gui" ] +[features] +serde = [ "dep:serde" ] + [dependencies] crossbeam-channel = "0.5" keyboard-types = "0.7" once_cell = "1" thiserror = "1" +serde = { version = "1", optional = true, features = ["derive"] } [target."cfg(target_os = \"macos\")".dependencies] bitflags = "2" diff --git a/src/hotkey.rs b/src/hotkey.rs index 7e2195c..5e4f484 100644 --- a/src/hotkey.rs +++ b/src/hotkey.rs @@ -28,7 +28,7 @@ //! pub use keyboard_types::{Code, Modifiers}; -use std::{borrow::Borrow, hash::Hash, str::FromStr}; +use std::{borrow::Borrow, fmt::Display, hash::Hash, str::FromStr}; #[cfg(target_os = "macos")] pub const CMD_OR_CTRL: Modifiers = Modifiers::SUPER; @@ -50,9 +50,35 @@ pub enum HotKeyParseError { /// one key ([`Code`](crate::hotkey::Code)). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HotKey { - pub(crate) mods: Modifiers, - pub(crate) key: Code, - id: u32, + /// The hotkey modifiers. + pub mods: Modifiers, + /// The hotkey key. + pub key: Code, + /// The hotkey id. + pub id: u32, +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for HotKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let hotkey = String::deserialize(deserializer)?; + hotkey + .parse() + .map_err(|e: HotKeyParseError| serde::de::Error::custom(e.to_string())) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for HotKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.to_string().serialize(serializer) + } } impl HotKey { @@ -65,27 +91,13 @@ impl HotKey { mods.insert(Modifiers::SUPER); } - let id = Self::generate_hash(mods, key); - - Self { mods, key, id } + let mut hotkey = Self { mods, key, id: 0 }; + hotkey.id = hotkey.generate_hash(); + hotkey } - fn generate_hash(mods: Modifiers, key: Code) -> u32 { - let mut hotkey_str = String::new(); - if mods.contains(Modifiers::SHIFT) { - hotkey_str.push_str("shift+") - } - if mods.contains(Modifiers::CONTROL) { - hotkey_str.push_str("control+") - } - if mods.contains(Modifiers::ALT) { - hotkey_str.push_str("alt+") - } - if mods.contains(Modifiers::SUPER) { - hotkey_str.push_str("super+") - } - hotkey_str.push_str(&key.to_string()); - + fn generate_hash(&self) -> u32 { + let hotkey_str = self.into_string(); let mut hasher = std::collections::hash_map::DefaultHasher::new(); hotkey_str.hash(&mut hasher); std::hash::Hasher::finish(&hasher) as u32 @@ -105,6 +117,31 @@ impl HotKey { let key = key.borrow(); self.mods == *modifiers & base_mods && self.key == *key } + + /// Converts this hotkey into a string. + pub fn into_string(self) -> String { + let mut hotkey = String::new(); + if self.mods.contains(Modifiers::SHIFT) { + hotkey.push_str("shift+") + } + if self.mods.contains(Modifiers::CONTROL) { + hotkey.push_str("control+") + } + if self.mods.contains(Modifiers::ALT) { + hotkey.push_str("alt+") + } + if self.mods.contains(Modifiers::SUPER) { + hotkey.push_str("super+") + } + hotkey.push_str(&self.key.to_string()); + hotkey + } +} + +impl Display for HotKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.into_string()) + } } // HotKey::from_str is available to be backward diff --git a/src/lib.rs b/src/lib.rs index ba62036..aba71b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ use hotkey::HotKey; /// Describes the state of the [`HotKey`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum HotKeyState { /// The [`HotKey`] is pressed (the key is down). Pressed, @@ -71,6 +72,7 @@ pub enum HotKeyState { /// Describes a global hotkey event emitted when a [`HotKey`] is pressed or released. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct GlobalHotKeyEvent { /// Id of the associated [`HotKey`]. pub id: u32,