blitz_platform_api/storage.rs
1//! The storage provider this crate can supply without depending on anything:
2//! an in-memory one.
3//!
4//! A persistent provider is the embedder's, because persistence is a policy
5//! question this crate has no standing to answer. Where the profile directory
6//! is, what the quota is, what happens when the store is corrupt on load: all
7//! of those belong to whatever is embedding Blitz. See the README.
8
9use std::collections::HashMap;
10use std::sync::Mutex;
11
12use blitz_traits::platform::{OriginKey, StorageError, StorageProvider};
13
14/// Origin-scoped key/value storage that lives and dies with the process.
15///
16/// Correct, just not durable. It is the right provider for a test, for a
17/// private-browsing mode, and for an embedder that has not wired a real one
18/// yet, and it is what the storage semantics are asserted against.
19///
20/// **Note what it is not: a fallback that silently loses data.** chuzz's
21/// current JavaScript shim is an in-memory `localStorage` that a page cannot
22/// distinguish from a real one, so a site's settings vanish on reload with no
23/// diagnostic. That is a property of the shim rather than of in-memory storage,
24/// but an embedder choosing this deliberately should still know it is choosing
25/// it.
26#[derive(Default)]
27pub struct MemoryStorage {
28 /// Keyed by origin first, so `clear` is one removal rather than a scan, and
29 /// so the isolation is visible in the type rather than living in a key
30 /// format everything has to agree about.
31 origins: Mutex<HashMap<String, HashMap<String, String>>>,
32}
33
34impl MemoryStorage {
35 pub fn new() -> Self {
36 Self::default()
37 }
38
39 /// How many origins hold anything. For tests that want to assert isolation
40 /// rather than infer it.
41 pub fn origin_count(&self) -> usize {
42 self.origins.lock().unwrap().len()
43 }
44}
45
46impl StorageProvider for MemoryStorage {
47 fn get(&self, origin: &OriginKey, key: &str) -> Option<String> {
48 self.origins
49 .lock()
50 .unwrap()
51 .get(origin.as_str())?
52 .get(key)
53 .cloned()
54 }
55
56 fn set(&self, origin: &OriginKey, key: &str, value: &str) -> Result<(), StorageError> {
57 self.origins
58 .lock()
59 .unwrap()
60 .entry(origin.as_str().to_owned())
61 .or_default()
62 .insert(key.to_owned(), value.to_owned());
63 Ok(())
64 }
65
66 fn remove(&self, origin: &OriginKey, key: &str) {
67 if let Some(entries) = self.origins.lock().unwrap().get_mut(origin.as_str()) {
68 entries.remove(key);
69 }
70 }
71
72 fn clear(&self, origin: &OriginKey) {
73 self.origins.lock().unwrap().remove(origin.as_str());
74 }
75}