Files
mtgc/src-tauri/src/lib.rs

111 lines
3.8 KiB
Rust
Raw Normal View History

mod commands;
Add a News tab: set releases and links to Wizards' articles The app knew nothing about the game outside your own collection — no way to see that a set drops next week, or that a Secret Lair was announced. Two sources with very different reliability, kept apart so one failing can't take the page down. Releases come from Scryfall's documented /sets endpoint. Clicking a set fetches its cards live rather than reading reference.db, so a set released or spoiled since the last card-data download still works; ownership is joined locally by oracle_id, and the same add-to-library stepper works there as anywhere else. Articles have no supported source at all: Wizards publishes no RSS or Atom feed and no public API, and their page is a Nuxt app whose CSS classes are per-build hashes. So the app links out rather than reproducing anyone's writing, which keeps the scraped surface to titles and hrefs. parse_articles keys on nothing cosmetic — anchors pointing at /en/news/<category>/<slug>, taking the anchor's own text as the title. A restyle can't break that; only a change to their URL scheme could, and the fixture tests fail loudly when it does rather than the page quietly going empty. Verified against a live capture: 15 articles, correct titles and categories. Two things the fixtures caught. Where an article is linked several times per card, the first link is a "Learn More" button, so the longest text wins rather than the first. And a *successful* fetch yielding zero articles is treated as a failure, since that is the signature of their markup changing. get_set_cards returns a full CardRef rather than the handful of fields the grid needs, so the tab renders the same CardTile as the rest of the app and gets click-to-preview instead of a lookalike that does nothing when clicked. Both feeds cache to news-cache.json for six hours and fall back to whatever was last seen, so opening the tab offline shows stale content with its age rather than an error. Also promotes the HTTP agent out of ingest.rs into a shared http module, so timeouts and User-Agent can't drift between the ingester and anything else that reaches the network. Adds tauri-plugin-opener for opening articles externally.
2026-08-07 23:35:50 -05:00
mod news;
mod state;
Rework library sync around a JSON document; add card filters Sync The previous design put the merge inside SQL: timestamp-stamping triggers, a tombstone table, per-device SQLite snapshots, a device id in the database, and row merges written as correlated-subquery UPDATEs. All of that schema was inherited by desktop, and the merge was very hard to verify. Replace it with a single JSON document plus a three-way merge in plain Rust. Storing the last-read remote document as the merge base makes "who changed what" and "what was deleted" derivable, which removes the tombstone table, the updated_at columns and all eight triggers. It also makes a single shared file safe: if a publish is lost the remote is unchanged, so the next merge re-detects the local edits and republishes rather than reading their absence as a deletion. That removes the need for per-device files and for device identity entirely. Schema impact drops to one column, decks.uuid. Conflicts resolve to "theirs", which is required for convergence rather than chosen for taste — preferring ours has each device re-asserting its own value forever. Every such choice is reported to the user. SQLite becomes invisible: the working copy lives in the app data dir and the user only ever names a .json file. There is always a document, defaulting to private storage, so edits are never stranded with nowhere to be saved. Pointing at a file that doesn't exist yet moves the current library there; pointing at one that does adopts it. A converter brings a pre-document library.db across. Android reaches the folder through the Storage Access Framework rather than MANAGE_EXTERNAL_STORAGE, so no storage permission is declared at all. Filters Track card origin in reference.db: Universes Beyond, digital-only and Un-set cards, plus numeric power/toughness. Each origin flag is derived as "every printing", not "any printing" — ordinary cards get reprinted into those sets, and Lightning Bolt appearing in Marvel Commander must not hide it. Non-numeric power ("*", "1+*") stays NULL so it drops out of range filters instead of being compared as zero. Also wire up filters over data already present: price range, format legality (restricted counts as legal) and "can be a commander". All of these degrade silently against an older reference.db rather than failing every query. The shared criteria live in one component used by both filter panels, as a compact row of icon toggles. Card Search gains the Library tab's bulk selection: checkboxes in both views, select-all, and a bulk add-to-deck with a single undo for the whole batch.
2026-08-07 22:03:49 -05:00
mod sync;
use std::sync::Mutex;
use tauri::Manager;
use state::AppState;
Fix card-data download for Scryfall's new gzipped-JSONL bulk API "Download card data" had been failing on every platform with "missing field `download_uri`". Scryfall changed the bulk-data API: entries now carry `jsonl_download_uri`/`compressed_size` instead of `download_uri`/`size`, and the payload is gzipped JSONL (one card object per line) rather than an uncompressed JSON array. An existing reference.db keeps working, which is why this went unnoticed. - Rename the BulkEntry fields and decompress with flate2 (the response is Content-Type: application/gzip with no Content-Encoding, so ureq won't transparently inflate it). rust_backend keeps it pure Rust, so nothing new has to cross-compile for Android. - Replace the streaming JSON-array visitor with a line-based JSONL parser. Closure errors now propagate directly instead of being laundered through serde::de::Error::custom, so cancellation reports a clean Cancelled; override_if_cancelled stays as a safety net and its doc comment is corrected. - Progress wraps the compressed stream, since compressed_size is what the index reports. - Test fixtures stay readable pretty-printed JSON arrays via a jsonl() helper that converts them, so tests still exercise the production parser. Downloads are far smaller now: 101MB compressed (24 + 77) versus ~740MB, which matters a lot on a phone. Also log command failures to the platform log. Tauri command errors are returned to the WebView as values, so they never reached logcat and the UI only showed them truncated -- this bug was invisible until then. mlog! is a plain eprintln!, which tao's Android glue forwards to logcat under RustStdoutStderr, so no logging dependency is needed. View with: adb logcat -s RustStdoutStderr Verified against the live API: 36,242 oracle cards, 116,409 printings, trigram FTS working, and the aarch64 APK still builds.
2026-08-02 14:00:04 -05:00
/// Write a line to the platform log.
///
/// On Android this reaches `adb logcat` under the tag `RustStdoutStderr`: tao's
/// Android glue `dup2`s stdout/stderr into a pipe and forwards each line to the
/// system log, so a plain `eprintln!` is all that's needed and no logging
/// dependency is warranted. On desktop it's just stderr.
///
/// This matters because Tauri command failures are *returned* to the WebView as
/// values, not raised — so without logging them explicitly they never appear in
/// logcat at all, and the UI only ever shows a truncated version.
#[macro_export]
macro_rules! mlog {
($($arg:tt)*) => { eprintln!("[mtgc] {}", format_args!($($arg)*)) };
}
/// Log a command failure (with its full, untruncated message) and pass the
/// result through unchanged.
pub(crate) fn logged<T>(what: &str, result: Result<T, String>) -> Result<T, String> {
if let Err(e) = &result {
crate::mlog!("{what} failed: {e}");
}
result
}
Rework library sync around a JSON document; add card filters Sync The previous design put the merge inside SQL: timestamp-stamping triggers, a tombstone table, per-device SQLite snapshots, a device id in the database, and row merges written as correlated-subquery UPDATEs. All of that schema was inherited by desktop, and the merge was very hard to verify. Replace it with a single JSON document plus a three-way merge in plain Rust. Storing the last-read remote document as the merge base makes "who changed what" and "what was deleted" derivable, which removes the tombstone table, the updated_at columns and all eight triggers. It also makes a single shared file safe: if a publish is lost the remote is unchanged, so the next merge re-detects the local edits and republishes rather than reading their absence as a deletion. That removes the need for per-device files and for device identity entirely. Schema impact drops to one column, decks.uuid. Conflicts resolve to "theirs", which is required for convergence rather than chosen for taste — preferring ours has each device re-asserting its own value forever. Every such choice is reported to the user. SQLite becomes invisible: the working copy lives in the app data dir and the user only ever names a .json file. There is always a document, defaulting to private storage, so edits are never stranded with nowhere to be saved. Pointing at a file that doesn't exist yet moves the current library there; pointing at one that does adopts it. A converter brings a pre-document library.db across. Android reaches the folder through the Storage Access Framework rather than MANAGE_EXTERNAL_STORAGE, so no storage permission is declared at all. Filters Track card origin in reference.db: Universes Beyond, digital-only and Un-set cards, plus numeric power/toughness. Each origin flag is derived as "every printing", not "any printing" — ordinary cards get reprinted into those sets, and Lightning Bolt appearing in Marvel Commander must not hide it. Non-numeric power ("*", "1+*") stays NULL so it drops out of range filters instead of being compared as zero. Also wire up filters over data already present: price range, format legality (restricted counts as legal) and "can be a commander". All of these degrade silently against an older reference.db rather than failing every query. The shared criteria live in one component used by both filter panels, as a compact row of icon toggles. Card Search gains the Library tab's bulk selection: checkboxes in both views, select-all, and a bulk add-to-deck with a single undo for the whole batch.
2026-08-07 22:03:49 -05:00
/// Register the Kotlin-side `SyncPlugin` and stash its handle in app state, so
/// the sync commands can call across to SAF. See `sync.rs` for why library sync
/// goes through a Java plugin rather than plain `std::fs`.
#[cfg(target_os = "android")]
fn sync_plugin() -> tauri::plugin::TauriPlugin<tauri::Wry> {
tauri::plugin::Builder::new("mtgc-sync")
.setup(|app, api| {
let handle = api.register_android_plugin("com.garrettmills.mtgc", "SyncPlugin")?;
app.manage(sync::SyncBridge(handle));
Ok(())
})
.build()
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
Add a News tab: set releases and links to Wizards' articles The app knew nothing about the game outside your own collection — no way to see that a set drops next week, or that a Secret Lair was announced. Two sources with very different reliability, kept apart so one failing can't take the page down. Releases come from Scryfall's documented /sets endpoint. Clicking a set fetches its cards live rather than reading reference.db, so a set released or spoiled since the last card-data download still works; ownership is joined locally by oracle_id, and the same add-to-library stepper works there as anywhere else. Articles have no supported source at all: Wizards publishes no RSS or Atom feed and no public API, and their page is a Nuxt app whose CSS classes are per-build hashes. So the app links out rather than reproducing anyone's writing, which keeps the scraped surface to titles and hrefs. parse_articles keys on nothing cosmetic — anchors pointing at /en/news/<category>/<slug>, taking the anchor's own text as the title. A restyle can't break that; only a change to their URL scheme could, and the fixture tests fail loudly when it does rather than the page quietly going empty. Verified against a live capture: 15 articles, correct titles and categories. Two things the fixtures caught. Where an article is linked several times per card, the first link is a "Learn More" button, so the longest text wins rather than the first. And a *successful* fetch yielding zero articles is treated as a failure, since that is the signature of their markup changing. get_set_cards returns a full CardRef rather than the handful of fields the grid needs, so the tab renders the same CardTile as the rest of the app and gets click-to-preview instead of a lookalike that does nothing when clicked. Both feeds cache to news-cache.json for six hours and fall back to whatever was last seen, so opening the tab offline shows stale content with its age rather than an error. Also promotes the HTTP agent out of ingest.rs into a shared http module, so timeouts and User-Agent can't drift between the ingester and anything else that reaches the network. Adds tauri-plugin-opener for opening articles externally.
2026-08-07 23:35:50 -05:00
let builder = tauri::Builder::default().plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_opener::init());
Rework library sync around a JSON document; add card filters Sync The previous design put the merge inside SQL: timestamp-stamping triggers, a tombstone table, per-device SQLite snapshots, a device id in the database, and row merges written as correlated-subquery UPDATEs. All of that schema was inherited by desktop, and the merge was very hard to verify. Replace it with a single JSON document plus a three-way merge in plain Rust. Storing the last-read remote document as the merge base makes "who changed what" and "what was deleted" derivable, which removes the tombstone table, the updated_at columns and all eight triggers. It also makes a single shared file safe: if a publish is lost the remote is unchanged, so the next merge re-detects the local edits and republishes rather than reading their absence as a deletion. That removes the need for per-device files and for device identity entirely. Schema impact drops to one column, decks.uuid. Conflicts resolve to "theirs", which is required for convergence rather than chosen for taste — preferring ours has each device re-asserting its own value forever. Every such choice is reported to the user. SQLite becomes invisible: the working copy lives in the app data dir and the user only ever names a .json file. There is always a document, defaulting to private storage, so edits are never stranded with nowhere to be saved. Pointing at a file that doesn't exist yet moves the current library there; pointing at one that does adopts it. A converter brings a pre-document library.db across. Android reaches the folder through the Storage Access Framework rather than MANAGE_EXTERNAL_STORAGE, so no storage permission is declared at all. Filters Track card origin in reference.db: Universes Beyond, digital-only and Un-set cards, plus numeric power/toughness. Each origin flag is derived as "every printing", not "any printing" — ordinary cards get reprinted into those sets, and Lightning Bolt appearing in Marvel Commander must not hide it. Non-numeric power ("*", "1+*") stays NULL so it drops out of range filters instead of being compared as zero. Also wire up filters over data already present: price range, format legality (restricted counts as legal) and "can be a commander". All of these degrade silently against an older reference.db rather than failing every query. The shared criteria live in one component used by both filter panels, as a compact row of icon toggles. Card Search gains the Library tab's bulk selection: checkboxes in both views, select-all, and a bulk add-to-deck with a single undo for the whole batch.
2026-08-07 22:03:49 -05:00
#[cfg(target_os = "android")]
let builder = builder.plugin(sync_plugin());
builder
.setup(|app| {
let data_dir = app
.path()
.app_data_dir()
.expect("resolve app data dir");
std::fs::create_dir_all(&data_dir).ok();
Fix card-data download for Scryfall's new gzipped-JSONL bulk API "Download card data" had been failing on every platform with "missing field `download_uri`". Scryfall changed the bulk-data API: entries now carry `jsonl_download_uri`/`compressed_size` instead of `download_uri`/`size`, and the payload is gzipped JSONL (one card object per line) rather than an uncompressed JSON array. An existing reference.db keeps working, which is why this went unnoticed. - Rename the BulkEntry fields and decompress with flate2 (the response is Content-Type: application/gzip with no Content-Encoding, so ureq won't transparently inflate it). rust_backend keeps it pure Rust, so nothing new has to cross-compile for Android. - Replace the streaming JSON-array visitor with a line-based JSONL parser. Closure errors now propagate directly instead of being laundered through serde::de::Error::custom, so cancellation reports a clean Cancelled; override_if_cancelled stays as a safety net and its doc comment is corrected. - Progress wraps the compressed stream, since compressed_size is what the index reports. - Test fixtures stay readable pretty-printed JSON arrays via a jsonl() helper that converts them, so tests still exercise the production parser. Downloads are far smaller now: 101MB compressed (24 + 77) versus ~740MB, which matters a lot on a phone. Also log command failures to the platform log. Tauri command errors are returned to the WebView as values, so they never reached logcat and the UI only showed them truncated -- this bug was invisible until then. mlog! is a plain eprintln!, which tao's Android glue forwards to logcat under RustStdoutStderr, so no logging dependency is needed. View with: adb logcat -s RustStdoutStderr Verified against the live API: 36,242 oracle cards, 116,409 printings, trigram FTS working, and the aarch64 APK still builds.
2026-08-02 14:00:04 -05:00
mlog!("data dir: {}", data_dir.display());
app.manage(Mutex::new(AppState::new(data_dir)));
Ok(())
})
.invoke_handler(tauri::generate_handler![
commands::get_status,
commands::update_card_data,
commands::cancel_update_card_data,
Add Android support via Tauri The Rust core was already portable (caller-provided db paths, rustls rather than openssl) and the shell already had the mobile entry point, so this is mostly toolchain, provisioning, and platform-gating. Toolchain: - scripts/android-setup.sh installs what Android Studio doesn't: SDK cmdline-tools, a pinned stable NDK, and the Rust Android targets. The NDK is pinned rather than "latest" because sdkmanager's newest is often a beta (30.0.15729638 is r30-beta2) -- not something to compile SQLite with. Prefers Android Studio's bundled JDK 21, since Gradle rejects newer JDKs. - Track src-tauri/gen/android (excluding build output): AndroidManifest.xml is hand-edited and Android permissions have no tauri.conf.json equivalent. - vite: serve on TAURI_DEV_HOST so a device/emulator can reach the dev server. Card data: downloading streams ~740MB to build a ~73MB db, which is rough on a phone. Add import_reference_db to adopt a desktop-built file instead, validated by db::inspect_reference_db first so picking the wrong file is a clean error rather than a broken install. Installs via copy-then-rename so an interrupted copy can't leave a partial db. Ingest hardening (helps desktop too): - Give ureq connect/read timeouts. Cancellation is only polled per parsed record, so a stalled socket previously hung forever with no way to cancel. - Drop the remove_file before rename; POSIX rename already replaces atomically, and the delete-first window could leave no reference.db at all if the process was killed -- much likelier on Android. UI: hide Open/New Library on mobile (scoped storage has no arbitrary-path equivalent, and SQLite can't open a content:// URI), reported via a cfg!(mobile) flag on Status rather than user-agent sniffing. Use 100dvh so the soft keyboard doesn't break the shell. Verified: 70/70 core tests, desktop build clean, and the aarch64 APK builds (bundled SQLite C and ring both cross-compile under NDK 28). Not yet run on a device.
2026-08-02 13:16:34 -05:00
commands::import_reference_db,
Rework library sync around a JSON document; add card filters Sync The previous design put the merge inside SQL: timestamp-stamping triggers, a tombstone table, per-device SQLite snapshots, a device id in the database, and row merges written as correlated-subquery UPDATEs. All of that schema was inherited by desktop, and the merge was very hard to verify. Replace it with a single JSON document plus a three-way merge in plain Rust. Storing the last-read remote document as the merge base makes "who changed what" and "what was deleted" derivable, which removes the tombstone table, the updated_at columns and all eight triggers. It also makes a single shared file safe: if a publish is lost the remote is unchanged, so the next merge re-detects the local edits and republishes rather than reading their absence as a deletion. That removes the need for per-device files and for device identity entirely. Schema impact drops to one column, decks.uuid. Conflicts resolve to "theirs", which is required for convergence rather than chosen for taste — preferring ours has each device re-asserting its own value forever. Every such choice is reported to the user. SQLite becomes invisible: the working copy lives in the app data dir and the user only ever names a .json file. There is always a document, defaulting to private storage, so edits are never stranded with nowhere to be saved. Pointing at a file that doesn't exist yet moves the current library there; pointing at one that does adopts it. A converter brings a pre-document library.db across. Android reaches the folder through the Storage Access Framework rather than MANAGE_EXTERNAL_STORAGE, so no storage permission is declared at all. Filters Track card origin in reference.db: Universes Beyond, digital-only and Un-set cards, plus numeric power/toughness. Each origin flag is derived as "every printing", not "any printing" — ordinary cards get reprinted into those sets, and Lightning Bolt appearing in Marvel Commander must not hide it. Non-numeric power ("*", "1+*") stays NULL so it drops out of range filters instead of being compared as zero. Also wire up filters over data already present: price range, format legality (restricted counts as legal) and "can be a commander". All of these degrade silently against an older reference.db rather than failing every query. The shared criteria live in one component used by both filter panels, as a compact row of icon toggles. Card Search gains the Library tab's bulk selection: checkboxes in both views, select-all, and a bulk add-to-deck with a single undo for the whole batch.
2026-08-07 22:03:49 -05:00
sync::sync_status,
sync::sync_pick_folder,
sync::sync_set_document,
Let Android pick which file in the synced folder is the library Android grants a *folder* through SAF, not a file, and the document name was hardcoded to "library.json". A library named anything else was therefore never found: the phone created an empty second library beside it and the two devices silently never synced, while appearing to work. The Info tab now lists the .json files in the granted folder and lets you choose one. Picking an existing file adopts it — the working copy is dropped and rebuilt from it, matching what desktop does when opening a document — while a name that isn't there yet keeps the current library and publishes it, matching New Library. The name is validated as a plain filename, since a path would escape the granted folder that the SAF grant exists to bound. Two bugs found while testing this on-device: load_config decided whether a config was the pre-document format by testing whether document_path or legacy_library_path were set. An Android install has neither — its folder is a grant, not a path, and there is no legacy library — so a valid config's document_name was discarded on every launch and the chosen library file silently reverted to the default. It no longer infers the format from which fields happen to be populated; every field defaults, and the legacy library_path is picked up as a separate additive step. Covered by tests, including one built from the exact Android config shape, because a setting that quietly resets itself is not something a careful read catches. The Info tab's sync refresh fired at mount when the app relaunched onto that tab — the active tab is persisted — which is before setup() calls manage(), producing "state not managed for field db". Gated on statusLoaded like the startup refresh already was; get_status retries until the backend is ready, so waiting on it is what guarantees the state exists.
2026-08-09 12:17:57 -05:00
sync::sync_list_documents,
sync::sync_set_document_name,
Rework library sync around a JSON document; add card filters Sync The previous design put the merge inside SQL: timestamp-stamping triggers, a tombstone table, per-device SQLite snapshots, a device id in the database, and row merges written as correlated-subquery UPDATEs. All of that schema was inherited by desktop, and the merge was very hard to verify. Replace it with a single JSON document plus a three-way merge in plain Rust. Storing the last-read remote document as the merge base makes "who changed what" and "what was deleted" derivable, which removes the tombstone table, the updated_at columns and all eight triggers. It also makes a single shared file safe: if a publish is lost the remote is unchanged, so the next merge re-detects the local edits and republishes rather than reading their absence as a deletion. That removes the need for per-device files and for device identity entirely. Schema impact drops to one column, decks.uuid. Conflicts resolve to "theirs", which is required for convergence rather than chosen for taste — preferring ours has each device re-asserting its own value forever. Every such choice is reported to the user. SQLite becomes invisible: the working copy lives in the app data dir and the user only ever names a .json file. There is always a document, defaulting to private storage, so edits are never stranded with nowhere to be saved. Pointing at a file that doesn't exist yet moves the current library there; pointing at one that does adopts it. A converter brings a pre-document library.db across. Android reaches the folder through the Storage Access Framework rather than MANAGE_EXTERNAL_STORAGE, so no storage permission is declared at all. Filters Track card origin in reference.db: Universes Beyond, digital-only and Un-set cards, plus numeric power/toughness. Each origin flag is derived as "every printing", not "any printing" — ordinary cards get reprinted into those sets, and Lightning Bolt appearing in Marvel Commander must not hide it. Non-numeric power ("*", "1+*") stays NULL so it drops out of range filters instead of being compared as zero. Also wire up filters over data already present: price range, format legality (restricted counts as legal) and "can be a commander". All of these degrade silently against an older reference.db rather than failing every query. The shared criteria live in one component used by both filter panels, as a compact row of icon toggles. Card Search gains the Library tab's bulk selection: checkboxes in both views, select-all, and a bulk add-to-deck with a single undo for the whole batch.
2026-08-07 22:03:49 -05:00
sync::sync_forget_document,
sync::sync_now,
sync::convert_legacy_library,
Add a News tab: set releases and links to Wizards' articles The app knew nothing about the game outside your own collection — no way to see that a set drops next week, or that a Secret Lair was announced. Two sources with very different reliability, kept apart so one failing can't take the page down. Releases come from Scryfall's documented /sets endpoint. Clicking a set fetches its cards live rather than reading reference.db, so a set released or spoiled since the last card-data download still works; ownership is joined locally by oracle_id, and the same add-to-library stepper works there as anywhere else. Articles have no supported source at all: Wizards publishes no RSS or Atom feed and no public API, and their page is a Nuxt app whose CSS classes are per-build hashes. So the app links out rather than reproducing anyone's writing, which keeps the scraped surface to titles and hrefs. parse_articles keys on nothing cosmetic — anchors pointing at /en/news/<category>/<slug>, taking the anchor's own text as the title. A restyle can't break that; only a change to their URL scheme could, and the fixture tests fail loudly when it does rather than the page quietly going empty. Verified against a live capture: 15 articles, correct titles and categories. Two things the fixtures caught. Where an article is linked several times per card, the first link is a "Learn More" button, so the longest text wins rather than the first. And a *successful* fetch yielding zero articles is treated as a failure, since that is the signature of their markup changing. get_set_cards returns a full CardRef rather than the handful of fields the grid needs, so the tab renders the same CardTile as the rest of the app and gets click-to-preview instead of a lookalike that does nothing when clicked. Both feeds cache to news-cache.json for six hours and fall back to whatever was last seen, so opening the tab offline shows stale content with its age rather than an error. Also promotes the HTTP agent out of ingest.rs into a shared http module, so timeouts and User-Agent can't drift between the ingester and anything else that reaches the network. Adds tauri-plugin-opener for opening articles externally.
2026-08-07 23:35:50 -05:00
news::get_news,
news::get_set_cards,
commands::search_cards,
commands::add_owned,
commands::set_owned,
commands::set_location,
commands::search_library,
commands::search_reference,
commands::get_library_stats,
commands::get_library_facets,
commands::get_reference_facets,
commands::list_decks,
commands::create_deck,
commands::delete_deck,
commands::rename_deck,
commands::duplicate_deck,
commands::set_deck_format,
commands::set_deck_commander,
commands::set_deck_card,
commands::deck_contents,
commands::suggest_cards,
commands::card_decks,
commands::export_deck,
commands::import_deck,
commands::export_library_cards,
commands::import_library_cards,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}