Skip to main content

openlogi_core/hid/
error.rs

1//! HID++ read/write error vocabulary — pure data, no I/O.
2//!
3//! The conversions from `hidpp`/`async_hid` error types into [`WriteError`]
4//! live in `openlogi_hid::write::error`, which this crate must never depend
5//! on.
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10/// Error returned by HID++ read/write operations.
11///
12/// Serializable + Clone so it can cross the agent↔GUI IPC unchanged: the GUI
13/// classifies a device read/write error as permanent (FeatureUnsupported /
14/// EmptyDpiList) vs transient, so the discriminating variant must survive the
15/// wire — stringifying it would collapse every case to "transient" and a device
16/// that genuinely lacks a feature would be re-probed forever. Variant order is
17/// therefore wire format: changes require a `PROTOCOL_VERSION` bump (guarded
18/// by `openlogi-ipc/tests/wire_format.rs`).
19#[derive(Debug, Clone, Error, Serialize, Deserialize)]
20pub enum WriteError {
21    /// HID transport error serialized as text.
22    ///
23    /// `async_hid::HidError` isn't `Serialize`, so carry its message as text;
24    /// the typed error is never matched on (only constructed + displayed).
25    #[error("HID transport error: {0}")]
26    Hid(String),
27    /// No currently connected device matched the requested route.
28    #[error("no connected device matched the route")]
29    DeviceNotFound,
30    /// The HID node opened, but the target HID++ device index did not answer.
31    #[error("device at index {index:#04x} did not respond to HID++")]
32    DeviceUnreachable {
33        /// HID++ device index that failed to answer.
34        index: u8,
35    },
36    /// Device does not expose the requested HID++ feature.
37    #[error("device does not expose HID++ feature {feature_hex:#06x}")]
38    FeatureUnsupported {
39        /// HID++ feature ID that was not present.
40        feature_hex: u16,
41    },
42    /// Device reported no valid DPI values.
43    #[error("device returned no supported DPI values")]
44    EmptyDpiList,
45    /// Generic HID++ protocol error serialized as text.
46    #[error("HID++ protocol error: {0}")]
47    Hidpp(String),
48    /// HID++ feature error response.
49    #[error("HID++ feature error during {operation:?} for feature {feature_hex:#06x}: {kind:?}")]
50    HidppFeature {
51        /// Operation being performed.
52        operation: HidppOperation,
53        /// HID++ feature ID involved in the operation.
54        feature_hex: u16,
55        /// HID++ feature error kind.
56        kind: HidppFeatureErrorKind,
57    },
58    /// Device returned a structurally unsupported response.
59    #[error("HID++ unsupported response during {operation:?} for feature {feature_hex:#06x}")]
60    UnsupportedResponse {
61        /// Operation being performed.
62        operation: HidppOperation,
63        /// HID++ feature ID involved in the operation.
64        feature_hex: u16,
65    },
66    /// HID++ request timed out.
67    #[error("HID++ request timed out during {operation:?}")]
68    RequestTimedOut {
69        /// Operation that timed out.
70        operation: HidppOperation,
71    },
72    /// Tokio runtime could not be initialized for a sync caller.
73    #[error("tokio runtime init failed: {message}")]
74    RuntimeInit {
75        /// Runtime initialization error message.
76        message: String,
77    },
78    /// Background agent write path is unavailable.
79    #[error("background agent is unavailable")]
80    AgentUnavailable,
81    /// A standalone light value is outside the driver's supported range.
82    #[error("invalid light value for {control}: {value}")]
83    InvalidLightValue {
84        /// Semantic control name.
85        control: String,
86        /// Rejected value in the semantic/native unit supplied by the caller.
87        value: u16,
88    },
89    /// The selected light driver does not implement a requested control.
90    #[error("light control is unsupported: {control}")]
91    LightUnsupported {
92        /// Semantic control name.
93        control: String,
94    },
95    /// Multiple raw HID nodes matched one physical route.
96    #[error("multiple raw HID devices matched the route")]
97    AmbiguousRawDevice,
98}
99
100/// HID++ operation being performed when a device write/read failed.
101///
102/// Variant order is wire format because this travels inside [`WriteError`].
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
104pub enum HidppOperation {
105    /// Resolve a feature ID to its runtime feature index.
106    ResolveFeature,
107    /// Enumerate the device feature table.
108    DumpFeatures,
109    /// Read current DPI.
110    ReadDpi,
111    /// Read DPI capabilities.
112    ReadDpiCapabilities,
113    /// Write DPI.
114    WriteDpi,
115    /// Read SmartShift status.
116    ReadSmartShift,
117    /// Write SmartShift status.
118    WriteSmartShift,
119    /// Write keyboard lighting.
120    Lighting,
121    /// Read HiResWheel capabilities or the current wheel mode.
122    ReadWheelMode,
123    /// Write and verify the native HiResWheel mode.
124    WriteWheelMode,
125    /// Read the keyboard backlight config or info.
126    ReadBacklight,
127    /// Write the keyboard backlight config.
128    WriteBacklight,
129    /// Write keyboard Fn-lock (fn inversion).
130    WriteFnLock,
131    /// Write a standalone-light command. Appended last — variant order is wire format.
132    Light,
133    /// Play one haptic waveform. Appended last — variant order is wire format.
134    PlayHaptic,
135}
136
137/// HID++ feature error kind in a serializable wire-safe form.
138///
139/// Variant order is wire format because this travels inside [`WriteError`].
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
141pub enum HidppFeatureErrorKind {
142    /// HID++ `NoError` code.
143    NoError,
144    /// Unknown HID++ error.
145    Unknown,
146    /// Invalid argument.
147    InvalidArgument,
148    /// Argument out of range.
149    OutOfRange,
150    /// Hardware error.
151    HwError,
152    /// Logitech-internal firmware error.
153    LogitechInternal,
154    /// Invalid feature index.
155    InvalidFeatureIndex,
156    /// Invalid function ID.
157    InvalidFunctionId,
158    /// Device is busy.
159    Busy,
160    /// Operation is unsupported.
161    Unsupported,
162    /// Error code not modeled by OpenLogi.
163    Unrecognized,
164}