Skip to main content

pryty_rustbrowser/
storage.rs

1use 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}