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}