idb_sys/transaction/
mod.rs

1mod transaction_mode;
2
3pub use self::transaction_mode::TransactionMode;
4
5use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
6use web_sys::{DomException, Event, EventTarget, IdbTransaction};
7
8use crate::{utils::dom_string_list_to_vec, Database, Error, ObjectStore};
9
10/// Provides a static, asynchronous transaction on a database. All reading and writing of data is done within
11/// transactions.
12#[derive(Debug)]
13pub struct Transaction {
14    inner: IdbTransaction,
15    abort_callback: Option<Closure<dyn FnMut(Event)>>,
16    complete_callback: Option<Closure<dyn FnMut(Event)>>,
17    error_callback: Option<Closure<dyn FnMut(Event)>>,
18}
19
20impl Transaction {
21    /// Returns a list of the names of object stores in the transaction’s scope. For an upgrade transaction this is all
22    /// object stores in the database.
23    pub fn store_names(&self) -> Vec<String> {
24        dom_string_list_to_vec(&self.inner.object_store_names())
25    }
26
27    /// Returns the mode the transaction was created with ("readonly" or "readwrite"), or "versionchange" for an upgrade
28    /// transaction.
29    pub fn mode(&self) -> Result<TransactionMode, Error> {
30        self.inner
31            .mode()
32            .map_err(Error::TransactionModeNotFound)?
33            .try_into()
34    }
35
36    /// Returns the transaction’s connection.
37    pub fn database(&self) -> Database {
38        self.inner.db().into()
39    }
40
41    /// If the transaction was aborted, returns the error (a `DOMException`) providing the reason.
42    pub fn error(&self) -> Option<DomException> {
43        self.inner.error()
44    }
45
46    /// Returns an [`ObjectStore`] in the transaction's scope.
47    pub fn object_store(&self, name: &str) -> Result<ObjectStore, Error> {
48        self.inner
49            .object_store(name)
50            .map(Into::into)
51            .map_err(Error::ObjectStoreNotFound)
52    }
53
54    /// Attempts to commit the transaction. All pending requests will be allowed to complete, but no new requests will
55    /// be accepted. This can be used to force a transaction to quickly finish, without waiting for pending requests to
56    /// fire success events before attempting to commit normally.
57    pub fn commit(&self) -> Result<(), Error> {
58        self.inner.commit().map_err(Error::TransactionCommitError)
59    }
60
61    /// Aborts the transaction. All pending requests will fail and all changes made to the database will be reverted.
62    pub fn abort(&self) -> Result<(), Error> {
63        self.inner.abort().map_err(Error::TransactionAbortError)
64    }
65
66    /// Adds an event handler for `abort` event.
67    pub fn on_abort<F>(&mut self, callback: F)
68    where
69        F: FnOnce(Event) + 'static,
70    {
71        let closure = Closure::once(callback);
72        self.inner
73            .set_onabort(Some(closure.as_ref().unchecked_ref()));
74        self.abort_callback = Some(closure);
75    }
76
77    /// Adds an event handler for `complete` event.
78    pub fn on_complete<F>(&mut self, callback: F)
79    where
80        F: FnOnce(Event) + 'static,
81    {
82        let closure = Closure::once(callback);
83        self.inner
84            .set_oncomplete(Some(closure.as_ref().unchecked_ref()));
85        self.complete_callback = Some(closure);
86    }
87
88    /// Adds an event handler for `error` event.
89    pub fn on_error<F>(&mut self, callback: F)
90    where
91        F: FnOnce(Event) + 'static,
92    {
93        let closure = Closure::once(callback);
94        self.inner
95            .set_onerror(Some(closure.as_ref().unchecked_ref()));
96        self.error_callback = Some(closure);
97    }
98}
99
100impl TryFrom<EventTarget> for Transaction {
101    type Error = Error;
102
103    fn try_from(target: EventTarget) -> Result<Self, Self::Error> {
104        let target: JsValue = target.into();
105        target
106            .dyn_into::<IdbTransaction>()
107            .map(Into::into)
108            .map_err(|value| Error::UnexpectedJsType("IdbTransaction", value))
109    }
110}
111
112impl From<IdbTransaction> for Transaction {
113    fn from(inner: IdbTransaction) -> Self {
114        Self {
115            inner,
116            abort_callback: None,
117            complete_callback: None,
118            error_callback: None,
119        }
120    }
121}
122
123impl From<Transaction> for IdbTransaction {
124    fn from(transaction: Transaction) -> Self {
125        transaction.inner
126    }
127}
128
129impl TryFrom<JsValue> for Transaction {
130    type Error = Error;
131
132    fn try_from(value: JsValue) -> Result<Self, Self::Error> {
133        value
134            .dyn_into::<IdbTransaction>()
135            .map(Into::into)
136            .map_err(|value| Error::UnexpectedJsType("IdbTransaction", value))
137    }
138}
139
140impl From<Transaction> for JsValue {
141    fn from(value: Transaction) -> Self {
142        value.inner.into()
143    }
144}