matrix_sdk_indexeddb/serializer/safe_encode/
traits.rs1use base64::{
4 Engine, alphabet,
5 engine::{GeneralPurpose, general_purpose},
6};
7use indexed_db_futures::KeyRange;
8use matrix_sdk_store_encryption::StoreCipher;
9use ruma::{
10 DeviceId, EventId, MxcUri, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, TransactionId,
11 UserId,
12 events::{
13 GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType, receipt::ReceiptType,
14 },
15};
16use wasm_bindgen::JsValue;
17
18pub const KEY_SEPARATOR: &str = "\u{001D}";
20pub const RANGE_END: &str = "\u{001E}";
23pub const ESCAPED: &str = "\u{001E}\u{001D}";
26
27const STANDARD_NO_PAD: GeneralPurpose =
28 GeneralPurpose::new(&alphabet::STANDARD, general_purpose::NO_PAD);
29
30pub trait SafeEncode {
37 fn as_encoded_string(&self) -> String;
43
44 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
48 STANDARD_NO_PAD
49 .encode(store_cipher.hash_key(table_name, self.as_encoded_string().as_bytes()))
50 }
51
52 fn encode_to_range(&self) -> KeyRange<JsValue> {
56 let key = self.as_encoded_string();
57 KeyRange::Bound(
58 JsValue::from([&key, KEY_SEPARATOR].concat()),
59 false,
60 JsValue::from([&key, RANGE_END].concat()),
61 false,
62 )
63 }
64
65 fn encode_to_range_secure(
66 &self,
67 table_name: &str,
68 store_cipher: &StoreCipher,
69 ) -> KeyRange<JsValue> {
70 let key = self.as_secure_string(table_name, store_cipher);
71 KeyRange::Bound(
72 JsValue::from([&key, KEY_SEPARATOR].concat()),
73 false,
74 JsValue::from([&key, RANGE_END].concat()),
75 false,
76 )
77 }
78}
79
80impl<A, B> SafeEncode for (A, B)
83where
84 A: SafeEncode,
85 B: SafeEncode,
86{
87 fn as_encoded_string(&self) -> String {
88 [&self.0.as_encoded_string(), KEY_SEPARATOR, &self.1.as_encoded_string()].concat()
89 }
90
91 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
92 [
93 &STANDARD_NO_PAD
94 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
95 KEY_SEPARATOR,
96 &STANDARD_NO_PAD
97 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
98 ]
99 .concat()
100 }
101}
102
103impl<A, B, C> SafeEncode for (A, B, C)
106where
107 A: SafeEncode,
108 B: SafeEncode,
109 C: SafeEncode,
110{
111 fn as_encoded_string(&self) -> String {
112 [
113 &self.0.as_encoded_string(),
114 KEY_SEPARATOR,
115 &self.1.as_encoded_string(),
116 KEY_SEPARATOR,
117 &self.2.as_encoded_string(),
118 ]
119 .concat()
120 }
121
122 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
123 [
124 &STANDARD_NO_PAD
125 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
126 KEY_SEPARATOR,
127 &STANDARD_NO_PAD
128 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
129 KEY_SEPARATOR,
130 &STANDARD_NO_PAD
131 .encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
132 ]
133 .concat()
134 }
135}
136
137impl<A, B, C, D> SafeEncode for (A, B, C, D)
140where
141 A: SafeEncode,
142 B: SafeEncode,
143 C: SafeEncode,
144 D: SafeEncode,
145{
146 fn as_encoded_string(&self) -> String {
147 [
148 &self.0.as_encoded_string(),
149 KEY_SEPARATOR,
150 &self.1.as_encoded_string(),
151 KEY_SEPARATOR,
152 &self.2.as_encoded_string(),
153 KEY_SEPARATOR,
154 &self.3.as_encoded_string(),
155 ]
156 .concat()
157 }
158
159 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
160 [
161 &STANDARD_NO_PAD
162 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
163 KEY_SEPARATOR,
164 &STANDARD_NO_PAD
165 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
166 KEY_SEPARATOR,
167 &STANDARD_NO_PAD
168 .encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
169 KEY_SEPARATOR,
170 &STANDARD_NO_PAD
171 .encode(store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes())),
172 ]
173 .concat()
174 }
175}
176
177impl<A, B, C, D, E> SafeEncode for (A, B, C, D, E)
180where
181 A: SafeEncode,
182 B: SafeEncode,
183 C: SafeEncode,
184 D: SafeEncode,
185 E: SafeEncode,
186{
187 fn as_encoded_string(&self) -> String {
188 [
189 &self.0.as_encoded_string(),
190 KEY_SEPARATOR,
191 &self.1.as_encoded_string(),
192 KEY_SEPARATOR,
193 &self.2.as_encoded_string(),
194 KEY_SEPARATOR,
195 &self.3.as_encoded_string(),
196 KEY_SEPARATOR,
197 &self.4.as_encoded_string(),
198 ]
199 .concat()
200 }
201
202 fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String {
203 [
204 &STANDARD_NO_PAD
205 .encode(store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes())),
206 KEY_SEPARATOR,
207 &STANDARD_NO_PAD
208 .encode(store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes())),
209 KEY_SEPARATOR,
210 &STANDARD_NO_PAD
211 .encode(store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes())),
212 KEY_SEPARATOR,
213 &STANDARD_NO_PAD
214 .encode(store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes())),
215 KEY_SEPARATOR,
216 &STANDARD_NO_PAD
217 .encode(store_cipher.hash_key(table_name, self.4.as_encoded_string().as_bytes())),
218 ]
219 .concat()
220 }
221}
222
223impl SafeEncode for String {
224 fn as_encoded_string(&self) -> String {
225 self.replace(KEY_SEPARATOR, ESCAPED)
226 }
227}
228
229impl SafeEncode for str {
230 fn as_encoded_string(&self) -> String {
231 self.replace(KEY_SEPARATOR, ESCAPED)
232 }
233}
234
235impl<T: SafeEncode + ?Sized> SafeEncode for &T {
236 fn as_encoded_string(&self) -> String {
237 (*self).as_encoded_string()
238 }
239}
240
241impl SafeEncode for TransactionId {
242 fn as_encoded_string(&self) -> String {
243 self.to_string().as_encoded_string()
244 }
245}
246
247impl SafeEncode for GlobalAccountDataEventType {
248 fn as_encoded_string(&self) -> String {
249 self.to_string().as_encoded_string()
250 }
251}
252
253impl SafeEncode for RoomAccountDataEventType {
254 fn as_encoded_string(&self) -> String {
255 self.to_string().as_encoded_string()
256 }
257}
258
259impl SafeEncode for StateEventType {
260 fn as_encoded_string(&self) -> String {
261 self.to_string().as_encoded_string()
262 }
263}
264
265impl SafeEncode for ReceiptType {
266 fn as_encoded_string(&self) -> String {
267 self.as_str().as_encoded_string()
268 }
269}
270
271impl SafeEncode for RoomId {
272 fn as_encoded_string(&self) -> String {
273 self.as_str().as_encoded_string()
274 }
275}
276
277impl SafeEncode for OwnedRoomId {
278 fn as_encoded_string(&self) -> String {
279 self.as_str().as_encoded_string()
280 }
281}
282
283impl SafeEncode for UserId {
284 fn as_encoded_string(&self) -> String {
285 self.as_str().as_encoded_string()
286 }
287}
288
289impl SafeEncode for OwnedUserId {
290 fn as_encoded_string(&self) -> String {
291 self.as_str().as_encoded_string()
292 }
293}
294
295impl SafeEncode for DeviceId {
296 fn as_encoded_string(&self) -> String {
297 self.as_str().as_encoded_string()
298 }
299}
300
301impl SafeEncode for EventId {
302 fn as_encoded_string(&self) -> String {
303 self.as_str().as_encoded_string()
304 }
305}
306
307impl SafeEncode for OwnedEventId {
308 fn as_encoded_string(&self) -> String {
309 self.as_str().as_encoded_string()
310 }
311}
312
313impl SafeEncode for MxcUri {
314 fn as_encoded_string(&self) -> String {
315 self.as_str().as_encoded_string()
316 }
317}
318
319impl SafeEncode for usize {
320 fn as_encoded_string(&self) -> String {
321 self.to_string()
322 }
323
324 fn as_secure_string(&self, _table_name: &str, _store_cipher: &StoreCipher) -> String {
325 self.to_string()
326 }
327}