gluesql_web_storage/
lib.rs

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
21/// gluesql-schema-names -> {Vec<String>}
22const TABLE_NAMES_PATH: &str = "gluesql-schema-names";
23
24/// gluesql-schema/{schema_name} -> {Schema}
25const SCHEMA_PATH: &str = "gluesql-schema";
26
27/// gluesql-data/{table_name} -> {Vec<DataRow>}
28const 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 {}