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 lazy_static::lazy_static;
114 use serde_json::json;
115
116 const PAIRING_CODE: &'static [u8] = b"ABC1234";
117
118 lazy_static! {
119 static ref QR_CODE: QrCode = QrCode { data: String::from("qrcodedata") };
120 static ref PAIRING_STATE: PairingState = 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 ref RESET_CONFIG: Value = json!({
128 "network_config": true,
129 "service_config": true,
131 });
133 }
134
135 struct MockStackBuilder {
136 expected_stack: Vec<Box<dyn FnOnce(StackRequest) + Send + 'static>>,
137 expected_pair: Vec<Box<dyn FnOnce(PairingStateWatcherRequest) + Send + 'static>>,
138 }
139
140 impl MockStackBuilder {
141 fn new() -> Self {
142 Self { expected_stack: vec![], expected_pair: vec![] }
143 }
144
145 fn push_stack(mut self, request: impl FnOnce(StackRequest) + Send + 'static) -> Self {
146 self.expected_stack.push(Box::new(request));
147 self
148 }
149
150 fn push_pair(
151 mut self,
152 request: impl FnOnce(PairingStateWatcherRequest) + Send + 'static,
153 ) -> Self {
154 self.expected_pair.push(Box::new(request));
155 self
156 }
157
158 fn expect_get_qr_code(self, result: StackGetQrCodeResult) -> Self {
159 self.push_stack(move |req| match req {
160 StackRequest::GetQrCode { responder } => {
161 responder.send(result.as_ref().map_err(|e| *e)).unwrap()
162 }
163 req => panic!("unexpected request: {:?}", req),
164 })
165 }
166
167 fn expect_get_pairing_state(self, result: PairingState) -> Self {
168 self.push_pair(move |req| match req {
169 PairingStateWatcherRequest::WatchPairingState { responder } => {
170 responder.send(&fidl_fuchsia_weave::PairingState::from(result)).unwrap();
171 }
172 })
173 }
174
175 fn expect_reset_config(
176 self,
177 _expected_flags: fidl_fuchsia_weave::ResetConfigFlags,
178 result: StackResetConfigResult,
179 ) -> Self {
180 self.push_stack(move |req| match req {
181 StackRequest::ResetConfig { responder, flags } => {
182 assert_matches!(flags, _expected_flags);
183 responder.send(result).unwrap()
184 }
185 req => panic!("unexpected request: {:?}", req),
186 })
187 }
188
189 fn build_stack(self) -> (WeaveFacade, impl Future<Output = ()>) {
190 let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>();
191 let fut = async move {
192 let _ = &self;
193 for expected in self.expected_stack {
194 expected(stream.next().await.unwrap().unwrap());
195 }
196 assert_matches!(stream.next().await, None);
197 };
198 let facade = WeaveFacade::new();
199 facade.stack.set(proxy).expect("just-created facade should have empty stack");
200 (facade, fut)
201 }
202 fn build_stack_and_pairing_state_watcher(self) -> (WeaveFacade, impl Future<Output = ()>) {
203 let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>();
204 let stream_fut = async move {
205 let _ = &self;
206 match stream.next().await {
207 Some(Ok(StackRequest::GetPairingStateWatcher {
208 watcher,
209 control_handle: _,
210 })) => {
211 let mut into_stream = watcher.into_stream();
212 for expected in self.expected_pair {
213 expected(into_stream.next().await.unwrap().unwrap());
214 }
215 assert_matches!(into_stream.next().await, None);
216 }
217 err => panic!("Error in request handler: {:?}", err),
218 }
219 };
220 let facade = WeaveFacade::new();
221 facade.stack.set(proxy).expect("just-created facade should have empty stack");
222 (facade, stream_fut)
223 }
224 }
225
226 struct MockFactoryDataManagerBuilder {
227 expected: Vec<Box<dyn FnOnce(FactoryDataManagerRequest) + Send + 'static>>,
228 }
229
230 impl MockFactoryDataManagerBuilder {
231 fn new() -> Self {
232 Self { expected: vec![] }
233 }
234
235 fn push(
236 mut self,
237 request: impl FnOnce(FactoryDataManagerRequest) + Send + 'static,
238 ) -> Self {
239 self.expected.push(Box::new(request));
240 self
241 }
242
243 fn expect_get_pairing_code(self, result: Result<&'static [u8], ErrorCode>) -> Self {
244 self.push(move |req| match req {
245 FactoryDataManagerRequest::GetPairingCode { responder } => {
246 responder.send(result).unwrap()
247 }
248 _ => {}
249 })
250 }
251
252 fn build(self) -> (WeaveFacade, impl Future<Output = ()>) {
253 let (proxy, mut stream) =
254 fidl::endpoints::create_proxy_and_stream::<FactoryDataManagerMarker>();
255 let fut = async move {
256 for expected in self.expected {
257 expected(stream.next().await.unwrap().unwrap());
258 }
259 assert_matches!(stream.next().await, None);
260 };
261 let facade = WeaveFacade::new();
262 facade
263 .factory_data_manager
264 .set(proxy)
265 .expect("just-created facade should have empty fdm");
266 (facade, fut)
267 }
268 }
269
270 #[fasync::run_singlethreaded(test)]
271 async fn test_get_pairing_code() {
272 let (facade, pairing_code_fut) =
273 MockFactoryDataManagerBuilder::new().expect_get_pairing_code(Ok(PAIRING_CODE)).build();
274
275 let facade_fut = async move {
276 assert_eq!(facade.get_pairing_code().await.unwrap(), PAIRING_CODE);
277 };
278
279 future::join(facade_fut, pairing_code_fut).await;
280 }
281
282 #[fasync::run_singlethreaded(test)]
283 async fn test_get_qr_code() {
284 let qr_clone: StackGetQrCodeResult = Ok((*QR_CODE).clone());
285 let (facade, qr_code_fut) =
286 MockStackBuilder::new().expect_get_qr_code(qr_clone).build_stack();
287
288 let facade_fut = async move {
289 assert_eq!(facade.get_qr_code().await.unwrap(), *QR_CODE.data);
290 };
291
292 future::join(facade_fut, qr_code_fut).await;
293 }
294
295 #[fasync::run_singlethreaded(test)]
296 async fn test_get_pairing_state() {
297 let (facade, pairing_state_fut) = MockStackBuilder::new()
298 .expect_get_pairing_state(*PAIRING_STATE)
299 .build_stack_and_pairing_state_watcher();
300
301 let facade_fut = async move {
302 let pairing_state = facade.get_pairing_state().await.unwrap();
303 assert_eq!(pairing_state, *PAIRING_STATE);
304 };
305
306 future::join(facade_fut, pairing_state_fut).await;
307 }
308
309 #[allow(clippy::unit_cmp)] #[fasync::run_singlethreaded(test)]
311 async fn test_reset_config() {
312 let flags: ResetConfigFlags = fidl_fuchsia_weave::ResetConfigFlags::NETWORK_CONFIG
313 | fidl_fuchsia_weave::ResetConfigFlags::SERVICE_CONFIG;
314 let (facade, reset_config_fut) =
315 MockStackBuilder::new().expect_reset_config(flags, Ok(())).build_stack();
316
317 let facade_fut =
318 async move { assert_eq!(facade.reset_config(RESET_CONFIG.clone()).await.unwrap(), ()) };
319
320 future::join(facade_fut, reset_config_fut).await;
321 }
322}