feat(card_db): Import card database from edopro GitHub repo

This commit is contained in:
Tristan Daniël Maat 2025-03-15 03:02:31 +08:00
parent 88da43e208
commit a3ce7f8008
Signed by: tlater
GPG key ID: 49670FD774E43268
14 changed files with 691 additions and 108 deletions

295
Cargo.lock generated
View file

@ -122,6 +122,15 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.9.0"
@ -403,15 +412,22 @@ dependencies = [
"diesel-derive-enum",
"diesel_migrations",
"futures",
"futures-util",
"gloo",
"gloo-net 0.6.0",
"leptos",
"log",
"serde",
"serde-wasm-bindgen",
"serde_json",
"sqlite-wasm-rs",
"thiserror 2.0.12",
"thiserror-ext",
"time",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-logger",
]
[[package]]
@ -624,6 +640,109 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gloo"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d15282ece24eaf4bd338d73ef580c6714c8615155c4190c781290ee3fa0fd372"
dependencies = [
"gloo-console",
"gloo-dialogs",
"gloo-events",
"gloo-file",
"gloo-history",
"gloo-net 0.5.0",
"gloo-render",
"gloo-storage",
"gloo-timers",
"gloo-utils",
"gloo-worker",
]
[[package]]
name = "gloo-console"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261"
dependencies = [
"gloo-utils",
"js-sys",
"serde",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-dialogs"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4748e10122b01435750ff530095b1217cf6546173459448b83913ebe7815df"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-events"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-file"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f"
dependencies = [
"futures-channel",
"gloo-events",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-history"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6"
dependencies = [
"getrandom 0.2.15",
"gloo-events",
"gloo-utils",
"serde",
"serde-wasm-bindgen",
"serde_urlencoded",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173"
dependencies = [
"futures-channel",
"futures-core",
"futures-sink",
"gloo-utils",
"http 0.2.12",
"js-sys",
"pin-project",
"serde",
"serde_json",
"thiserror 1.0.69",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.6.0"
@ -634,7 +753,7 @@ dependencies = [
"futures-core",
"futures-sink",
"gloo-utils",
"http",
"http 1.2.0",
"js-sys",
"pin-project",
"serde",
@ -645,6 +764,43 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gloo-render"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56008b6744713a8e8d98ac3dcb7d06543d5662358c9c805b4ce2167ad4649833"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-storage"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a"
dependencies = [
"gloo-utils",
"js-sys",
"serde",
"serde_json",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-timers"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "gloo-utils"
version = "0.2.0"
@ -658,6 +814,37 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gloo-worker"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "085f262d7604911c8150162529cefab3782e91adb20202e8658f7275d2aefe5d"
dependencies = [
"bincode",
"futures",
"gloo-utils",
"gloo-worker-macros",
"js-sys",
"pinned",
"serde",
"thiserror 1.0.69",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "gloo-worker-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "guardian"
version = "1.3.0"
@ -703,6 +890,17 @@ dependencies = [
"utf8-width",
]
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http"
version = "1.2.0"
@ -1287,6 +1485,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pinned"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
dependencies = [
"futures",
"rustversion",
"thiserror 1.0.69",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
@ -1309,6 +1518,16 @@ dependencies = [
"syn",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
@ -1608,6 +1827,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "server_fn"
version = "0.7.7"
@ -1618,8 +1849,8 @@ dependencies = [
"const_format",
"dashmap",
"futures",
"gloo-net",
"http",
"gloo-net 0.6.0",
"http 1.2.0",
"js-sys",
"once_cell",
"pin-project-lite",
@ -1801,6 +2032,28 @@ dependencies = [
"thiserror-impl 2.0.12",
]
[[package]]
name = "thiserror-ext"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4323942237f7cc071061f2c5f0db919e6053c2cdf58c6bc974883073429737"
dependencies = [
"thiserror 1.0.69",
"thiserror-ext-derive",
]
[[package]]
name = "thiserror-ext-derive"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96541747c50e6c73e094737938f4f5dfaf50c48a31adff4197a3e2a481371674"
dependencies = [
"either",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
@ -1892,7 +2145,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
"toml_edit 0.22.24",
]
[[package]]
@ -1904,6 +2157,17 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.22.24"
@ -1914,7 +2178,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
"winnow 0.7.3",
]
[[package]]
@ -2063,6 +2327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
dependencies = [
"cfg-if",
"futures-core",
"js-sys",
"once_cell",
"wasm-bindgen",
@ -2101,6 +2366,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-logger"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074649a66bb306c8f2068c9016395fa65d8e08d2affcbf95acf3c24c3ab19718"
dependencies = [
"log",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
@ -2206,6 +2482,15 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.7.3"

View file

@ -15,9 +15,14 @@ serde_json = "1.0.140"
thiserror = "2.0.12"
url = "2.5.4"
wasm-bindgen = "0.2.100"
wasm-bindgen-futures = "0.4.50"
wasm-bindgen-futures = { version = "0.4.50", features = ["futures-core-03-stream"] }
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]
time = { version = "0.3.39", features = ["parsing"] }
wasm-logger = "0.2.0"
log = "0.4.26"
sqlite-wasm-rs = { version = ">=0.3.0, <0.4.0" , default-features = false, features = ["precompiled"]}
thiserror-ext = "0.2.1"
futures-util = "0.3.31"
gloo = { version = "0.11.0", features = ["futures"] }
gloo-net = { version = "0.6.0" }

View file

@ -1,5 +1,8 @@
<!DOCTYPE html>
<html>
<head></head>
<head>
<link data-trunk rel="rust" data-type="worker" data-bin="database_worker" data-weak-refs data-loader-shim />
<link data-trunk rel="rust" data-bin="draft-manager" data-weak-refs />
</head>
<body></boy>
</html>

View file

@ -1 +0,0 @@
-- This file should undo anything in `up.sql`

View file

@ -1,7 +0,0 @@
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
)

View file

@ -0,0 +1,14 @@
use draft_manager::database::BrowserDatabase;
fn main() {
console_error_panic_hook::set_once();
wasm_logger::init(wasm_logger::Config::default());
wasm_bindgen_futures::spawn_local(async {
let a = BrowserDatabase::init_worker().await;
if let Err(e) = a {
log::error!("{:?}", e);
}
})
}

View file

@ -1,13 +1,13 @@
use leptos::prelude::*;
use crate::database::models::CardDetails;
use crate::database::models::CardData;
#[component]
pub fn Card(details: Option<CardDetails>) -> impl IntoView {
pub fn Card(details: Option<CardData>) -> impl IntoView {
match details {
Some(details) => view! {
<p>{details.name}</p>
<span style="white-space: pre-line">{details.description}</span>
<p>{details.id}</p>
<span style="white-space: pre-line">{details.level}</span>
}
.into_any(),
None => view! { <p>"Placeholder"</p> }.into_any(),

View file

@ -1,54 +1,60 @@
use crate::database::{self, models::FrameType, schema::cards};
use database::models::CardDetails;
use diesel::{prelude::*, Connection};
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use thiserror::Error;
use gloo::worker::{Registrable, Spawnable, WorkerBridge};
pub mod models;
pub mod schema;
mod worker;
const DB_URL: &str = "cards.db";
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
use sqlite_wasm_rs::export::{install_opfs_sahpool, OpfsSAHError};
use thiserror_ext::AsReport;
use worker::{DatabaseWorker, Query};
pub struct BrowserDatabase(SqliteConnection);
use crate::utils::github_fetch_file;
pub struct BrowserDatabase {
worker: WorkerBridge<DatabaseWorker>,
}
impl BrowserDatabase {
pub fn open() -> Result<Self> {
let mut connection = SqliteConnection::establish(DB_URL)?;
connection.run_pending_migrations(MIGRATIONS).unwrap();
Ok(Self(connection))
pub fn new() -> Self {
let bridge = DatabaseWorker::spawner()
.callback(move |o| {
log::info!("{:?}", o);
})
.spawn_with_loader("/database_worker_loader.js");
log::debug!("Shoulda spawned a worker");
Self { worker: bridge }
}
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 fn send_message(&self) {
self.worker.send(Query::ByPassword(1861629));
}
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()
/// Initialize the database worker.
///
/// Must be executed in a worker script, not on the main thread.
pub async fn init_worker() -> Result<(), OpfsSAHError> {
log::debug!("Initializing database worker...");
let sah_pool_util = install_opfs_sahpool(None, false).await?;
let babel_cdb = match github_fetch_file("ProjectIgnis", "BabelCDB", "cards.cdb").await {
Ok(cdb) => cdb,
Err(err) => {
log::error!("{}", err.to_report_string());
panic!("{:?}", err);
}
};
diesel::insert_into(cards::table)
.values(decode_talker)
.execute(&mut self.0)?;
sah_pool_util.import_db("/cards.db", &babel_cdb)?;
DatabaseWorker::registrar().register();
log::debug!("Database worker set up!");
Ok(())
}
}
#[derive(Debug, Error)]
pub enum BrowserDatabaseError {
#[error("failed to connect to database")]
DieselConnectionError(#[from] diesel::ConnectionError),
#[error("failed to query database")]
DieselQueryError(#[from] diesel::result::Error),
impl Default for BrowserDatabase {
fn default() -> Self {
Self::new()
}
}
type Result<T> = std::result::Result<T, BrowserDatabaseError>;

View file

@ -1,38 +1,79 @@
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use crate::database::schema::cards;
use crate::database::schema::datas;
use crate::database::schema::texts;
#[derive(Clone, Debug, Serialize, Deserialize, Insertable, Selectable, Queryable)]
#[diesel(table_name = cards)]
#[derive(Debug, Selectable, Queryable)]
#[diesel(table_name = datas)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
pub struct CardData {
pub id: i32,
pub ot: i32,
pub alias: i32,
pub setcode: i32,
pub card_type: i32,
pub atk: i32,
pub def: i32,
pub level: i32,
pub race: i32,
pub attribute: i32,
pub category: i32,
}
#[derive(Debug, Associations, Selectable, Queryable)]
#[diesel(table_name = texts)]
#[diesel(belongs_to(CardData, foreign_key = id))]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
pub struct CardTexts {
pub id: i32,
pub name: String,
pub desc: String,
pub str1: String,
pub str2: String,
pub str3: String,
pub str4: String,
pub str5: String,
pub str6: String,
pub str7: String,
pub str8: String,
pub str9: String,
pub str10: String,
pub str11: String,
pub str12: String,
pub str13: String,
pub str14: String,
pub str15: String,
pub str16: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CardDetails {
pub password: i32,
pub name: String,
pub description: String,
pub frame: FrameType,
pub image: String,
pub desc: String,
pub card_type: i32,
pub atk: i32,
pub def: i32,
pub level: i32,
pub race: i32,
pub attribute: i32,
pub category: i32,
}
#[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,
impl CardDetails {
pub fn from_data(data: CardData, texts: CardTexts) -> Self {
Self {
password: data.id,
name: texts.name,
desc: texts.desc,
card_type: data.card_type,
atk: data.atk,
def: data.def,
level: data.level,
race: data.race,
attribute: data.attribute,
category: data.category,
}
}
}

View file

@ -1,9 +1,40 @@
diesel::table! {
cards (password) {
password -> Integer,
name -> VarChar,
description -> Text,
frame -> crate::database::models::FrameTypeMapping,
image -> Text,
datas {
id -> Integer,
ot -> Integer,
alias -> Integer,
setcode -> Integer,
#[sql_name = "type"]
card_type -> Integer,
atk -> Integer,
def -> Integer,
level -> Integer,
race -> Integer,
attribute -> Integer,
category -> Integer,
}
}
diesel::table! {
texts {
id -> Integer,
name -> Text,
desc -> Text,
str1 -> Text,
str2 -> Text,
str3 -> Text,
str4 -> Text,
str5 -> Text,
str6 -> Text,
str7 -> Text,
str8 -> Text,
str9 -> Text,
str10 -> Text,
str11 -> Text,
str12 -> Text,
str13 -> Text,
str14 -> Text,
str15 -> Text,
str16 -> Text,
}
}

140
src/database/worker.rs Normal file
View file

@ -0,0 +1,140 @@
use diesel::{prelude::*, Connection};
use futures::TryFutureExt;
use gloo::worker::{HandlerId, Worker, WorkerScope};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use thiserror_ext::AsReport;
use wasm_bindgen_futures::spawn_local;
use super::{
models::CardDetails,
schema::{datas, texts},
};
const DB_URL: &str = "file:/cards.db?vfs=opfs-sahpool&mode=ro";
#[derive(Serialize, Deserialize)]
pub enum Query {
ByPassword(i32),
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Response {
ByPassword(Option<CardDetails>),
Error(String),
}
pub struct DatabaseWorker {}
impl DatabaseWorker {
async fn run_query(query: &Query) -> Result<Response> {
let res = match query {
Query::ByPassword(pw) => Response::ByPassword(Self::get_card(*pw).await?),
};
Ok(res)
}
/// Open a connection to the database.
///
/// We do not hold a connection in memory because that requires
/// lots of awkward multi-thread data management.
fn open_connection() -> Result<SqliteConnection> {
let connection = SqliteConnection::establish(DB_URL)?;
Ok(connection)
}
async fn get_card(password: i32) -> Result<Option<CardDetails>> {
let mut connection = Self::open_connection()?;
let res = {
let data = datas::dsl::datas.find(password).first(&mut connection)?;
let texts = texts::dsl::texts.find(password).first(&mut connection)?;
Ok((data, texts))
};
let Some((data, texts)) = match res {
Ok(details) => Ok(Some(details)),
Err(diesel::result::Error::NotFound) => Ok(None),
Err(other) => Err(other),
}?
else {
return Ok(None);
};
// match datas::dsl::datas.find(password).first(&mut connection) {
// Ok(details) => Ok(Some(details)),
// Err(diesel::result::Error::NotFound) => Ok(None),
// Err(other) => Err(other.into()),
// };
Ok(Some(CardDetails::from_data(data, texts)))
}
// pub async fn load_card_data(&mut self) -> Result<()> {
// let last_updated =
// utils::github_fetch_last_updated("ProjectIgnis", "BabelCDB", "cards.cdb").await?;
// if last_updated > time::OffsetDateTime::UNIX_EPOCH {
// let card_data =
// utils::github_fetch_file("ProjectIgnis", "BabelCDB", "cards.cdb").await?;
// self.opfs_util.import_db("cards.cdb", &card_data)?;
// }
// 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.connection)?;
// Ok(())
// }
}
impl Worker for DatabaseWorker {
type Input = Query;
type Message = ();
type Output = std::result::Result<Response, String>;
fn create(_scope: &WorkerScope<Self>) -> Self {
Self {}
}
fn update(&mut self, _scope: &WorkerScope<Self>, _msg: Self::Message) {
log::debug!("Unimplemented!");
}
fn received(&mut self, scope: &WorkerScope<Self>, query: Self::Input, id: HandlerId) {
let scope = scope.clone();
spawn_local(async move {
let res = DatabaseWorker::run_query(&query)
.map_err(|err| {
log::error!("{}", err.to_report_string_pretty());
format!("{}", err.as_report())
})
.await;
scope.respond(id, res);
})
}
}
#[derive(Debug, Error)]
pub enum BrowserDatabaseError {
// #[error("failed to set up browser database storage")]
// VFSError(#[from] sqlite_wasm_rs::export::OpfsSAHError),
// #[error("could not fetch updated card database")]
// CardDbFetchError(#[from] GithubError),
#[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, BrowserDatabaseError>;

View file

@ -1,3 +1,4 @@
pub mod components;
pub mod draft_list;
pub mod database;
pub mod draft_list;
pub mod utils;

View file

@ -1,32 +1,37 @@
use draft_manager::{components::card::Card, database::BrowserDatabase};
use std::time::Duration;
use draft_manager::database::BrowserDatabase;
use leptos::prelude::*;
use wasm_bindgen_futures::spawn_local;
fn main() {
console_error_panic_hook::set_once();
wasm_logger::init(wasm_logger::Config::default());
log::debug!("Startin app database");
let db = BrowserDatabase::new();
log::debug!("Database initialized");
leptos::mount::mount_to_body(|| view! { <App /> });
spawn_local(async move {
let db = db;
log::info!("Running loop!");
db.send_message();
// We create a loop so that listeners can be held for forever.
loop {
gloo::timers::future::sleep(Duration::from_secs(3600)).await;
}
});
}
#[component]
fn App() -> impl IntoView {
let card = LocalResource::new(|| async move {
let mut database = BrowserDatabase::open()?;
database.load_data()?;
database.get_card(1861629).await
});
view! {
<Transition fallback=move || {
view! { <p>"Loading database..."</p> }
}>
{move || {
card.read()
.as_deref()
.and_then(|card| {
view! { <Card details=card.as_ref().unwrap().clone() /> }.into()
})
}}
</Transition>
}>{move || {}}</Transition>
}
}

60
src/utils.rs Normal file
View file

@ -0,0 +1,60 @@
use gloo::net::http;
use thiserror::Error;
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
const GITHUB_API: &str = "https://api.github.com";
pub async fn github_fetch_last_updated(
owner: &str,
repo: &str,
path: &str,
) -> Result<OffsetDateTime, GithubError> {
let commit: Vec<serde_json::Value> = http::Request::get(&format!(
"{GITHUB_API}/repos/{owner}/{repo}/commits?path={path}&page=1&per_page=1"
))
.send()
.await?
.json()
.await?;
commit[0]
.get("commit")
.and_then(|commit| commit.get("committer"))
.and_then(|committer| committer.get("date"))
.map(|date| {
OffsetDateTime::parse(
date.as_str().ok_or(GithubError::MissingCommitDateError)?,
&Rfc3339,
)
.map_err(|err| err.into())
})
.unwrap_or(Err(GithubError::MissingCommitDateError))
}
pub async fn github_fetch_file(
owner: &str,
repo: &str,
path: &str,
) -> Result<Vec<u8>, GithubError> {
http::Request::get(&format!(
"{GITHUB_API}/repos/{owner}/{repo}/contents/{path}"
))
.header("Accept", "application/vnd.github.raw+json")
.send()
.await?
.binary()
.await
.map_err(|err| err.into())
}
#[derive(Debug, Error)]
pub enum GithubError {
#[error("no file returned from the given GitHub API")]
NoSuchFile,
#[error("failed to fetch from GitHub API")]
RequestError(#[from] gloo::net::Error),
#[error("file fetched from the GitHub API does not have a commit date")]
MissingCommitDateError,
#[error("date/time format reported for GitHub commit is invalid")]
InvalidDateTimeError(#[from] time::error::Parse),
}