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
//! Host backend capability metadata and browse card helpers.
use sim_kernel::{CapabilityName, Expr, Symbol};
/// Capability helpers for device-local sensors and actuators.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DeviceCapability {
/// Pose or spatial tracking samples.
Pose,
/// Camera frames or still captures.
Camera,
/// Health or biometric samples.
Health,
/// Location samples.
Location,
/// Microphone input samples.
Mic,
/// Vendor diagnostic reports.
VendorReport,
}
impl DeviceCapability {
/// All baseline device capabilities.
pub const ALL: [Self; 6] = [
Self::Pose,
Self::Camera,
Self::Health,
Self::Location,
Self::Mic,
Self::VendorReport,
];
/// Stable capability name, suitable for `Cx::require`.
pub fn as_str(self) -> &'static str {
match self {
Self::Pose => "device/pose",
Self::Camera => "device/camera",
Self::Health => "device/health",
Self::Location => "device/location",
Self::Mic => "device/mic",
Self::VendorReport => "device/vendor-report",
}
}
/// Returns the kernel capability name.
pub fn capability_name(self) -> CapabilityName {
CapabilityName::new(self.as_str())
}
/// Returns the visible grant symbol used by consent receipts.
pub fn grant_symbol(self) -> Symbol {
Symbol::qualified("device", self.local_name())
}
/// Resolves a baseline helper from a capability name.
pub fn from_name(name: &str) -> Option<Self> {
Self::ALL
.into_iter()
.find(|capability| capability.as_str() == name)
}
fn local_name(self) -> &'static str {
self.as_str()
.strip_prefix("device/")
.expect("device capability names keep the device/ prefix")
}
}
/// Capability advertised by a host backend.
///
/// Each variant names one host-integration feature a backend may support, such
/// as a media direction, hotplug/reconnect handling, or the deterministic fake
/// transport used during validation.
///
/// # Examples
///
/// ```
/// use sim_lib_stream_host::HostBackendCapability;
///
/// let symbol = HostBackendCapability::Duplex.symbol();
/// assert_eq!(symbol.as_qualified_str(), "stream/host-capability/duplex");
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HostBackendCapability {
/// Backend can capture audio from a host input device.
AudioInput,
/// Backend can render audio to a host output device.
AudioOutput,
/// Backend can receive MIDI from a host input port.
MidiInput,
/// Backend can send MIDI to a host output port.
MidiOutput,
/// Backend can drive a single full-duplex (input and output) device.
Duplex,
/// Backend reports device arrival and removal at runtime.
Hotplug,
/// Backend can re-establish a dropped device or peer connection.
Reconnect,
/// Backend can enumerate and plan without opening hardware streams.
Offline,
/// Backend is a deterministic fake used for validation rather than hardware.
Fake,
}
impl HostBackendCapability {
/// Returns the stable qualified symbol naming this capability.
pub fn symbol(self) -> Symbol {
match self {
Self::AudioInput => Symbol::qualified("stream/host-capability", "audio-input"),
Self::AudioOutput => Symbol::qualified("stream/host-capability", "audio-output"),
Self::MidiInput => Symbol::qualified("stream/host-capability", "midi-input"),
Self::MidiOutput => Symbol::qualified("stream/host-capability", "midi-output"),
Self::Duplex => Symbol::qualified("stream/host-capability", "duplex"),
Self::Hotplug => Symbol::qualified("stream/host-capability", "hotplug"),
Self::Reconnect => Symbol::qualified("stream/host-capability", "reconnect"),
Self::Offline => Symbol::qualified("stream/host-capability", "offline"),
Self::Fake => Symbol::qualified("stream/host-capability", "fake"),
}
}
}
/// Emits a browse card for a missing backend capability.
pub fn missing_capability_card_expr(backend: &Symbol, capability: HostBackendCapability) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("subject")),
Expr::Symbol(backend.clone()),
),
(
Expr::Symbol(Symbol::new("kind")),
Expr::Symbol(Symbol::qualified("stream", "host-missing-capability")),
),
(
Expr::Symbol(Symbol::new("capability")),
Expr::Symbol(capability.symbol()),
),
])
}