Skip to content

Commit 412236d

Browse files
committed
Updating the Web Storage interface
* remove panics * support both local and session storage * provide detailed error conditions * provide a fuller interface
1 parent 6790f0a commit 412236d

File tree

5 files changed

+144
-9
lines changed

5 files changed

+144
-9
lines changed

examples/todomvc/src/lib.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
use enclose::enc;
22
use indexmap::IndexMap;
3-
use seed::{
4-
browser::service::storage::{self, Storage},
5-
prelude::*,
6-
*,
7-
};
3+
use seed::{browser::service::web_storage::WebStorage, prelude::*, *};
84
use serde::{Deserialize, Serialize};
95
use std::mem;
106
use uuid::Uuid;
@@ -37,7 +33,7 @@ struct Data {
3733
}
3834

3935
struct Services {
40-
local_storage: Storage,
36+
local_storage: WebStorage,
4137
}
4238

4339
#[derive(Default)]
@@ -95,8 +91,8 @@ fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
9591
.subscribe(Msg::UrlChanged)
9692
.notify(subs::UrlChanged(url));
9793

98-
let local_storage = storage::get_storage().expect("get `LocalStorage`");
99-
let data = storage::load_data(&local_storage, STORAGE_KEY).unwrap_or_default();
94+
let local_storage = web_storage::get_local_storage().expect("get `LocalStorage`");
95+
let data = local_storage.load(STORAGE_KEY).unwrap_or_default();
10096

10197
Model {
10298
data,
@@ -209,7 +205,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
209205
Msg::NoOp => (),
210206
}
211207
// Save data into LocalStorage. It should be optimized in a real-world application.
212-
storage::store_data(&model.services.local_storage, STORAGE_KEY, &data);
208+
model.services.local_storage.save(STORAGE_KEY, &data);
213209
}
214210

215211
// ------ ------

src/browser/service.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod fetch;
22
pub mod routing;
33
pub mod storage;
4+
pub mod web_storage;

src/browser/service/storage.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern crate serde_json;
1010

1111
pub type Storage = web_sys::Storage;
1212

13+
#[deprecated(since = "0.7.0", note = "Use web_storage::get_local_storage")]
1314
#[allow(clippy::module_name_repetitions)]
1415
pub fn get_storage() -> Option<Storage> {
1516
web_sys::window()
@@ -20,6 +21,7 @@ pub fn get_storage() -> Option<Storage> {
2021
}
2122

2223
/// Create a new store, from a serializable data structure.
24+
#[deprecated(since = "0.7.0", note = "use WebStorage.store_data")]
2325
pub fn store_data<T>(storage: &Storage, name: &str, data: &T)
2426
where
2527
T: serde::Serialize,
@@ -31,6 +33,7 @@ where
3133
}
3234

3335
/// Load a store, to a deserializable data structure.
36+
#[deprecated(since = "0.7.0", note = "use WebStorage.load_data")]
3437
pub fn load_data<T>(storage: &Storage, name: &str) -> Option<T>
3538
where
3639
T: serde::de::DeserializeOwned,

src/browser/service/web_storage.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//! Allows use of the Web Storage API including both local and session storage.
2+
//!
3+
//! # References
4+
//! * [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/Storage)
5+
//! * [web-sys docs](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Storage.html)
6+
//! * [Example syntax](https://github.com/rustwasm/wasm-bindgen/blob/master/examples/todomvc/src/store.rs)
7+
8+
extern crate serde;
9+
extern crate serde_json;
10+
use crate::browser::util::window;
11+
use web_sys::Storage;
12+
13+
pub type JsValue = wasm_bindgen::JsValue;
14+
15+
pub enum Mechanism {
16+
LocalStorage,
17+
SessionStorage,
18+
}
19+
20+
pub struct WebStorage {
21+
pub mechanism: Mechanism,
22+
storage: Storage,
23+
}
24+
25+
/// Things that can go wrong when trying to load data from storage.
26+
pub enum LoadError {
27+
/// Could not connect to storage.
28+
CouldNotConnect(JsValue),
29+
/// The data could not be decoded from JSON.
30+
CouldNotDecode(serde_json::Error),
31+
/// There is no data for that key.
32+
NoData,
33+
}
34+
35+
/// Things that can go wrong when trying to save data to storage.
36+
pub enum SaveError {
37+
/// The browser denied saving to storage. Usually because the storage is full.
38+
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem#Exceptions
39+
CouldNotSave(JsValue),
40+
/// Supplied data could not be encoded to json.
41+
CouldNotEncode(serde_json::Error),
42+
}
43+
44+
impl WebStorage {
45+
/// Clear all data in storage
46+
pub fn clear(&self) -> bool {
47+
self.storage.clear().is_ok()
48+
}
49+
50+
/// A vector of all the keys in storage
51+
///
52+
/// # Errors
53+
///
54+
/// Will return a `Err(JsValue)` if the storage length could not be retrieved.
55+
pub fn keys(storage: &Storage) -> Result<Vec<String>, JsValue> {
56+
let mut keys = vec![];
57+
let length = storage.length()?;
58+
for index in 0..length {
59+
if let Ok(Some(key)) = storage.key(index) {
60+
keys.push(key);
61+
}
62+
}
63+
Ok(keys)
64+
}
65+
66+
/// Load a JSON deserializable data structure from storage.
67+
///
68+
/// # Errors
69+
///
70+
/// Will return a `Err(LoadError)` if the data could not be loaded
71+
pub fn load<T>(&self, key: &str) -> Result<T, LoadError>
72+
where
73+
T: serde::de::DeserializeOwned,
74+
{
75+
let item = self
76+
.storage
77+
.get_item(key)
78+
.map_err(LoadError::CouldNotConnect)?;
79+
80+
match item {
81+
None => Err(LoadError::NoData),
82+
Some(d) => {
83+
let decoded = serde_json::from_str(&d);
84+
decoded.map_err(LoadError::CouldNotDecode)
85+
}
86+
}
87+
}
88+
89+
/// Delete a key and associated data from storage
90+
pub fn delete(&self, key: &str) -> bool {
91+
self.storage.remove_item(key).is_ok()
92+
}
93+
94+
/// Save a JSON serializable data structure to storage.
95+
///
96+
/// # Errors
97+
///
98+
/// Will return a `SaveError` if the data could not be saved
99+
pub fn save<T>(&self, key: &str, data: &T) -> Result<(), SaveError>
100+
where
101+
T: serde::Serialize,
102+
{
103+
let serialized = serde_json::to_string(&data).map_err(SaveError::CouldNotEncode)?;
104+
self.storage
105+
.set_item(key, &serialized)
106+
.map_err(SaveError::CouldNotSave)
107+
}
108+
}
109+
110+
/// Get an instance of Local Storage
111+
/// Local Storage maintains a storage area that persists even when the browser
112+
/// is closed and reopened
113+
pub fn get_local_storage() -> Option<WebStorage> {
114+
window()
115+
.local_storage()
116+
.unwrap_or(None)
117+
.map(|storage| WebStorage {
118+
mechanism: Mechanism::LocalStorage,
119+
storage,
120+
})
121+
}
122+
123+
/// Get an instance of Session Storage
124+
/// Session Storage maintains a storage area for the duration of the page session
125+
/// (as long as the browser is open, including page reloads and restores)
126+
pub fn get_session_storage() -> Option<WebStorage> {
127+
window()
128+
.session_storage()
129+
.unwrap_or(None)
130+
.map(|storage| WebStorage {
131+
mechanism: Mechanism::SessionStorage,
132+
storage,
133+
})
134+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub use crate::{
1919
browser::service::fetch::{Method, Request, ResponseDataResult, ResponseResult},
2020
browser::service::routing::push_route,
2121
browser::service::storage,
22+
browser::service::web_storage,
2223
browser::url::Url,
2324
browser::util::{
2425
self, body, canvas, canvas_context_2d, cookies, document, error, history, html_document,

0 commit comments

Comments
 (0)