Skip to main content

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}