pryty_rustbrowser/
storage.rs1use dioxus::prelude::*;
2use serde::{de::DeserializeOwned, Serialize};
3
4#[derive(Debug, Clone, PartialEq)]
5pub enum StorageError {
6 WindowUnavailable,
7 LocalStorageUnavailable,
8 LocalStorageAccessFailed(String),
9 ReadFailed(String),
10 WriteFailed(String),
11 SerializeFailed(String),
12 DeserializeFailed(String),
13}
14
15impl core::fmt::Display for StorageError {
16 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17 match self {
18 StorageError::WindowUnavailable => write!(f, "window unavailable"),
19 StorageError::LocalStorageUnavailable => write!(f, "localStorage unavailable"),
20 StorageError::LocalStorageAccessFailed(e) => {
21 write!(f, "failed to access localStorage: {e}")
22 }
23 StorageError::ReadFailed(e) => write!(f, "failed to read from localStorage: {e}"),
24 StorageError::WriteFailed(e) => write!(f, "failed to write to localStorage: {e}"),
25 StorageError::SerializeFailed(e) => write!(f, "failed to serialize value: {e}"),
26 StorageError::DeserializeFailed(e) => write!(f, "failed to deserialize value: {e}"),
27 }
28 }
29}
30
31fn get_local_storage() -> Result<web_sys::Storage, StorageError> {
32 let window = web_sys::window().ok_or(StorageError::WindowUnavailable)?;
33 let storage = window
34 .local_storage()
35 .map_err(|e| StorageError::LocalStorageAccessFailed(format!("{e:?}")))?
36 .ok_or(StorageError::LocalStorageUnavailable)?;
37 Ok(storage)
38}
39
40pub fn use_storage<T>(
41 key: impl Into<String>,
42 default: T,
43) -> Result<(Signal<T>, Callback<T>), StorageError>
44where
45 T: Serialize + DeserializeOwned + Clone + 'static,
46{
47 let key = key.into();
48 let storage = get_local_storage()?;
49
50 let initial = match storage.get_item(&key) {
51 Ok(Some(raw)) => match serde_json::from_str::<T>(&raw) {
52 Ok(v) => v,
53 Err(_) => default.clone(),
54 },
55 Ok(None) => default.clone(),
56 Err(_) => default.clone(),
57 };
58
59 let mut value = use_signal(|| initial);
60
61 let setter = {
62 let key = key.clone();
63 let storage = storage.clone();
64
65 use_callback(move |new_val: T| {
66 if let Ok(serialized) = serde_json::to_string(&new_val) {
67 let _ = storage
68 .set_item(&key, &serialized)
69 .map_err(|e| StorageError::WriteFailed(format!("{e:?}")));
70 }
71 value.set(new_val);
72 })
73 };
74
75 Ok((value, setter))
76}
77
78pub fn read_storage<T>(key: impl AsRef<str>) -> Result<Option<T>, StorageError>
79where
80 T: DeserializeOwned,
81{
82 let storage = get_local_storage()?;
83 let key_ref = key.as_ref();
84
85 let raw = storage
86 .get_item(key_ref)
87 .map_err(|e| StorageError::ReadFailed(format!("{e:?}")))?;
88
89 match raw {
90 Some(text) => {
91 let parsed = serde_json::from_str::<T>(&text)
92 .map_err(|e| StorageError::DeserializeFailed(e.to_string()))?;
93 Ok(Some(parsed))
94 }
95 None => Ok(None),
96 }
97}
98
99pub fn write_storage<T>(key: impl AsRef<str>, value: &T) -> Result<(), StorageError>
100where
101 T: Serialize,
102{
103 let storage = get_local_storage()?;
104 let key_ref = key.as_ref();
105
106 let serialized =
107 serde_json::to_string(value).map_err(|e| StorageError::SerializeFailed(e.to_string()))?;
108
109 storage
110 .set_item(key_ref, &serialized)
111 .map_err(|e| StorageError::WriteFailed(format!("{e:?}")))?;
112
113 Ok(())
114}