1use js_sys::Array;
2use num_traits::ToPrimitive;
3use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
4use web_sys::{Event, EventTarget, IdbDatabase};
5
6use crate::{
7 utils::dom_string_list_to_vec, Error, ObjectStore, ObjectStoreParams, Transaction,
8 TransactionMode,
9};
10
11#[derive(Debug)]
15pub struct Database {
16 inner: IdbDatabase,
17 abort_callback: Option<Closure<dyn FnMut(Event)>>,
18 close_callback: Option<Closure<dyn FnMut(Event)>>,
19 error_callback: Option<Closure<dyn FnMut(Event)>>,
20 version_change_callback: Option<Closure<dyn FnMut(Event)>>,
21}
22
23impl Database {
24 pub fn name(&self) -> String {
26 self.inner.name()
27 }
28
29 pub fn version(&self) -> Result<u32, Error> {
31 self.inner
32 .version()
33 .to_u32()
34 .ok_or(Error::NumberConversionError)
35 }
36
37 pub fn store_names(&self) -> Vec<String> {
39 dom_string_list_to_vec(&self.inner.object_store_names())
40 }
41
42 pub fn transaction<T>(
45 &self,
46 store_names: &[T],
47 mode: TransactionMode,
48 ) -> Result<Transaction, Error>
49 where
50 T: AsRef<str>,
51 {
52 let store_names: Array = store_names
53 .iter()
54 .map(|s| JsValue::from(s.as_ref()))
55 .collect();
56
57 self.inner
58 .transaction_with_str_sequence_and_mode(&store_names, mode.into())
59 .map(Into::into)
60 .map_err(Error::TransactionOpenFailed)
61 }
62
63 pub fn close(&self) {
65 self.inner.close()
66 }
67
68 pub fn create_object_store(
71 &self,
72 name: &str,
73 params: ObjectStoreParams,
74 ) -> Result<ObjectStore, Error> {
75 self.inner
76 .create_object_store_with_optional_parameters(name, ¶ms.into())
77 .map(Into::into)
78 .map_err(Error::ObjectStoreCreateFailed)
79 }
80
81 pub fn delete_object_store(&self, name: &str) -> Result<(), Error> {
83 self.inner
84 .delete_object_store(name)
85 .map_err(Error::ObjectStoreDeleteFailed)
86 }
87
88 pub fn on_abort<F>(&mut self, callback: F)
90 where
91 F: FnOnce(Event) + 'static,
92 {
93 let closure = Closure::once(callback);
94 self.inner
95 .set_onabort(Some(closure.as_ref().unchecked_ref()));
96 self.abort_callback = Some(closure);
97 }
98
99 pub fn on_close<F>(&mut self, callback: F)
101 where
102 F: FnOnce(Event) + 'static,
103 {
104 let closure = Closure::once(callback);
105 self.inner
106 .set_onclose(Some(closure.as_ref().unchecked_ref()));
107 self.close_callback = Some(closure);
108 }
109
110 pub fn on_error<F>(&mut self, callback: F)
112 where
113 F: FnOnce(Event) + 'static,
114 {
115 let closure = Closure::once(callback);
116 self.inner
117 .set_onerror(Some(closure.as_ref().unchecked_ref()));
118 self.error_callback = Some(closure);
119 }
120
121 pub fn on_version_change<F>(&mut self, callback: F)
123 where
124 F: FnOnce(Event) + 'static,
125 {
126 let closure = Closure::once(callback);
127 self.inner
128 .set_onversionchange(Some(closure.as_ref().unchecked_ref()));
129 self.version_change_callback = Some(closure);
130 }
131}
132
133impl TryFrom<EventTarget> for Database {
134 type Error = Error;
135
136 fn try_from(target: EventTarget) -> Result<Self, Self::Error> {
137 let target: JsValue = target.into();
138 target
139 .dyn_into::<IdbDatabase>()
140 .map(Into::into)
141 .map_err(|value| Error::UnexpectedJsType("IdbDatabase", value))
142 }
143}
144
145impl From<IdbDatabase> for Database {
146 fn from(inner: IdbDatabase) -> Self {
147 Self {
148 inner,
149 abort_callback: None,
150 close_callback: None,
151 error_callback: None,
152 version_change_callback: None,
153 }
154 }
155}
156
157impl From<Database> for IdbDatabase {
158 fn from(database: Database) -> Self {
159 database.inner
160 }
161}
162
163impl TryFrom<JsValue> for Database {
164 type Error = Error;
165
166 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
167 value
168 .dyn_into::<IdbDatabase>()
169 .map(Into::into)
170 .map_err(|value| Error::UnexpectedJsType("IdbDatabase", value))
171 }
172}
173
174impl From<Database> for JsValue {
175 fn from(value: Database) -> Self {
176 value.inner.into()
177 }
178}