arora_websocket/
bridge.rs1use std::collections::HashMap;
16use std::sync::{Arc, Mutex};
17
18use arora_bridge::{
19 Bridge, BridgeCommand, BridgeOp, BridgeResult, CommandStream, DataRequestedStream, DeviceInfo,
20 DeviceInfoStream,
21};
22use arora_types::data::{Key, StateChange};
23use arora_types::value::Value;
24use async_trait::async_trait;
25use futures::channel::{mpsc, oneshot};
26
27use crate::messages::Outgoing;
28use crate::server::AroraWSServer;
29
30pub struct WsBridge {
37 server: Arc<AroraWSServer>,
38 commands: Mutex<Option<mpsc::UnboundedReceiver<BridgeCommand>>>,
40}
41
42impl WsBridge {
43 pub async fn new(server: Arc<AroraWSServer>) -> Self {
46 let (cmd_tx, cmd_rx) = mpsc::unbounded::<BridgeCommand>();
47
48 let tx = cmd_tx.clone();
51 server
52 .set_write_values_handler(move |values: HashMap<String, Value>| {
53 let mut change = StateChange::new();
54 for (path, value) in values {
55 change.set.insert(Key::from(path), Some(value));
56 }
57 let (reply_tx, _reply_rx) = oneshot::channel();
58 tx.unbounded_send(BridgeCommand::new(BridgeOp::Update(change), reply_tx))
59 .map_err(|_| "bridge command channel closed".to_string())
60 })
61 .await;
62
63 let tx = cmd_tx.clone();
65 server
66 .set_read_values_handler(Arc::new(move |keys: Vec<String>| {
67 let tx = tx.clone();
68 Box::pin(async move {
69 let store_keys: Vec<Key> = keys.iter().cloned().map(Key::from).collect();
70 let (reply_tx, reply_rx) = oneshot::channel();
71 if tx
72 .unbounded_send(BridgeCommand::new(BridgeOp::Get(store_keys), reply_tx))
73 .is_err()
74 {
75 return HashMap::new();
76 }
77 match reply_rx.await {
78 Ok(Ok(result)) => values_from_get(&keys, result.ret),
79 _ => HashMap::new(),
80 }
81 }) as _
82 }))
83 .await;
84
85 Self {
86 server,
87 commands: Mutex::new(Some(cmd_rx)),
88 }
89 }
90}
91
92fn values_from_get(keys: &[String], ret: Value) -> HashMap<String, Value> {
95 let mut out = HashMap::new();
96 if let Value::ArrayValue(items) = ret {
97 for (key, item) in keys.iter().zip(items) {
98 if let Value::Option(Some(value)) = item {
99 out.insert(key.clone(), *value);
100 }
101 }
102 }
103 out
104}
105
106#[async_trait]
107impl Bridge for WsBridge {
108 async fn get_device_info(&self) -> BridgeResult<Option<DeviceInfo>> {
109 Ok(None)
111 }
112
113 async fn device_info_updated(&self) -> BridgeResult<DeviceInfoStream> {
114 Ok(Box::pin(futures::stream::empty()))
115 }
116
117 async fn update_device_info(
118 &self,
119 info: Option<DeviceInfo>,
120 ) -> BridgeResult<Option<DeviceInfo>> {
121 Ok(info)
122 }
123
124 async fn data_requested(&self) -> DataRequestedStream {
125 Box::pin(futures::stream::once(async { true }))
127 }
128
129 async fn send_data(&self, data: StateChange) -> BridgeResult<()> {
130 let mut values = HashMap::new();
132 for (key, value) in data.set {
133 if let Some(value) = value {
134 values.insert(key.path, value);
135 }
136 }
137 if !values.is_empty() {
138 self.server.push(Outgoing::ValuesChanged { values });
139 }
140 Ok(())
141 }
142
143 async fn commands(&self) -> CommandStream {
148 match self.commands.lock().unwrap().take() {
149 Some(rx) => Box::pin(rx),
150 None => {
151 log::warn!(
152 "WsBridge::commands() called more than once; the command \
153 stream was already taken, returning an empty stream"
154 );
155 Box::pin(futures::stream::empty())
156 }
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::server::ServerConfig;
165
166 #[tokio::test]
167 async fn send_data_pushes_values_changed() {
168 let server = Arc::new(AroraWSServer::new(ServerConfig::default()));
169 let bridge = WsBridge::new(server.clone()).await;
170 let mut rx = server.subscribe();
171
172 bridge
173 .send_data(StateChange::set("face/mouth", Value::F64(0.5)))
174 .await
175 .expect("send_data");
176
177 match rx.recv().await.expect("a push") {
178 Outgoing::ValuesChanged { values } => {
179 assert_eq!(values.get("face/mouth"), Some(&Value::F64(0.5)));
180 }
181 other => panic!("expected ValuesChanged, got {other:?}"),
182 }
183 }
184}