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

82 lines
2.8 KiB
Rust
Raw Normal View History

mod commands;
mod state;
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
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.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::set_library_path,
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,
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");
}