Skip to main content

sim_lib_stream_host/
capability.rs

1//! Host backend capability metadata and browse card helpers.
2
3use sim_kernel::{CapabilityName, Expr, Symbol};
4
5/// Capability helpers for device-local sensors and actuators.
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum DeviceCapability {
8    /// Pose or spatial tracking samples.
9    Pose,
10    /// Camera frames or still captures.
11    Camera,
12    /// Health or biometric samples.
13    Health,
14    /// Location samples.
15    Location,
16    /// Microphone input samples.
17    Mic,
18    /// Vendor diagnostic reports.
19    VendorReport,
20}
21
22impl DeviceCapability {
23    /// All baseline device capabilities.
24    pub const ALL: [Self; 6] = [
25        Self::Pose,
26        Self::Camera,
27        Self::Health,
28        Self::Location,
29        Self::Mic,
30        Self::VendorReport,
31    ];
32
33    /// Stable capability name, suitable for `Cx::require`.
34    pub fn as_str(self) -> &'static str {
35        match self {
36            Self::Pose => "device/pose",
37            Self::Camera => "device/camera",
38            Self::Health => "device/health",
39            Self::Location => "device/location",
40            Self::Mic => "device/mic",
41            Self::VendorReport => "device/vendor-report",
42        }
43    }
44
45    /// Returns the kernel capability name.
46    pub fn capability_name(self) -> CapabilityName {
47        CapabilityName::new(self.as_str())
48    }
49
50    /// Returns the visible grant symbol used by consent receipts.
51    pub fn grant_symbol(self) -> Symbol {
52        Symbol::qualified("device", self.local_name())
53    }
54
55    /// Resolves a baseline helper from a capability name.
56    pub fn from_name(name: &str) -> Option<Self> {
57        Self::ALL
58            .into_iter()
59            .find(|capability| capability.as_str() == name)
60    }
61
62    fn local_name(self) -> &'static str {
63        self.as_str()
64            .strip_prefix("device/")
65            .expect("device capability names keep the device/ prefix")
66    }
67}
68
69/// Capability advertised by a host backend.
70///
71/// Each variant names one host-integration feature a backend may support, such
72/// as a media direction, hotplug/reconnect handling, or the deterministic fake
73/// transport used during validation.
74///
75/// # Examples
76///
77/// ```
78/// use sim_lib_stream_host::HostBackendCapability;
79///
80/// let symbol = HostBackendCapability::Duplex.symbol();
81/// assert_eq!(symbol.as_qualified_str(), "stream/host-capability/duplex");
82/// ```
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum HostBackendCapability {
85    /// Backend can capture audio from a host input device.
86    AudioInput,
87    /// Backend can render audio to a host output device.
88    AudioOutput,
89    /// Backend can receive MIDI from a host input port.
90    MidiInput,
91    /// Backend can send MIDI to a host output port.
92    MidiOutput,
93    /// Backend can drive a single full-duplex (input and output) device.
94    Duplex,
95    /// Backend reports device arrival and removal at runtime.
96    Hotplug,
97    /// Backend can re-establish a dropped device or peer connection.
98    Reconnect,
99    /// Backend can enumerate and plan without opening hardware streams.
100    Offline,
101    /// Backend is a deterministic fake used for validation rather than hardware.
102    Fake,
103}
104
105impl HostBackendCapability {
106    /// Returns the stable qualified symbol naming this capability.
107    pub fn symbol(self) -> Symbol {
108        match self {
109            Self::AudioInput => Symbol::qualified("stream/host-capability", "audio-input"),
110            Self::AudioOutput => Symbol::qualified("stream/host-capability", "audio-output"),
111            Self::MidiInput => Symbol::qualified("stream/host-capability", "midi-input"),
112            Self::MidiOutput => Symbol::qualified("stream/host-capability", "midi-output"),
113            Self::Duplex => Symbol::qualified("stream/host-capability", "duplex"),
114            Self::Hotplug => Symbol::qualified("stream/host-capability", "hotplug"),
115            Self::Reconnect => Symbol::qualified("stream/host-capability", "reconnect"),
116            Self::Offline => Symbol::qualified("stream/host-capability", "offline"),
117            Self::Fake => Symbol::qualified("stream/host-capability", "fake"),
118        }
119    }
120}
121
122/// Emits a browse card for a missing backend capability.
123pub fn missing_capability_card_expr(backend: &Symbol, capability: HostBackendCapability) -> Expr {
124    Expr::Map(vec![
125        (
126            Expr::Symbol(Symbol::new("subject")),
127            Expr::Symbol(backend.clone()),
128        ),
129        (
130            Expr::Symbol(Symbol::new("kind")),
131            Expr::Symbol(Symbol::qualified("stream", "host-missing-capability")),
132        ),
133        (
134            Expr::Symbol(Symbol::new("capability")),
135            Expr::Symbol(capability.symbol()),
136        ),
137    ])
138}