core_api/node/contracts/
audiobridge.rs1use 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 AudioDeviceFeature {
25 Speaker,
26 Microphone,
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 features: Vec<AudioDeviceFeature>,
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 features: Vec<AudioDeviceFeature>,
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 features: vec![AudioDeviceFeature::Speaker, AudioDeviceFeature::Microphone],
86 online: true,
87 })
88 .expect("serialize device");
89 assert!(value.get("platformKey").is_none());
90 assert!(value.get("endpointId").is_none());
91 assert_eq!(
92 value["features"],
93 serde_json::json!(["speaker", "microphone"])
94 );
95 }
96
97 #[test]
98 fn device_snapshot_is_the_direct_response_payload() {
99 let value = serde_json::to_value(DeviceSnapshot {
100 microphone_permission: MicrophonePermission::NotDetermined,
101 devices: Vec::new(),
102 candidates: Vec::new(),
103 })
104 .expect("serialize device snapshot");
105 assert!(value.get("devices").is_some());
106 assert!(value.get("candidates").is_some());
107 assert!(value.get("snapshot").is_none());
108 }
109}