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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
//! Host backend, device, direction, and open-plan model records.
use sim_kernel::{
CapabilityName, Expr, Symbol,
effect::{effect_device_read_kind, effect_device_write_kind},
};
use sim_lib_stream_core::{BufferPolicy, StreamDirection, StreamMedia, StreamMetadata};
use crate::capability::HostBackendCapability;
/// Returns the capability name gating host stream device access.
pub fn stream_host_capability() -> CapabilityName {
CapabilityName::new("stream.host")
}
/// Stable metadata describing a host backend.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HostBackendInfo {
id: Symbol,
transport: Symbol,
media: StreamMedia,
hardware_required: bool,
callbacks_bounded: bool,
capabilities: Vec<HostBackendCapability>,
}
/// Specification of an enumerated host device.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HostDeviceSpec {
id: Symbol,
backend: Symbol,
media: StreamMedia,
direction: HostDirection,
clock: Symbol,
buffer: BufferPolicy,
}
/// Direction of a host stream relative to the device.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HostDirection {
/// Device delivers data into the runtime (a source).
Input,
/// Device receives data from the runtime (a sink).
Output,
/// Device both delivers and receives data.
Duplex,
}
/// Resolved plan describing how a device would be opened.
///
/// Names the backend, device, the effect kind the open performs, and the
/// capabilities the open requires.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HostOpenPlan {
backend: Symbol,
device: Symbol,
effect_kind: Symbol,
requires: Vec<CapabilityName>,
}
impl HostBackendInfo {
/// Builds backend metadata with bounded callbacks and no capabilities.
pub fn new(id: Symbol, transport: Symbol, media: StreamMedia, hardware_required: bool) -> Self {
Self {
id,
transport,
media,
hardware_required,
callbacks_bounded: true,
capabilities: Vec::new(),
}
}
/// Replaces the advertised capabilities.
pub fn with_capabilities(mut self, capabilities: Vec<HostBackendCapability>) -> Self {
self.capabilities = capabilities;
self
}
/// Returns the backend identifier symbol.
pub fn id(&self) -> &Symbol {
&self.id
}
/// Returns the transport symbol.
pub fn transport(&self) -> &Symbol {
&self.transport
}
/// Returns the backend media.
pub fn media(&self) -> StreamMedia {
self.media
}
/// Returns whether opening a stream requires hardware.
pub fn hardware_required(&self) -> bool {
self.hardware_required
}
/// Returns whether host callbacks are bounded (non-blocking, fixed queue).
pub fn callbacks_bounded(&self) -> bool {
self.callbacks_bounded
}
/// Returns the advertised capabilities.
pub fn capabilities(&self) -> &[HostBackendCapability] {
&self.capabilities
}
/// Builds the browse card expression for this backend.
pub fn card_expr(&self) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("subject")),
Expr::Symbol(self.id.clone()),
),
(
Expr::Symbol(Symbol::new("kind")),
Expr::Symbol(Symbol::qualified("stream", "host-backend")),
),
(
Expr::Symbol(Symbol::new("transport")),
Expr::Symbol(self.transport.clone()),
),
(
Expr::Symbol(Symbol::new("media")),
Expr::Symbol(self.media.symbol()),
),
(
Expr::Symbol(Symbol::new("hardware-required")),
Expr::Bool(self.hardware_required),
),
(
Expr::Symbol(Symbol::new("callbacks-bounded")),
Expr::Bool(self.callbacks_bounded),
),
(
Expr::Symbol(Symbol::new("capabilities")),
Expr::List(
self.capabilities
.iter()
.map(|capability| Expr::Symbol(capability.symbol()))
.collect(),
),
),
])
}
}
impl HostDeviceSpec {
/// Builds a device spec from its identity, media, direction, clock, and
/// buffer policy.
pub fn new(
id: Symbol,
backend: Symbol,
media: StreamMedia,
direction: HostDirection,
clock: Symbol,
buffer: BufferPolicy,
) -> Self {
Self {
id,
backend,
media,
direction,
clock,
buffer,
}
}
/// Returns the device identifier symbol.
pub fn id(&self) -> &Symbol {
&self.id
}
/// Returns the owning backend symbol.
pub fn backend(&self) -> &Symbol {
&self.backend
}
/// Returns the device media.
pub fn media(&self) -> StreamMedia {
self.media
}
/// Returns the device direction.
pub fn direction(&self) -> HostDirection {
self.direction
}
/// Returns the device clock-domain symbol.
pub fn clock(&self) -> &Symbol {
&self.clock
}
/// Returns the device buffer policy.
pub fn buffer(&self) -> &BufferPolicy {
&self.buffer
}
/// Builds the [`StreamMetadata`] for a stream opened on this device.
pub fn metadata(&self) -> StreamMetadata {
StreamMetadata::new(
self.id.clone(),
self.media,
self.direction.stream_direction(),
self.clock.clone(),
self.buffer.clone(),
)
}
/// Builds the [`HostOpenPlan`] for opening this device.
pub fn open_plan(&self) -> HostOpenPlan {
HostOpenPlan {
backend: self.backend.clone(),
device: self.id.clone(),
effect_kind: self.direction.effect_kind(),
requires: vec![stream_host_capability()],
}
}
/// Builds the browse card expression for this device.
pub fn card_expr(&self) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("subject")),
Expr::Symbol(self.id.clone()),
),
(
Expr::Symbol(Symbol::new("kind")),
Expr::Symbol(Symbol::qualified("stream", "host-device")),
),
(
Expr::Symbol(Symbol::new("backend")),
Expr::Symbol(self.backend.clone()),
),
(
Expr::Symbol(Symbol::new("media")),
Expr::Symbol(self.media.symbol()),
),
(
Expr::Symbol(Symbol::new("direction")),
Expr::Symbol(self.direction.symbol()),
),
(
Expr::Symbol(Symbol::new("clock")),
Expr::Symbol(self.clock.clone()),
),
(Expr::Symbol(Symbol::new("buffer")), self.buffer.to_expr()),
])
}
}
impl HostDirection {
/// Maps this host direction to the core [`StreamDirection`].
pub fn stream_direction(self) -> StreamDirection {
match self {
Self::Input => StreamDirection::Source,
Self::Output => StreamDirection::Sink,
Self::Duplex => StreamDirection::Duplex,
}
}
/// Returns the stable qualified symbol naming this direction.
pub fn symbol(self) -> Symbol {
match self {
Self::Input => Symbol::qualified("stream/host-direction", "input"),
Self::Output => Symbol::qualified("stream/host-direction", "output"),
Self::Duplex => Symbol::qualified("stream/host-direction", "duplex"),
}
}
/// Returns the effect kind an open in this direction performs (device read
/// for input, device write for output or duplex).
pub fn effect_kind(self) -> Symbol {
match self {
Self::Input => effect_device_read_kind(),
Self::Output | Self::Duplex => effect_device_write_kind(),
}
}
}
impl HostOpenPlan {
/// Returns the backend that would perform the open.
pub fn backend(&self) -> &Symbol {
&self.backend
}
/// Returns the device that would be opened.
pub fn device(&self) -> &Symbol {
&self.device
}
/// Returns the effect kind the open performs.
pub fn effect_kind(&self) -> &Symbol {
&self.effect_kind
}
/// Returns the capabilities the open requires.
pub fn requires(&self) -> &[CapabilityName] {
&self.requires
}
}