automerge_persistent_localstorage/
lib.rs1#![warn(missing_docs)]
2#![warn(missing_crate_level_docs)]
3#![warn(missing_doc_code_examples)]
4#![warn(clippy::pedantic)]
5#![warn(clippy::nursery)]
6
7use std::collections::HashMap;
26
27use automerge::ActorId;
28use automerge_persistent::{Persister, StoredSizes};
29use base64::Engine;
30
31#[derive(Debug)]
38pub struct LocalStoragePersister {
39 storage: web_sys::Storage,
40 changes: HashMap<String, Vec<u8>>,
41 sync_states: HashMap<String, Vec<u8>>,
43 document_key: String,
44 changes_key: String,
45 sync_states_key: String,
46 sizes: StoredSizes,
47}
48
49#[derive(Debug, thiserror::Error)]
51pub enum LocalStoragePersisterError {
52 #[error(transparent)]
54 SerdeError(#[from] serde_json::Error),
55 #[error("storage error {0:?}")]
57 StorageError(wasm_bindgen::JsValue),
58}
59
60impl LocalStoragePersister {
61 pub fn new(
63 storage: web_sys::Storage,
64 document_key: String,
65 changes_key: String,
66 sync_states_key: String,
67 ) -> Result<Self, LocalStoragePersisterError> {
68 let changes = if let Some(stored) = storage
69 .get_item(&changes_key)
70 .map_err(LocalStoragePersisterError::StorageError)?
71 {
72 serde_json::from_str(&stored)?
73 } else {
74 HashMap::new()
75 };
76 let sync_states = if let Some(stored) = storage
77 .get_item(&sync_states_key)
78 .map_err(LocalStoragePersisterError::StorageError)?
79 {
80 serde_json::from_str(&stored)?
81 } else {
82 HashMap::new()
83 };
84 let document = if let Some(doc_string) = storage
85 .get_item(&document_key)
86 .map_err(LocalStoragePersisterError::StorageError)?
87 {
88 let doc = serde_json::from_str::<Vec<u8>>(&doc_string)?;
89 Some(doc)
90 } else {
91 None
92 };
93 let sizes = StoredSizes {
94 changes: changes.values().map(Vec::len).sum::<usize>() as u64,
95 document: document.unwrap_or_default().len() as u64,
96 sync_states: sync_states.values().map(Vec::len).sum::<usize>() as u64,
97 };
98 Ok(Self {
99 storage,
100 changes,
101 sync_states,
102 document_key,
103 changes_key,
104 sync_states_key,
105 sizes,
106 })
107 }
108}
109
110impl Persister for LocalStoragePersister {
111 type Error = LocalStoragePersisterError;
112
113 fn get_changes(&self) -> Result<Vec<Vec<u8>>, Self::Error> {
114 Ok(self.changes.values().cloned().collect())
115 }
116
117 fn insert_changes(&mut self, changes: Vec<(ActorId, u64, Vec<u8>)>) -> Result<(), Self::Error> {
118 for (a, s, c) in changes {
119 let key = make_key(&a, s);
120
121 self.sizes.changes += c.len() as u64;
122 if let Some(old) = self.changes.insert(key, c) {
123 self.sizes.changes -= old.len() as u64;
124 }
125 }
126 self.storage
127 .set_item(&self.changes_key, &serde_json::to_string(&self.changes)?)
128 .map_err(LocalStoragePersisterError::StorageError)?;
129 Ok(())
130 }
131
132 fn remove_changes(&mut self, changes: Vec<(&ActorId, u64)>) -> Result<(), Self::Error> {
133 let mut some_removal = false;
134 for (a, s) in changes {
135 let key = make_key(a, s);
136 if let Some(old) = self.changes.remove(&key) {
137 self.sizes.changes -= old.len() as u64;
138 some_removal = true;
139 }
140 }
141
142 if some_removal {
143 let s = serde_json::to_string(&self.changes)?;
144 self.storage
145 .set_item(&self.changes_key, &s)
146 .map_err(LocalStoragePersisterError::StorageError)?;
147 }
148 Ok(())
149 }
150
151 fn get_document(&self) -> Result<Option<Vec<u8>>, Self::Error> {
152 if let Some(doc_string) = self
153 .storage
154 .get_item(&self.document_key)
155 .map_err(LocalStoragePersisterError::StorageError)?
156 {
157 let doc = serde_json::from_str(&doc_string)?;
158 Ok(Some(doc))
159 } else {
160 Ok(None)
161 }
162 }
163
164 fn set_document(&mut self, data: Vec<u8>) -> Result<(), Self::Error> {
165 self.sizes.document = data.len() as u64;
166 let data = serde_json::to_string(&data)?;
167 self.storage
168 .set_item(&self.document_key, &data)
169 .map_err(LocalStoragePersisterError::StorageError)?;
170 Ok(())
171 }
172
173 fn get_sync_state(&self, peer_id: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
174 let peer_id = base64::engine::general_purpose::STANDARD.encode(peer_id);
175 Ok(self.sync_states.get(&peer_id).cloned())
176 }
177
178 fn set_sync_state(&mut self, peer_id: Vec<u8>, sync_state: Vec<u8>) -> Result<(), Self::Error> {
179 self.sizes.sync_states += sync_state.len() as u64;
180 let peer_id = base64::engine::general_purpose::STANDARD.encode(peer_id);
181 if let Some(old) = self.sync_states.insert(peer_id, sync_state) {
182 self.sizes.sync_states -= old.len() as u64;
183 }
184 self.storage
185 .set_item(
186 &self.sync_states_key,
187 &serde_json::to_string(&self.sync_states)?,
188 )
189 .map_err(LocalStoragePersisterError::StorageError)?;
190 Ok(())
191 }
192
193 fn remove_sync_states(&mut self, peer_ids: &[&[u8]]) -> Result<(), Self::Error> {
194 for peer_id in peer_ids {
195 let peer_id = base64::engine::general_purpose::STANDARD.encode(peer_id);
196 if let Some(old) = self.sync_states.remove(&peer_id) {
197 self.sizes.sync_states -= old.len() as u64;
198 }
199 }
200 self.storage
201 .set_item(
202 &self.sync_states_key,
203 &serde_json::to_string(&self.sync_states)?,
204 )
205 .map_err(LocalStoragePersisterError::StorageError)?;
206 Ok(())
207 }
208
209 fn get_peer_ids(&self) -> Result<Vec<Vec<u8>>, Self::Error> {
210 Ok(self
211 .sync_states
212 .keys()
213 .map(|key| {
214 base64::engine::general_purpose::STANDARD
215 .decode(key)
216 .expect("Failed to base64 decode they peer_id")
217 })
218 .collect())
219 }
220
221 fn sizes(&self) -> StoredSizes {
222 self.sizes.clone()
223 }
224
225 fn flush(&mut self) -> Result<usize, Self::Error> {
226 Ok(0)
227 }
228}
229
230fn make_key(actor_id: &ActorId, seq: u64) -> String {
234 let mut key = actor_id.to_hex_string();
235 key.push_str(&seq.to_string());
236 key
237}