1use crate::common_utils::common::LazyProxy;
6use crate::weave::types::{PairingState, ResetConfig};
7use anyhow::Error;
8use fidl::endpoints::create_proxy;
9use fidl_fuchsia_weave::{
10 ErrorCode, FactoryDataManagerMarker, FactoryDataManagerProxy, PairingStateWatcherMarker,
11 PairingStateWatcherProxy, ResetConfigFlags, StackMarker, StackProxy,
12};
13use serde_json::Value;
14
15#[derive(Debug)]
19pub struct WeaveFacade {
20 factory_data_manager: LazyProxy<FactoryDataManagerMarker>,
21 stack: LazyProxy<StackMarker>,
22}
23
24impl WeaveFacade {
25 pub fn new() -> WeaveFacade {
26 WeaveFacade { factory_data_manager: Default::default(), stack: Default::default() }
27 }
28
29 fn factory_data_manager(&self) -> Result<FactoryDataManagerProxy, Error> {
32 self.factory_data_manager.get_or_connect()
33 }
34
35 fn stack(&self) -> Result<StackProxy, Error> {
37 self.stack.get_or_connect()
38 }
39
40 fn pairing_state_watcher(&self) -> Result<PairingStateWatcherProxy, Error> {
42 let (pairing_proxy, pairing_server_end) = create_proxy::<PairingStateWatcherMarker>();
43 self.stack()?.get_pairing_state_watcher(pairing_server_end)?;
44 Ok(pairing_proxy)
45 }
46
47 fn map_weave_err(&self, code: ErrorCode) -> anyhow::Error {
49 anyhow!(match code {
50 ErrorCode::FileNotFound => "FileNotFound",
51 ErrorCode::CryptoError => "CryptoError",
52 ErrorCode::InvalidArgument => "InvalidArgument",
53 ErrorCode::InvalidState => "InvalidState",
54 ErrorCode::UnspecifiedError => "UnspecifiedError",
55 })
56 }
57
58 pub async fn get_pairing_code(&self) -> Result<Vec<u8>, Error> {
60 self.factory_data_manager()?.get_pairing_code().await?.map_err(|e| self.map_weave_err(e))
61 }
62
63 pub async fn get_qr_code(&self) -> Result<String, Error> {
65 self.stack()?
66 .get_qr_code()
67 .await?
68 .map(|qr_code| qr_code.data)
69 .map_err(|e| self.map_weave_err(e))
70 }
71
72 pub async fn get_pairing_state(&self) -> Result<PairingState, Error> {
74 let watch = self.pairing_state_watcher()?.watch_pairing_state().await;
75 watch.map(|pairing_state| pairing_state.into()).map_err(anyhow::Error::from)
76 }
77
78 pub async fn reset_config(&self, args: Value) -> Result<(), Error> {
94 let flags: ResetConfig = serde_json::from_value(args)?;
95 self.stack()?
96 .reset_config(ResetConfigFlags::from(flags))
97 .await?
98 .map_err(|e| self.map_weave_err(e))
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use assert_matches::assert_matches;
106 use fidl::endpoints::create_proxy_and_stream;
107 use fidl_fuchsia_weave::{
108 FactoryDataManagerRequest, PairingStateWatcherRequest, QrCode, StackGetQrCodeResult,
109 StackRequest, StackResetConfigResult,
110 };
111 use fuchsia_async as fasync;
112 use futures::prelude::*;
113 use serde_json::json;
114 use std::sync::LazyLock;
115
116 const PAIRING_CODE: &'static [u8] = b"ABC1234";
117
118 static QR_CODE: LazyLock<QrCode> =
119 LazyLock::new(|| QrCode { data: String::from("qrcodedata") });
120 static PAIRING_STATE: LazyLock<PairingState> = LazyLock::new(|| PairingState {
121 is_wlan_provisioned: Some(true),
122 is_fabric_provisioned: Some(true),
123 is_service_provisioned: Some(false),
124 is_weave_fully_provisioned: Some(false),
125 is_thread_provisioned: Some(true),
126 });
127 static RESET_CONFIG: LazyLock<Value> = LazyLock::new(|| {
128 json!({
129 "network_config": true,
130 "service_config": true,
132 })
134 });
135
136 struct MockStackBuilder {
137 expected_stack: Vec<Box<dyn FnOnce(StackRequest) + Send + 'static>>,
138 expected_pair: Vec<Box<dyn FnOnce(PairingStateWatcherRequest) + Send + 'static>>,
139 }
140
141 impl MockStackBuilder {
142 fn new() -> Self {
143 Self { expected_stack: vec![], expected_pair: vec![] }
144 }
145
146 fn push_stack(mut self, request: impl FnOnce(StackRequest) + Send + 'static) -> Self {
147 self.expected_stack.push(Box::new(request));
148 self
149 }
150
151 fn push_pair(
152 mut self,
153 request: impl FnOnce(PairingStateWatcherRequest) + Send + 'static,
154 ) -> Self {
155 self.expected_pair.push(Box::new(request));
156 self
157 }
158
159 fn expect_get_qr_code(self, result: StackGetQrCodeResult) -> Self {
160 self.push_stack(move |req| match req {
161 StackRequest::GetQrCode { responder } => {
162 responder.send(result.as_ref().map_err(|e| *e)).unwrap()
163 }
164 req => panic!("unexpected request: {:?}", req),
165 })
166 }
167
168 fn expect_get_pairing_state(self, result: PairingState) -> Self {
169 self.push_pair(move |req| match req {
170 PairingStateWatcherRequest::WatchPairingState { responder } => {
171 responder.send(&fidl_fuchsia_weave::PairingState::from(result)).unwrap();
172 }
173 })
174 }
175
176 fn expect_reset_config(
177 self,
178 _expected_flags: fidl_fuchsia_weave::ResetConfigFlags,
179 result: StackResetConfigResult,
180 ) -> Self {
181 self.push_stack(move |req| match req {
182 StackRequest::ResetConfig { responder, flags } => {
183 assert_matches!(flags, _expected_flags);
184 responder.send(result).unwrap()
185 }
186 req => panic!("unexpected request: {:?}", req),
187 })
188 }
189
190 fn build_stack(self) -> (WeaveFacade, impl Future<Output = ()>) {
191 let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>();
192 let fut = async move {
193 let _ = &self;
194 for expected in self.expected_stack {
195 expected(stream.next().await.unwrap().unwrap());
196 }
197 assert_matches!(stream.next().await, None);
198 };
199 let facade = WeaveFacade::new();
200 facade.stack.set(proxy).expect("just-created facade should have empty stack");
201 (facade, fut)
202 }
203 fn build_stack_and_pairing_state_watcher(self) -> (WeaveFacade, impl Future<Output = ()>) {
204 let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>();
205 let stream_fut = async move {
206 let _ = &self;
207 match stream.next().await {
208 Some(Ok(StackRequest::GetPairingStateWatcher {
209 watcher,
210 control_handle: _,
211 })) => {
212 let mut into_stream = watcher.into_stream();
213 for expected in self.expected_pair {
214 expected(into_stream.next().await.unwrap().unwrap());
215 }
216 assert_matches!(into_stream.next().await, None);
217 }
218 err => panic!("Error in request handler: {:?}", err),
219 }
220 };
221 let facade = WeaveFacade::new();
222 facade.stack.set(proxy).expect("just-created facade should have empty stack");
223 (facade, stream_fut)
224 }
225 }
226
227 struct MockFactoryDataManagerBuilder {
228 expected: Vec<Box<dyn FnOnce(FactoryDataManagerRequest) + Send + 'static>>,
229 }
230
231 impl MockFactoryDataManagerBuilder {
232 fn new() -> Self {
233 Self { expected: vec![] }
234 }
235
236 fn push(
237 mut self,
238 request: impl FnOnce(FactoryDataManagerRequest) + Send + 'static,
239 ) -> Self {
240 self.expected.push(Box::new(request));
241 self
242 }
243
244 fn expect_get_pairing_code(self, result: Result<&'static [u8], ErrorCode>) -> Self {
245 self.push(move |req| match req {
246 FactoryDataManagerRequest::GetPairingCode { responder } => {
247 responder.send(result).unwrap()
248 }
249 _ => {}
250 })
251 }
252
253 fn build(self) -> (WeaveFacade, impl Future<Output = ()>) {
254 let (proxy, mut stream) =
255 fidl::endpoints::create_proxy_and_stream::<FactoryDataManagerMarker>();
256 let fut = async move {
257 for expected in self.expected {
258 expected(stream.next().await.unwrap().unwrap());
259 }
260 assert_matches!(stream.next().await, None);
261 };
262 let facade = WeaveFacade::new();
263 facade
264 .factory_data_manager
265 .set(proxy)
266 .expect("just-created facade should have empty fdm");
267 (facade, fut)
268 }
269 }
270
271 #[fasync::run_singlethreaded(test)]
272 async fn test_get_pairing_code() {
273 let (facade, pairing_code_fut) =
274 MockFactoryDataManagerBuilder::new().expect_get_pairing_code(Ok(PAIRING_CODE)).build();
275
276 let facade_fut = async move {
277 assert_eq!(facade.get_pairing_code().await.unwrap(), PAIRING_CODE);
278 };
279
280 future::join(facade_fut, pairing_code_fut).await;
281 }
282
283 #[fasync::run_singlethreaded(test)]
284 async fn test_get_qr_code() {
285 let qr_clone: StackGetQrCodeResult = Ok((*QR_CODE).clone());
286 let (facade, qr_code_fut) =
287 MockStackBuilder::new().expect_get_qr_code(qr_clone).build_stack();
288
289 let facade_fut = async move {
290 assert_eq!(facade.get_qr_code().await.unwrap(), *QR_CODE.data);
291 };
292
293 future::join(facade_fut, qr_code_fut).await;
294 }
295
296 #[fasync::run_singlethreaded(test)]
297 async fn test_get_pairing_state() {
298 let (facade, pairing_state_fut) = MockStackBuilder::new()
299 .expect_get_pairing_state(*PAIRING_STATE)
300 .build_stack_and_pairing_state_watcher();
301
302 let facade_fut = async move {
303 let pairing_state = facade.get_pairing_state().await.unwrap();
304 assert_eq!(pairing_state, *PAIRING_STATE);
305 };
306
307 future::join(facade_fut, pairing_state_fut).await;
308 }
309
310 #[allow(clippy::unit_cmp)] #[fasync::run_singlethreaded(test)]
312 async fn test_reset_config() {
313 let flags: ResetConfigFlags = fidl_fuchsia_weave::ResetConfigFlags::NETWORK_CONFIG
314 | fidl_fuchsia_weave::ResetConfigFlags::SERVICE_CONFIG;
315 let (facade, reset_config_fut) =
316 MockStackBuilder::new().expect_reset_config(flags, Ok(())).build_stack();
317
318 let facade_fut =
319 async move { assert_eq!(facade.reset_config(RESET_CONFIG.clone()).await.unwrap(), ()) };
320
321 future::join(facade_fut, reset_config_fut).await;
322 }
323}