1use js_sys::{Function, Reflect};
5use wasm_bindgen::{JsCast, prelude::*};
6use web_sys::window;
7
8pub fn init() -> Result<(), JsValue> {
21 let biom = biometric_object()?;
22 let func = Reflect::get(&biom, &JsValue::from_str("init"))?.dyn_into::<Function>()?;
23 func.call0(&biom)?;
24 Ok(())
25}
26
27pub fn request_access(
41 auth_key: &str,
42 reason: Option<&str>,
43 options: Option<&JsValue>
44) -> Result<(), JsValue> {
45 let biom = biometric_object()?;
46 let func = Reflect::get(&biom, &JsValue::from_str("requestAccess"))?.dyn_into::<Function>()?;
47 let key = JsValue::from_str(auth_key);
48 match (reason, options) {
49 (Some(r), Some(o)) => {
50 let r = JsValue::from_str(r);
51 func.call3(&biom, &key, &r, o)?;
52 }
53 (Some(r), None) => {
54 let r = JsValue::from_str(r);
55 func.call2(&biom, &key, &r)?;
56 }
57 (None, Some(o)) => {
58 func.call3(&biom, &key, &JsValue::UNDEFINED, o)?;
59 }
60 (None, None) => {
61 func.call1(&biom, &key)?;
62 }
63 }
64 Ok(())
65}
66
67pub fn authenticate(
81 auth_key: &str,
82 reason: Option<&str>,
83 options: Option<&JsValue>
84) -> Result<(), JsValue> {
85 let biom = biometric_object()?;
86 let func = Reflect::get(&biom, &JsValue::from_str("authenticate"))?.dyn_into::<Function>()?;
87 let key = JsValue::from_str(auth_key);
88 match (reason, options) {
89 (Some(r), Some(o)) => {
90 let r = JsValue::from_str(r);
91 func.call3(&biom, &key, &r, o)?;
92 }
93 (Some(r), None) => {
94 let r = JsValue::from_str(r);
95 func.call2(&biom, &key, &r)?;
96 }
97 (None, Some(o)) => {
98 func.call3(&biom, &key, &JsValue::UNDEFINED, o)?;
99 }
100 (None, None) => {
101 func.call1(&biom, &key)?;
102 }
103 }
104 Ok(())
105}
106
107pub fn update_biometric_token(token: &str) -> Result<(), JsValue> {
120 let biom = biometric_object()?;
121 let func =
122 Reflect::get(&biom, &JsValue::from_str("updateBiometricToken"))?.dyn_into::<Function>()?;
123 func.call1(&biom, &JsValue::from_str(token))?;
124 Ok(())
125}
126
127pub fn open_settings() -> Result<(), JsValue> {
140 let biom = biometric_object()?;
141 let func = Reflect::get(&biom, &JsValue::from_str("openSettings"))?.dyn_into::<Function>()?;
142 func.call0(&biom)?;
143 Ok(())
144}
145
146pub fn is_inited() -> Result<bool, JsValue> {
158 let biom = biometric_object()?;
159 let value = Reflect::get(&biom, &JsValue::from_str("isInited"))?;
160 value
161 .as_bool()
162 .ok_or_else(|| JsValue::from_str("isInited not a bool"))
163}
164
165pub fn is_biometric_available() -> Result<bool, JsValue> {
177 let biom = biometric_object()?;
178 let value = Reflect::get(&biom, &JsValue::from_str("isBiometricAvailable"))?;
179 value
180 .as_bool()
181 .ok_or_else(|| JsValue::from_str("isBiometricAvailable not a bool"))
182}
183
184pub fn is_access_requested() -> Result<bool, JsValue> {
196 let biom = biometric_object()?;
197 let value = Reflect::get(&biom, &JsValue::from_str("isAccessRequested"))?;
198 value
199 .as_bool()
200 .ok_or_else(|| JsValue::from_str("isAccessRequested not a bool"))
201}
202
203pub fn is_access_granted() -> Result<bool, JsValue> {
215 let biom = biometric_object()?;
216 let value = Reflect::get(&biom, &JsValue::from_str("isAccessGranted"))?;
217 value
218 .as_bool()
219 .ok_or_else(|| JsValue::from_str("isAccessGranted not a bool"))
220}
221
222pub fn is_biometric_token_saved() -> Result<bool, JsValue> {
234 let biom = biometric_object()?;
235 let value = Reflect::get(&biom, &JsValue::from_str("isBiometricTokenSaved"))?;
236 value
237 .as_bool()
238 .ok_or_else(|| JsValue::from_str("isBiometricTokenSaved not a bool"))
239}
240
241pub fn device_id() -> Result<String, JsValue> {
253 let biom = biometric_object()?;
254 let value = Reflect::get(&biom, &JsValue::from_str("deviceId"))?;
255 value
256 .as_string()
257 .ok_or_else(|| JsValue::from_str("deviceId not a string"))
258}
259
260fn biometric_object() -> Result<JsValue, JsValue> {
261 let win = window().ok_or_else(|| JsValue::from_str("no window"))?;
262 let tg = Reflect::get(&win, &JsValue::from_str("Telegram"))?;
263 let webapp = Reflect::get(&tg, &JsValue::from_str("WebApp"))?;
264 Reflect::get(&webapp, &JsValue::from_str("BiometricManager"))
265}
266
267#[cfg(test)]
268mod tests {
269 use js_sys::{Function, Object, Reflect};
270 use wasm_bindgen::JsValue;
271 use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
272 use web_sys::window;
273
274 use super::*;
275
276 wasm_bindgen_test_configure!(run_in_browser);
277
278 #[allow(dead_code)]
279 fn setup_biometric() -> Object {
280 let win = window().expect("window should be available");
281 let telegram = Object::new();
282 let webapp = Object::new();
283 let biom = Object::new();
284 let _ = Reflect::set(&win, &"Telegram".into(), &telegram);
285 let _ = Reflect::set(&telegram, &"WebApp".into(), &webapp);
286 let _ = Reflect::set(&webapp, &"BiometricManager".into(), &biom);
287 biom
288 }
289
290 #[wasm_bindgen_test]
291 #[allow(dead_code, clippy::unused_unit)]
292 fn init_ok() {
293 let biom = setup_biometric();
294 let func = Function::new_no_args("this.called = true;");
295 let _ = Reflect::set(&biom, &"init".into(), &func);
296 assert!(init().is_ok());
297 assert!(
298 Reflect::get(&biom, &"called".into())
299 .unwrap()
300 .as_bool()
301 .unwrap()
302 );
303 }
304
305 #[wasm_bindgen_test]
306 #[allow(dead_code, clippy::unused_unit)]
307 fn init_err() {
308 let _ = setup_biometric();
309 assert!(init().is_err());
310 }
311
312 #[wasm_bindgen_test]
313 #[allow(dead_code, clippy::unused_unit)]
314 fn request_access_ok() {
315 let biom = setup_biometric();
316 let func = Function::new_with_args("key", "this.called = true; this.key = key;");
317 let _ = Reflect::set(&biom, &"requestAccess".into(), &func);
318 assert!(request_access("abc", None, None).is_ok());
319 assert!(
320 Reflect::get(&biom, &"called".into())
321 .unwrap()
322 .as_bool()
323 .unwrap()
324 );
325 assert_eq!(
326 Reflect::get(&biom, &"key".into())
327 .unwrap()
328 .as_string()
329 .unwrap(),
330 "abc"
331 );
332 }
333
334 #[wasm_bindgen_test]
335 #[allow(dead_code, clippy::unused_unit)]
336 fn request_access_err() {
337 let _ = setup_biometric();
338 assert!(request_access("abc", None, None).is_err());
339 }
340
341 #[wasm_bindgen_test]
342 #[allow(dead_code, clippy::unused_unit)]
343 fn authenticate_ok() {
344 let biom = setup_biometric();
345 let func = Function::new_with_args("key", "this.called = true; this.key = key;");
346 let _ = Reflect::set(&biom, &"authenticate".into(), &func);
347 assert!(authenticate("abc", None, None).is_ok());
348 assert!(
349 Reflect::get(&biom, &"called".into())
350 .unwrap()
351 .as_bool()
352 .unwrap()
353 );
354 assert_eq!(
355 Reflect::get(&biom, &"key".into())
356 .unwrap()
357 .as_string()
358 .unwrap(),
359 "abc"
360 );
361 assert_eq!(
362 Reflect::get(&biom, &"reason".into())
363 .unwrap()
364 .as_string()
365 .unwrap(),
366 "why"
367 );
368 }
369
370 #[wasm_bindgen_test]
371 #[allow(dead_code, clippy::unused_unit)]
372 fn authenticate_err() {
373 let _ = setup_biometric();
374 assert!(authenticate("abc", None, None).is_err());
375 }
376
377 #[wasm_bindgen_test]
378 #[allow(dead_code, clippy::unused_unit)]
379 fn update_biometric_token_ok() {
380 let biom = setup_biometric();
381 let func = Function::new_with_args("token", "this.token = token;");
382 let _ = Reflect::set(&biom, &"updateBiometricToken".into(), &func);
383 assert!(update_biometric_token("abc").is_ok());
384 assert_eq!(
385 Reflect::get(&biom, &"token".into())
386 .unwrap()
387 .as_string()
388 .unwrap(),
389 "abc"
390 );
391 }
392
393 #[wasm_bindgen_test]
394 #[allow(dead_code, clippy::unused_unit)]
395 fn update_biometric_token_err() {
396 let _ = setup_biometric();
397 assert!(update_biometric_token("abc").is_err());
398 }
399
400 #[wasm_bindgen_test]
401 #[allow(dead_code, clippy::unused_unit)]
402 fn open_settings_ok() {
403 let biom = setup_biometric();
404 let func = Function::new_no_args("this.called = true;");
405 let _ = Reflect::set(&biom, &"openSettings".into(), &func);
406 assert!(open_settings().is_ok());
407 assert!(
408 Reflect::get(&biom, &"called".into())
409 .unwrap()
410 .as_bool()
411 .unwrap()
412 );
413 }
414
415 #[wasm_bindgen_test]
416 #[allow(dead_code, clippy::unused_unit)]
417 fn open_settings_err() {
418 let _ = setup_biometric();
419 assert!(open_settings().is_err());
420 }
421
422 #[wasm_bindgen_test]
423 #[allow(dead_code, clippy::unused_unit)]
424 fn is_inited_ok() {
425 let biom = setup_biometric();
426 let _ = Reflect::set(&biom, &"isInited".into(), &JsValue::from(true));
427 assert!(is_inited().expect("is_inited"));
428 }
429
430 #[wasm_bindgen_test]
431 #[allow(dead_code, clippy::unused_unit)]
432 fn is_inited_err() {
433 let _ = setup_biometric();
434 assert!(is_inited().is_err());
435 }
436
437 #[wasm_bindgen_test]
438 #[allow(dead_code, clippy::unused_unit)]
439 fn is_biometric_available_ok() {
440 let biom = setup_biometric();
441 let _ = Reflect::set(&biom, &"isBiometricAvailable".into(), &JsValue::from(true));
442 assert!(is_biometric_available().expect("is_biometric_available"));
443 }
444
445 #[wasm_bindgen_test]
446 #[allow(dead_code, clippy::unused_unit)]
447 fn is_biometric_available_err() {
448 let _ = setup_biometric();
449 assert!(is_biometric_available().is_err());
450 }
451
452 #[wasm_bindgen_test]
453 #[allow(dead_code, clippy::unused_unit)]
454 fn is_access_requested_ok() {
455 let biom = setup_biometric();
456 let _ = Reflect::set(&biom, &"isAccessRequested".into(), &JsValue::from(true));
457 assert!(is_access_requested().expect("is_access_requested"));
458 }
459
460 #[wasm_bindgen_test]
461 #[allow(dead_code, clippy::unused_unit)]
462 fn is_access_requested_err() {
463 let _ = setup_biometric();
464 assert!(is_access_requested().is_err());
465 }
466
467 #[wasm_bindgen_test]
468 #[allow(dead_code, clippy::unused_unit)]
469 fn is_access_granted_ok() {
470 let biom = setup_biometric();
471 let _ = Reflect::set(&biom, &"isAccessGranted".into(), &JsValue::from(true));
472 assert!(is_access_granted().expect("is_access_granted"));
473 }
474
475 #[wasm_bindgen_test]
476 #[allow(dead_code, clippy::unused_unit)]
477 fn is_access_granted_err() {
478 let _ = setup_biometric();
479 assert!(is_access_granted().is_err());
480 }
481
482 #[wasm_bindgen_test]
483 #[allow(dead_code, clippy::unused_unit)]
484 fn is_biometric_token_saved_ok() {
485 let biom = setup_biometric();
486 let _ = Reflect::set(&biom, &"isBiometricTokenSaved".into(), &JsValue::from(true));
487 assert!(is_biometric_token_saved().expect("is_biometric_token_saved"));
488 }
489
490 #[wasm_bindgen_test]
491 #[allow(dead_code, clippy::unused_unit)]
492 fn is_biometric_token_saved_err() {
493 let _ = setup_biometric();
494 assert!(is_biometric_token_saved().is_err());
495 }
496
497 #[wasm_bindgen_test]
498 #[allow(dead_code, clippy::unused_unit)]
499 fn device_id_ok() {
500 let biom = setup_biometric();
501 let _ = Reflect::set(&biom, &"deviceId".into(), &JsValue::from_str("id123"));
502 assert_eq!(device_id().expect("device_id"), "id123");
503 }
504
505 #[wasm_bindgen_test]
506 #[allow(dead_code, clippy::unused_unit)]
507 fn device_id_err() {
508 let _ = setup_biometric();
509 assert!(device_id().is_err());
510 }
511}