Skip to main content

core_api/node/contracts/
audiobridge.rs

1use serde::{Deserialize, Serialize};
2
3pub const VERSION: &str = crate::node::V1;
4pub const NODE_TYPE: &str = "audiobridge";
5pub const RUNTIME_TARGET_PREFIX: &str = "/audiobridge/app/";
6pub const DEVICE_SNAPSHOT: &str = "device/snapshot";
7pub const DEVICE_REFRESH: &str = "device/refresh";
8pub const DEVICE_PAIR: &str = "device/pair";
9pub const DEVICE_UNPAIR: &str = "device/unpair";
10pub const DEVICE_TEST: &str = "device/test";
11
12#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13#[serde(rename_all = "camelCase", deny_unknown_fields)]
14pub struct EmptyRequest {}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase", deny_unknown_fields)]
18pub struct DeviceRequest {
19    pub device_id: String,
20}
21
22#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
23#[serde(rename_all = "lowercase")]
24pub enum AudioEndpointDirection {
25    Input,
26    Output,
27}
28
29#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
30#[serde(rename_all = "camelCase")]
31pub enum MicrophonePermission {
32    NotDetermined,
33    Granted,
34    Denied,
35    Restricted,
36    Unavailable,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
40#[serde(rename_all = "camelCase")]
41pub struct AudioBridgeDevice {
42    pub device_id: String,
43    pub display_name: String,
44    pub transport: String,
45    pub direction: AudioEndpointDirection,
46    pub online: bool,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
50#[serde(rename_all = "camelCase")]
51pub struct AudioBridgeCandidate {
52    pub device_id: String,
53    pub display_name: String,
54    pub transport: String,
55    pub direction: AudioEndpointDirection,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct DeviceSnapshot {
61    pub microphone_permission: MicrophonePermission,
62    #[serde(default)]
63    pub devices: Vec<AudioBridgeDevice>,
64    #[serde(default)]
65    pub candidates: Vec<AudioBridgeCandidate>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(rename_all = "camelCase")]
70pub struct PairingResponse {
71    pub changed: bool,
72    pub device: AudioBridgeDevice,
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn public_contract_does_not_expose_platform_identity() {
81        let value = serde_json::to_value(AudioBridgeDevice {
82            device_id: "device-1".to_string(),
83            display_name: "Living Room Speaker".to_string(),
84            transport: "bluetooth".to_string(),
85            direction: AudioEndpointDirection::Output,
86            online: true,
87        })
88        .expect("serialize device");
89        assert!(value.get("platformKey").is_none());
90        assert!(value.get("endpointId").is_none());
91    }
92
93    #[test]
94    fn device_snapshot_is_the_direct_response_payload() {
95        let value = serde_json::to_value(DeviceSnapshot {
96            microphone_permission: MicrophonePermission::NotDetermined,
97            devices: Vec::new(),
98            candidates: Vec::new(),
99        })
100        .expect("serialize device snapshot");
101        assert!(value.get("devices").is_some());
102        assert!(value.get("candidates").is_some());
103        assert!(value.get("snapshot").is_none());
104    }
105}