Skip to main content

kbd_global/
error.rs

1//! Error types for the kbd-global runtime.
2//!
3//! Errors are scoped to the operation that produces them:
4//!
5//! - [`StartupError`] — manager construction and engine spawn
6//! - [`RegisterError`] — binding registration (hotkeys, sequences, tap-hold)
7//! - [`LayerError`] — layer definition and stack operations
8//! - [`QueryError`] — hotkey queries that accept parseable input
9//! - [`ShutdownError`] — manager and engine shutdown
10//! - [`ManagerStopped`] — standalone error for simple queries with no
11//!   domain failure modes
12
13/// The manager has been shut down or the engine thread has exited.
14///
15/// Returned standalone by query methods whose only failure mode is
16/// a dead engine, and as a variant inside other scoped error types.
17#[derive(Debug, thiserror::Error)]
18#[error("hotkey manager is no longer running")]
19pub struct ManagerStopped;
20
21/// Error returned when starting the manager or spawning the engine.
22///
23/// Covers device access, unsupported feature requests (e.g. grab mode
24/// without the `grab` feature), and engine thread failures.
25#[derive(Debug, thiserror::Error)]
26#[non_exhaustive]
27pub enum StartupError {
28    /// An input device operation failed (open, read, ioctl, etc.).
29    #[error("input device operation failed")]
30    Device,
31    /// The requested feature is not supported by the selected backend.
32    ///
33    /// Returned when calling [`HotkeyManagerBuilder::grab()`](crate::manager::HotkeyManagerBuilder::grab)
34    /// without the `grab` feature enabled.
35    #[error("requested feature is unsupported by the selected backend")]
36    UnsupportedFeature,
37    /// An internal engine failure occurred (thread panic, fd error, etc.).
38    #[error("hotkey engine encountered an internal failure")]
39    Engine,
40}
41
42impl From<kbd_evdev::error::Error> for StartupError {
43    fn from(error: kbd_evdev::error::Error) -> Self {
44        tracing::warn!(%error, "evdev backend error");
45        Self::Device
46    }
47}
48
49/// Error returned when registering a binding fails.
50///
51/// Covers hotkey registration, sequence registration, and tap-hold
52/// registration through the manager.
53#[derive(Debug, thiserror::Error)]
54#[non_exhaustive]
55pub enum RegisterError {
56    /// A hotkey string like `"Ctrl+A"` could not be parsed.
57    #[error("parse error: {0}")]
58    Parse(#[from] kbd::error::ParseHotkeyError),
59    /// The hotkey is already bound to another action.
60    #[error("hotkey registration conflicts with an existing binding")]
61    AlreadyRegistered,
62    /// The requested feature is not supported by the selected backend.
63    ///
64    /// Returned when registering a tap-hold binding without grab mode enabled.
65    #[error("requested feature is unsupported by the selected backend")]
66    UnsupportedFeature,
67    /// The manager has been shut down or the engine thread has exited.
68    #[error(transparent)]
69    ManagerStopped(#[from] ManagerStopped),
70}
71
72impl From<kbd::error::RegisterError> for RegisterError {
73    fn from(error: kbd::error::RegisterError) -> Self {
74        match error {
75            kbd::error::RegisterError::Parse(e) => Self::Parse(e),
76            kbd::error::RegisterError::AlreadyRegistered => Self::AlreadyRegistered,
77            _ => unreachable!("unknown kbd::error::RegisterError variant"),
78        }
79    }
80}
81
82/// Error returned when a layer operation fails.
83///
84/// Covers layer definition, push, pop, and toggle operations
85/// through the manager.
86#[derive(Debug, thiserror::Error)]
87#[non_exhaustive]
88pub enum LayerError {
89    /// A layer with the given name was already defined.
90    #[error("a layer with this name is already defined")]
91    AlreadyDefined,
92    /// No layer with the given name has been defined.
93    #[error("no layer with this name has been defined")]
94    NotDefined,
95    /// No active layers to pop from the stack.
96    #[error("no active layer to pop")]
97    EmptyStack,
98    /// The manager has been shut down or the engine thread has exited.
99    #[error(transparent)]
100    ManagerStopped(#[from] ManagerStopped),
101}
102
103impl From<kbd::error::LayerError> for LayerError {
104    fn from(error: kbd::error::LayerError) -> Self {
105        match error {
106            kbd::error::LayerError::AlreadyDefined => Self::AlreadyDefined,
107            kbd::error::LayerError::NotDefined => Self::NotDefined,
108            kbd::error::LayerError::EmptyStack => Self::EmptyStack,
109            _ => unreachable!("unknown kbd::error::LayerError variant"),
110        }
111    }
112}
113
114/// Error returned by query methods that accept parseable hotkey input.
115///
116/// Methods like [`is_registered()`](crate::manager::HotkeyManager::is_registered)
117/// and [`bindings_for_key()`](crate::manager::HotkeyManager::bindings_for_key)
118/// can fail from string parsing or a dead engine.
119#[derive(Debug, thiserror::Error)]
120#[non_exhaustive]
121pub enum QueryError {
122    /// A hotkey string could not be parsed.
123    #[error("parse error: {0}")]
124    Parse(#[from] kbd::error::ParseHotkeyError),
125    /// The manager has been shut down or the engine thread has exited.
126    #[error(transparent)]
127    ManagerStopped(#[from] ManagerStopped),
128}
129
130/// Error returned when shutting down the manager.
131#[derive(Debug, thiserror::Error)]
132#[non_exhaustive]
133pub enum ShutdownError {
134    /// An internal engine failure occurred (thread panic, mutex poison, etc.).
135    #[error("hotkey engine encountered an internal failure")]
136    Engine,
137    /// The manager has been shut down or the engine thread has exited.
138    #[error(transparent)]
139    ManagerStopped(#[from] ManagerStopped),
140}