1use std::fmt;
8
9use anyhow::{bail, Result};
10use web_sys::{self, Storage};
11
12use psh::{PshStore, ZeroizingString};
13
14pub struct PshWebDb {
16 local_storage: Storage,
17}
18
19impl PshWebDb {
20 pub fn new() -> Self {
21 let window = web_sys::window().expect("Window not available");
22 let local_storage = window.local_storage()
23 .expect("LocalStorage not available")
24 .expect("LocalStorage not defined");
25
26 Self { local_storage }
27 }
28}
29
30impl PshStore for PshWebDb {
31 fn exists(&self) -> bool {
32 self.records().next().is_some()
33 }
34
35 fn records(&self) -> Box<dyn Iterator<Item=ZeroizingString>> {
36 Box::new(PshWebDbIter::new(self.local_storage.clone()))
37 }
38
39 fn append(&mut self, record: &ZeroizingString) -> Result<()> {
40 match self.local_storage.set_item(record, "psh") {
41 Ok(_) => Ok(()),
42 Err(js_err) => bail!(Error::AliasAppendError(
43 ZeroizingString::new(js_err.as_string().unwrap())))
44 }
45 }
46
47 fn delete(&mut self, record: &ZeroizingString) -> Result<()> {
48 match self.local_storage.remove_item(record) {
49 Ok(_) => Ok(()),
50 Err(js_err) => bail!(Error::AliasRemoveError(
51 ZeroizingString::new(js_err.as_string().unwrap())))
52 }
53 }
54}
55
56struct PshWebDbIter {
57 store: Storage,
58 index: u32,
59}
60
61impl PshWebDbIter {
62 pub fn new(store: Storage) -> Self {
63 Self {
64 store,
65 index: 0,
66 }
67 }
68}
69
70impl Iterator for PshWebDbIter {
71 type Item = ZeroizingString;
72
73 fn next(&mut self) -> Option<Self::Item> {
74 if let Ok(ls_length) = self.store.length() {
75 if ls_length > self.index {
76 while let Ok(Some(key)) = self.store.key(self.index) {
77 self.index += 1;
78 if let Ok(Some(value)) = self.store.get_item(&key) {
79 if value == "psh" {
80 return Some(ZeroizingString::new(key));
81 }
82 }
83 if self.index == ls_length {
84 break;
85 }
86 }
87 }
88 }
89 None
90 }
91}
92
93#[derive(Debug)]
95enum Error {
96 AliasAppendError(ZeroizingString),
98
99 AliasRemoveError(ZeroizingString),
101}
102
103impl fmt::Display for Error {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 Error::AliasAppendError(err_string) => write!(
107 f, "Cannot add alias to DB: {}", err_string
108 ),
109 Error::AliasRemoveError(err_string) => write!(
110 f, "Cannot remove alias from DB: {}", err_string
111 ),
112 }
113 }
114}
115
116impl std::error::Error for Error {}