1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! The contract between OpenLogi's HID++ layer and the HID stack beneath it.
//!
//! [`HidBackend`] is the seam. Above it sits everything that knows HID++ and
//! nothing about a host; below it sits one implementation per host HID API —
//! `openlogi-hid` over `async-hid` today, a scripted device tree in tests, and
//! WebHID under wasm if that is ever built.
//!
//! Its own dependencies stay host-free for the same reason, which CI's
//! `wasm (portable crates)` job checks rather than trusts. The conversions
//! *from* a backend's own types belong with that backend, never here.
use fmt;
use Arc;
use Stream;
use async_trait;
use HidppChannel;
use Error;
/// A failure raised by the HID backend beneath the HID++ channel layer.
///
/// Deliberately narrow. The only distinction anything above the transport
/// branches on is "the device is gone" versus everything else, so a backend
/// collapses its own error taxonomy into these two variants and every caller
/// stays backend-agnostic.
/// A HID node appeared on or vanished from the OS device tree.
///
/// Deliberately carries no identity: every consumer reacts by re-enumerating,
/// and a backend that can only report "something changed" must still be able
/// to raise it.
/// Opaque identity of one HID node, as the backend that enumerated it names it.
///
/// Distinct per OS device node while that node exists, so it keys the open
/// channels and the per-node ledger. It is **not** a portable physical key —
/// a hidraw path on Linux, a device path on Windows, an IOKit registry entry
/// on macOS — and must never be persisted. Physical identity comes from the
/// device's own serial or HID++ model info instead.
;
/// One HID node as the backend reports it, before anything is opened.
///
/// These are the fields enumeration filters on and routes address by — the
/// intersection every HID backend can supply, which is also all the layers
/// above the transport ever read.
/// A stream of [`HotplugEvent`]s, boxed so [`HidBackend`] stays object-safe.
pub type HotplugStream = ;
/// A raw output-report sink, for reports the HID++ framing cannot model.
///
/// The HID++ channel covers reports `0x10`/`0x11`/`0x12` with request/response
/// correlation. A few devices need a bare output report written with no reply
/// expected — Logitech's Litra lights, driven over their own vendor protocol —
/// and that is all this is for.
/// The HID stack beneath OpenLogi's HID++ layer.
///
/// One implementation per host HID API. Everything above it — enumeration
/// policy, the probe, the write layer, capture sessions — is expressed against
/// this trait and holds none of the backend's own types, which is what lets a
/// second implementation (a scripted device tree in tests, WebHID under wasm)
/// drop in without touching that code.
///
/// Opening is only defined for a node a previous [`Self::enumerate`] reported:
/// a backend may hold OS handles from that enumeration rather than re-finding
/// the node, so an unknown [`NodeInfo`] is [`BackendError::Disconnected`].
/// Carries a backend failure across the IPC boundary as text.
///
/// [`WriteError`](openlogi_core::hid::WriteError) is `Serialize` and
/// [`BackendError`] is not, so the message is the payload; the typed error is
/// never matched on downstream.
///
/// The impl lives here rather than beside `WriteError` because [`BackendError`]
/// is the local half — the orphan rule allows exactly one of the two homes, and
/// `openlogi-core` must never depend on a backend.
/// Carries a backend failure across the IPC boundary as text, as
/// [`From<BackendError> for WriteError`](BackendError) does for writes.