1#![cfg(target_arch = "wasm32")]
2#![deny(clippy::str_to_string)]
3
4use {
5 async_trait::async_trait,
6 futures::stream::iter,
7 gloo_storage::{LocalStorage, SessionStorage, Storage, errors::StorageError},
8 gluesql_core::{
9 ast::ColumnUniqueOption,
10 data::{Key, Schema},
11 error::{Error, Result},
12 store::{
13 AlterTable, CustomFunction, CustomFunctionMut, DataRow, Index, IndexMut, Metadata,
14 RowIter, Store, StoreMut, Transaction,
15 },
16 },
17 serde::{Deserialize, Serialize},
18 uuid::Uuid,
19};
20
21const TABLE_NAMES_PATH: &str = "gluesql-schema-names";
23
24const SCHEMA_PATH: &str = "gluesql-schema";
26
27const DATA_PATH: &str = "gluesql-data";
29
30#[derive(Clone, Copy, Default, Serialize, Deserialize)]
31pub enum WebStorageType {
32 #[default]
33 Local,
34 Session,
35}
36
37#[derive(Clone, Default, Serialize, Deserialize)]
38pub struct WebStorage {
39 storage_type: WebStorageType,
40}
41
42impl WebStorage {
43 pub fn new(storage_type: WebStorageType) -> Self {
44 Self { storage_type }
45 }
46
47 pub fn raw(&self) -> web_sys::Storage {
48 match self.storage_type {
49 WebStorageType::Local => LocalStorage::raw(),
50 WebStorageType::Session => SessionStorage::raw(),
51 }
52 }
53
54 pub fn get<T>(&self, key: impl AsRef<str>) -> Result<Option<T>>
55 where
56 T: for<'de> Deserialize<'de>,
57 {
58 let value = match self.storage_type {
59 WebStorageType::Local => LocalStorage::get(key),
60 WebStorageType::Session => SessionStorage::get(key),
61 };
62
63 match value {
64 Ok(value) => Ok(Some(value)),
65 Err(StorageError::KeyNotFound(_)) => Ok(None),
66 Err(e) => Err(Error::StorageMsg(e.to_string())),
67 }
68 }
69
70 pub fn set<T>(&self, key: impl AsRef<str>, value: T) -> Result<()>
71 where
72 T: Serialize,
73 {
74 match self.storage_type {
75 WebStorageType::Local => LocalStorage::set(key, value),
76 WebStorageType::Session => SessionStorage::set(key, value),
77 }
78 .map_err(|e| Error::StorageMsg(e.to_string()))
79 }
80
81 pub fn delete(&self, key: impl AsRef<str>) {
82 match self.storage_type {
83 WebStorageType::Local => LocalStorage::delete(key),
84 WebStorageType::Session => SessionStorage::delete(key),
85 }
86 }
87}
88
89#[async_trait]
90impl Store for WebStorage {
91 async fn fetch_all_schemas(&self) -> Result<Vec<Schema>> {
92 let mut table_names: Vec<String> = self.get(TABLE_NAMES_PATH)?.unwrap_or_default();
93 table_names.sort();
94
95 table_names
96 .iter()
97 .filter_map(|table_name| {
98 self.get(format!("{}/{}", SCHEMA_PATH, table_name))
99 .transpose()
100 })
101 .collect::<Result<Vec<_>>>()
102 }
103
104 async fn fetch_schema(&self, table_name: &str) -> Result<Option<Schema>> {
105 self.get(format!("{}/{}", SCHEMA_PATH, table_name))
106 }
107
108 async fn fetch_data(&self, table_name: &str, target: &Key) -> Result<Option<DataRow>> {
109 let path = format!("{}/{}", DATA_PATH, table_name);
110 let row = self
111 .get::<Vec<(Key, DataRow)>>(path)?
112 .unwrap_or_default()
113 .into_iter()
114 .find_map(|(key, row)| (&key == target).then_some(row));
115
116 Ok(row)
117 }
118
119 async fn scan_data<'a>(&'a self, table_name: &str) -> Result<RowIter<'a>> {
120 let path = format!("{}/{}", DATA_PATH, table_name);
121 let mut rows = self.get::<Vec<(Key, DataRow)>>(path)?.unwrap_or_default();
122
123 match self.get(format!("{}/{}", SCHEMA_PATH, table_name))? {
124 Some(Schema {
125 column_defs: Some(column_defs),
126 ..
127 }) if column_defs.iter().any(|column_def| {
128 matches!(
129 column_def.unique,
130 Some(ColumnUniqueOption { is_primary: true })
131 )
132 }) =>
133 {
134 rows.sort_by(|(key_a, _), (key_b, _)| key_a.cmp(key_b));
135 }
136 _ => {}
137 }
138
139 Ok(Box::pin(iter(rows.into_iter().map(Ok))))
140 }
141}
142
143#[async_trait]
144impl StoreMut for WebStorage {
145 async fn insert_schema(&mut self, schema: &Schema) -> Result<()> {
146 let mut table_names: Vec<String> = self.get(TABLE_NAMES_PATH)?.unwrap_or_default();
147 table_names.push(schema.table_name.clone());
148
149 self.set(TABLE_NAMES_PATH, table_names)?;
150 self.set(format!("{}/{}", SCHEMA_PATH, schema.table_name), schema)
151 }
152
153 async fn delete_schema(&mut self, table_name: &str) -> Result<()> {
154 let mut table_names: Vec<String> = self.get(TABLE_NAMES_PATH)?.unwrap_or_default();
155 table_names
156 .iter()
157 .position(|name| name == table_name)
158 .map(|i| table_names.remove(i));
159
160 self.set(TABLE_NAMES_PATH, table_names)?;
161 self.delete(format!("{}/{}", SCHEMA_PATH, table_name));
162 self.delete(format!("{}/{}", DATA_PATH, table_name));
163 Ok(())
164 }
165
166 async fn append_data(&mut self, table_name: &str, new_rows: Vec<DataRow>) -> Result<()> {
167 let path = format!("{}/{}", DATA_PATH, table_name);
168 let rows = self.get::<Vec<(Key, DataRow)>>(&path)?.unwrap_or_default();
169 let new_rows = new_rows.into_iter().map(|row| {
170 let key = Key::Uuid(Uuid::new_v4().as_u128());
171
172 (key, row)
173 });
174
175 let rows = rows.into_iter().chain(new_rows).collect::<Vec<_>>();
176
177 self.set(path, rows)
178 }
179
180 async fn insert_data(&mut self, table_name: &str, new_rows: Vec<(Key, DataRow)>) -> Result<()> {
181 let path = format!("{}/{}", DATA_PATH, table_name);
182 let mut rows = self.get::<Vec<(Key, DataRow)>>(&path)?.unwrap_or_default();
183
184 for (key, row) in new_rows.into_iter() {
185 if let Some(i) = rows.iter().position(|(k, _)| k == &key) {
186 rows[i] = (key, row);
187 } else {
188 rows.push((key, row));
189 }
190 }
191
192 self.set(path, rows)
193 }
194
195 async fn delete_data(&mut self, table_name: &str, keys: Vec<Key>) -> Result<()> {
196 let path = format!("{}/{}", DATA_PATH, table_name);
197 let mut rows = self.get::<Vec<(Key, DataRow)>>(&path)?.unwrap_or_default();
198
199 for key in keys.iter() {
200 if let Some(i) = rows.iter().position(|(k, _)| k == key) {
201 rows.remove(i);
202 }
203 }
204
205 self.set(path, rows)
206 }
207}
208
209impl AlterTable for WebStorage {}
210impl Index for WebStorage {}
211impl IndexMut for WebStorage {}
212impl Transaction for WebStorage {}
213impl Metadata for WebStorage {}
214impl CustomFunction for WebStorage {}
215impl CustomFunctionMut for WebStorage {}