From 2bb09cbe54bd30515edc425b548cb51717128ddd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= <tm@tlater.net>
Date: Fri, 14 Mar 2025 00:43:34 +0800
Subject: [PATCH] feat(card_db): Switch to fully-fledged diesel-based sqlite db

---
 .rustfmt.toml                                 |   1 +
 Cargo.lock                                    | 266 ++++++++++++++++--
 Cargo.toml                                    |   9 +-
 diesel.toml                                   |   9 +
 flake.nix                                     |   2 +
 .../2025-03-13-161839_create_cards/down.sql   |   1 +
 .../2025-03-13-161839_create_cards/up.sql     |   7 +
 src/components/card.rs                        |   2 +-
 src/database.rs                               |   2 +
 src/database/models.rs                        |  38 +++
 src/database/schema.rs                        |   9 +
 src/lib.rs                                    |   1 +
 src/main.rs                                   |   4 +-
 src/utils/card_database.rs                    | 158 +++--------
 14 files changed, 361 insertions(+), 148 deletions(-)
 create mode 100644 .rustfmt.toml
 create mode 100644 diesel.toml
 create mode 100644 migrations/2025-03-13-161839_create_cards/down.sql
 create mode 100644 migrations/2025-03-13-161839_create_cards/up.sql
 create mode 100644 src/database.rs
 create mode 100644 src/database/models.rs
 create mode 100644 src/database/schema.rs

diff --git a/.rustfmt.toml b/.rustfmt.toml
new file mode 100644
index 0000000..f216078
--- /dev/null
+++ b/.rustfmt.toml
@@ -0,0 +1 @@
+edition = "2024"
diff --git a/Cargo.lock b/Cargo.lock
index f850824..4a701ef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -242,6 +242,41 @@ version = "0.8.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "dashmap"
 version = "6.1.0"
@@ -267,6 +302,15 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
 [[package]]
 name = "derive-where"
 version = "1.2.7"
@@ -278,6 +322,60 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "diesel"
+version = "2.2.4"
+source = "git+https://github.com/diesel-rs/diesel.git#04e0503656e79bb8edcc5780ee3b7f36f0753361"
+dependencies = [
+ "diesel_derives",
+ "downcast-rs",
+ "libsqlite3-sys",
+ "sqlite-wasm-rs",
+ "time",
+]
+
+[[package]]
+name = "diesel-derive-enum"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81c5131a2895ef64741dad1d483f358c2a229a3a2d1b256778cdc5e146db64d4"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "diesel_derives"
+version = "2.2.0"
+source = "git+https://github.com/diesel-rs/diesel.git#04e0503656e79bb8edcc5780ee3b7f36f0753361"
+dependencies = [
+ "diesel_table_macro_syntax",
+ "dsl_auto_type",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "diesel_migrations"
+version = "2.2.0"
+source = "git+https://github.com/diesel-rs/diesel.git#64b3783f5e26349ddfbf309fea9ab35c47fc7aa4"
+dependencies = [
+ "diesel",
+ "migrations_internals",
+ "migrations_macros",
+]
+
+[[package]]
+name = "diesel_table_macro_syntax"
+version = "0.2.0"
+source = "git+https://github.com/diesel-rs/diesel.git#04e0503656e79bb8edcc5780ee3b7f36f0753361"
+dependencies = [
+ "syn",
+]
+
 [[package]]
 name = "displaydoc"
 version = "0.2.5"
@@ -289,20 +387,31 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
 [[package]]
 name = "draft-manager"
 version = "0.1.0"
 dependencies = [
  "console_error_panic_hook",
  "dedent",
+ "diesel",
+ "diesel-derive-enum",
+ "diesel_migrations",
  "futures",
- "idb",
  "leptos",
  "serde",
  "serde-wasm-bindgen",
  "serde_json",
+ "sqlite-wasm-rs",
  "thiserror 2.0.12",
  "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
 ]
 
 [[package]]
@@ -311,6 +420,19 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408"
 
+[[package]]
+name = "dsl_auto_type"
+version = "0.1.0"
+source = "git+https://github.com/diesel-rs/diesel.git#04e0503656e79bb8edcc5780ee3b7f36f0753361"
+dependencies = [
+ "darling",
+ "either",
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.19"
@@ -375,6 +497,12 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "fragile"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
+
 [[package]]
 name = "futures"
 version = "0.3.31"
@@ -548,6 +676,18 @@ version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
 
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
 [[package]]
 name = "hermit-abi"
 version = "0.3.9"
@@ -707,18 +847,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "idb"
-version = "0.6.4"
+name = "ident_case"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3afe8830d5802f769dc0be20a87f9f116798c896650cb6266eb5c19a3c109eed"
-dependencies = [
- "js-sys",
- "num-traits",
- "thiserror 1.0.69",
- "tokio",
- "wasm-bindgen",
- "web-sys",
-]
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
 [[package]]
 name = "idna"
@@ -912,6 +1044,16 @@ version = "0.2.170"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
 
+[[package]]
+name = "libsqlite3-sys"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbb8270bb4060bd76c6e96f20c52d80620f1d82a3470885694e41e0f81ef6fe7"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "linear-map"
 version = "1.2.0"
@@ -969,6 +1111,25 @@ version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
+[[package]]
+name = "migrations_internals"
+version = "2.2.0"
+source = "git+https://github.com/diesel-rs/diesel.git#64b3783f5e26349ddfbf309fea9ab35c47fc7aa4"
+dependencies = [
+ "serde",
+ "toml",
+]
+
+[[package]]
+name = "migrations_macros"
+version = "2.2.0"
+source = "git+https://github.com/diesel-rs/diesel.git#64b3783f5e26349ddfbf309fea9ab35c47fc7aa4"
+dependencies = [
+ "migrations_internals",
+ "proc-macro2",
+ "quote",
+]
+
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -1001,13 +1162,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "num-traits"
-version = "0.2.19"
+name = "num-conv"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
-]
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
 
 [[package]]
 name = "num_cpus"
@@ -1129,6 +1287,18 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
 [[package]]
 name = "prettyplease"
 version = "0.2.30"
@@ -1516,12 +1686,35 @@ version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
 
+[[package]]
+name = "sqlite-wasm-rs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84473e8335ed2f167c6f8040970d976990fb66aa62292ae58735de6d61ecfb73"
+dependencies = [
+ "fragile",
+ "js-sys",
+ "once_cell",
+ "parking_lot",
+ "thiserror 2.0.12",
+ "tokio",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
 [[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
 [[package]]
 name = "syn"
 version = "2.0.99"
@@ -1639,6 +1832,37 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "time"
+version = "0.3.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
+
+[[package]]
+name = "time-macros"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
 [[package]]
 name = "tinystr"
 version = "0.7.6"
@@ -1769,6 +1993,12 @@ dependencies = [
  "getrandom 0.3.1",
 ]
 
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
 [[package]]
 name = "version_check"
 version = "0.9.5"
diff --git a/Cargo.toml b/Cargo.toml
index fa8b96e..333117c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,10 +7,17 @@ edition = "2021"
 console_error_panic_hook = "0.1.7"
 dedent = "0.1.1"
 futures = "0.3.31"
-idb = "0.6.4"
 leptos = { version = "0.7.7", features = ["csr"] }
+diesel = { git = "https://github.com/diesel-rs/diesel.git", features = ["sqlite"] }
 serde = { version = "1.0.219", features = ["derive"] }
 serde-wasm-bindgen = "0.6.5"
 serde_json = "1.0.140"
 thiserror = "2.0.12"
 url = "2.5.4"
+wasm-bindgen = "0.2.100"
+wasm-bindgen-futures = "0.4.50"
+diesel-derive-enum = { version = "2.1.0", features = ["sqlite"] }
+diesel_migrations = { git = "https://github.com/diesel-rs/diesel.git", features = ["sqlite"] }
+
+[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
+sqlite-wasm-rs = { version = ">=0.3.0, <0.4.0" , default-features = false, features = ["precompiled"]}
diff --git a/diesel.toml b/diesel.toml
new file mode 100644
index 0000000..ba651ac
--- /dev/null
+++ b/diesel.toml
@@ -0,0 +1,9 @@
+# For documentation on how to configure this file,
+# see https://diesel.rs/guides/configuring-diesel-cli
+
+[print_schema]
+file = "src/schema.rs"
+custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
+
+[migrations_directory]
+dir = "/home/tlater/Documents/Projects/draft-manager/migrations"
diff --git a/flake.nix b/flake.nix
index 4389845..dc2446d 100644
--- a/flake.nix
+++ b/flake.nix
@@ -26,6 +26,8 @@
         packages = with pkgs; [
           cargo
           clippy
+          diesel-cli
+          emscripten
           rustc
           rustfmt
           leptosfmt
diff --git a/migrations/2025-03-13-161839_create_cards/down.sql b/migrations/2025-03-13-161839_create_cards/down.sql
new file mode 100644
index 0000000..d9a93fe
--- /dev/null
+++ b/migrations/2025-03-13-161839_create_cards/down.sql
@@ -0,0 +1 @@
+-- This file should undo anything in `up.sql`
diff --git a/migrations/2025-03-13-161839_create_cards/up.sql b/migrations/2025-03-13-161839_create_cards/up.sql
new file mode 100644
index 0000000..32fc4a6
--- /dev/null
+++ b/migrations/2025-03-13-161839_create_cards/up.sql
@@ -0,0 +1,7 @@
+CREATE TABLE cards (
+       password PRIMARY KEY,
+       name VARCHAR NOT NULL,
+       description TEXT NOT NULL,
+       frame TEXT CHECK(frame IN ('normal', 'effect', 'ritual', 'fusion', 'synchro', 'xyz', 'link', 'normal_pendulum', 'effect_pendulum', 'ritual_pendulum', 'fusion_pendulum', 'synchro_pendulum', 'xyz_pendulum', 'spell', 'trap', 'token')) NOT NULL,
+       image TEXT
+)
diff --git a/src/components/card.rs b/src/components/card.rs
index 929ec38..29fd136 100644
--- a/src/components/card.rs
+++ b/src/components/card.rs
@@ -1,6 +1,6 @@
 use leptos::prelude::*;
 
-use crate::utils::card_database::CardDetails;
+use crate::database::models::CardDetails;
 
 #[component]
 pub fn Card(details: Option<CardDetails>) -> impl IntoView {
diff --git a/src/database.rs b/src/database.rs
new file mode 100644
index 0000000..d5cbad7
--- /dev/null
+++ b/src/database.rs
@@ -0,0 +1,2 @@
+pub mod models;
+pub mod schema;
diff --git a/src/database/models.rs b/src/database/models.rs
new file mode 100644
index 0000000..f98978a
--- /dev/null
+++ b/src/database/models.rs
@@ -0,0 +1,38 @@
+use diesel::prelude::*;
+use serde::{Deserialize, Serialize};
+
+use crate::database::schema::cards;
+
+#[derive(Clone, Debug, Serialize, Deserialize, Insertable, Selectable, Queryable)]
+#[diesel(table_name = cards)]
+#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
+pub struct CardDetails {
+    pub password: i32,
+
+    pub name: String,
+    pub description: String,
+
+    pub frame: FrameType,
+    pub image: String,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, diesel_derive_enum::DbEnum)]
+#[serde(rename_all = "snake_case")]
+pub enum FrameType {
+    Normal,
+    Effect,
+    Ritual,
+    Fusion,
+    Synchro,
+    Xyz,
+    Link,
+    NormalPendulum,
+    EffectPendulum,
+    RitualPendulum,
+    FusionPendulum,
+    SynchroPendulum,
+    XyzPendulum,
+    Spell,
+    Trap,
+    Token,
+}
diff --git a/src/database/schema.rs b/src/database/schema.rs
new file mode 100644
index 0000000..6d77694
--- /dev/null
+++ b/src/database/schema.rs
@@ -0,0 +1,9 @@
+diesel::table! {
+    cards (password) {
+        password -> Integer,
+        name -> VarChar,
+        description -> Text,
+        frame -> crate::database::models::FrameTypeMapping,
+        image -> Text,
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 17388d8..4d022bc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
 pub mod components;
 pub mod draft_list;
 pub mod utils;
+pub mod database;
diff --git a/src/main.rs b/src/main.rs
index 9e3bc2f..da91295 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,8 +10,8 @@ fn main() {
 #[component]
 fn App() -> impl IntoView {
     let card = LocalResource::new(|| async move {
-        let database = CardDatabase::open().await?;
-        database.load_data().await?;
+        let mut database = CardDatabase::open()?;
+        database.load_data()?;
 
         database.get_card(1861629).await
     });
diff --git a/src/utils/card_database.rs b/src/utils/card_database.rs
index 96998eb..b18dbe8 100644
--- a/src/utils/card_database.rs
+++ b/src/utils/card_database.rs
@@ -1,145 +1,51 @@
-use std::{cell::RefCell, rc::Rc};
-
-use idb::{
-    event::VersionChangeEvent, Database, DatabaseEvent, Factory, KeyPath, ObjectStoreParams, Query,
-    TransactionMode,
-};
-
-use serde::{Deserialize, Serialize};
-use serde_wasm_bindgen::Serializer;
+use crate::database::{self, models::FrameType, schema::cards};
+use database::models::CardDetails;
+use diesel::prelude::*;
+use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
 use thiserror::Error;
 
-#[derive(Clone, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub enum FrameType {
-    Normal,
-    Effect,
-    Ritual,
-    Fusion,
-    Synchro,
-    Xyz,
-    Link,
-    NormalPendulum,
-    EffectPendulum,
-    RitualPendulum,
-    FusionPendulum,
-    SynchroPendulum,
-    XyzPendulum,
-    Spell,
-    Trap,
-    Token,
-}
+const DB_URL: &str = "cards.db";
+const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
 
-#[derive(Clone, Deserialize)]
-pub struct CardDetails {
-    pub password: u32,
-
-    pub name: String,
-    pub description: String,
-
-    pub frame: FrameType,
-    pub image: String,
-}
-
-pub struct CardDatabase(Database);
-
-const DB_VERSION: u32 = 1;
+pub struct CardDatabase(SqliteConnection);
 
 impl CardDatabase {
-    pub async fn open() -> Result<Self> {
-        let factory = Factory::new()?;
-        let mut open_request = factory.open("cards", Some(DB_VERSION))?;
-
-        let err = Rc::new(RefCell::new(Ok(())));
-        let moved_err = err.clone(); // Will be moved inside closure
-        open_request.on_upgrade_needed(move |event| {
-            let res = Self::upgrade_database(event);
-
-            if let Err(e) = res {
-                *std::cell::RefCell::<_>::borrow_mut(&moved_err) = Err(e);
-                drop(moved_err);
-            }
-        });
-
-        let database = open_request.await?;
-        // Get the error if there is one - note that if the Rc has a
-        // strong count > 1 and therefore Symbol’s value as variable is void: into_inner returns Symbol’s value as variable is void: None,
-        // this means that the callback was not called, and as such
-        // there cannot have been an error.
-        let res = Rc::into_inner(err).unwrap_or(Ok(()).into()).into_inner();
-
-        res.map(|_| Self(database))
+    pub fn open() -> Result<Self> {
+        let mut connection = SqliteConnection::establish(DB_URL)?;
+        connection.run_pending_migrations(MIGRATIONS).unwrap();
+        Ok(Self(connection))
     }
 
-    pub async fn get_card(&self, key: u32) -> Result<Option<CardDetails>> {
-        let transaction = self.0.transaction(&["cards"], TransactionMode::ReadOnly)?;
-        let store = transaction.object_store("cards")?;
-        let card = store.get(Query::Key(key.into()))?.await?;
-        Ok(card.map(serde_wasm_bindgen::from_value).transpose()?)
+    pub async fn get_card(&mut self, password: i32) -> Result<Option<CardDetails>> {
+        match cards::dsl::cards.find(password).first(&mut self.0) {
+            Ok(details) => Ok(Some(details)),
+            Err(diesel::result::Error::NotFound) => Ok(None),
+            Err(other) => Err(other.into()),
+        }
     }
 
-    pub async fn load_data(&self) -> Result<()> {
-        let transaction = self.0.transaction(&["cards"], TransactionMode::ReadWrite)?;
-        let store = transaction.object_store("cards")?;
-
-        // Add some test data for now
-        let decode_talker = serde_json::json!({
-            "password": 1861629,
-            "name": "Decode Talker",
-            "description": "2+ Effect Monsters\r\nGains 500 ATK for each monster it points to. When your opponent activates a card or effect that targets a card(s) you control (Quick Effect): You can Tribute 1 monster this card points to; negate the activation, and if you do, destroy that card.",
-            "frame": "link",
-            "image": "https://images.ygoprodeck.com/images/cards/1861629.jpg"
-        });
-
-        store
-            .put(
-                &decode_talker.serialize(&Serializer::json_compatible())?,
-                None,
-            )?
-            .await?;
-
-        transaction.commit()?.await?;
-
-        Ok(())
-    }
-
-    /// Upgrade the database; this is called by the browser whenever
-    /// we try to open a newer database version.
-    fn upgrade_database(event: VersionChangeEvent) -> Result<()> {
-        let database = event.database()?;
-
-        let upgrade_from = event.old_version()?;
-        match upgrade_from {
-            // This version number signals that the database
-            // hasn't been created before.
-            0 => {
-                let mut store_params = ObjectStoreParams::new();
-                store_params.key_path(Some(KeyPath::new_single("password")));
-
-                let store = database.create_object_store("cards", store_params)?;
-
-                store.create_index(
-                    "fulltext",
-                    KeyPath::new_array(vec!["password", "name", "description"]),
-                    None,
-                )?;
-            }
-            other => return Err(CardDatabaseError::UnknownDatabaseVersion(other)),
+    pub fn load_data(&mut self) -> Result<()> {
+        let decode_talker = CardDetails {
+            password: 1861629,
+            name: "Decode Talker".to_owned(),
+            description: "2+ Effect Monsters\r\nGains 500 ATK for each monster it points to. When your opponent activates a card or effect that targets a card(s) you control (Quick Effect): You can Tribute 1 monster this card points to; negate the activation, and if you do, destroy that card.".to_owned(),
+            frame: FrameType::Link,
+            image: "https://images.ygoprodeck.com/images/cards/1861629.jpg".to_owned()
         };
 
+        diesel::insert_into(cards::table)
+            .values(decode_talker)
+            .execute(&mut self.0)?;
+
         Ok(())
     }
 }
 
 #[derive(Debug, Error)]
 pub enum CardDatabaseError {
-    #[error(
-        "unknown card database version `{0}`, please close the tab and re-open the application"
-    )]
-    UnknownDatabaseVersion(u32),
-    #[error("card database operation failed; does the browser support IndexedDB?")]
-    BrowserError(#[from] idb::Error),
-    #[error("invalid test data")]
-    SerdeError(#[from] serde_wasm_bindgen::Error),
+    #[error("failed to connect to database")]
+    DieselConnectionError(#[from] diesel::ConnectionError),
+    #[error("failed to query database")]
+    DieselQueryError(#[from] diesel::result::Error),
 }
 type Result<T> = std::result::Result<T, CardDatabaseError>;