diff --git a/app.vue b/app.vue index f81c432..00c2190 100644 --- a/app.vue +++ b/app.vue @@ -29,6 +29,21 @@ router.beforeEach(async () => { setupHooks(); initialNavigation(state); +listen("database_corrupted", (event) => { + createModal( + ModalType.Notification, + { + title: "Database corrupted", + description: `Drop encountered an error while reading your download. A copy can be found at: "${( + event.payload as unknown as string + ).toString()}"`, + buttonText: "Close" + }, + (e, c) => c() + ); + +}) + useHead({ title: "Drop", }); diff --git a/src-tauri/src/db.rs b/src-tauri/src/db.rs index ad4f4f6..12dccb9 100644 --- a/src-tauri/src/db.rs +++ b/src-tauri/src/db.rs @@ -2,12 +2,13 @@ use std::{ collections::HashMap, fs::{self, create_dir_all}, path::{Path, PathBuf}, - sync::{Arc, LazyLock, Mutex, RwLockWriteGuard}, + sync::{Arc, LazyLock, Mutex, RwLockWriteGuard}, time::{Instant, SystemTime, UNIX_EPOCH}, }; +use chrono::Utc; use directories::BaseDirs; use log::debug; -use rustbreak::{DeSerError, DeSerializer, PathDatabase}; +use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_with::serde_as; use tauri::AppHandle; @@ -92,6 +93,7 @@ pub struct Database { pub auth: Option, pub base_url: String, pub applications: DatabaseApplications, + pub prev_database: Option } pub static DATA_ROOT_DIR: LazyLock> = LazyLock::new(|| Mutex::new(BaseDirs::new().unwrap().data_dir().join("drop"))); @@ -135,7 +137,11 @@ impl DatabaseImpls for DatabaseInterface { let exists = fs::exists(db_path.clone()).unwrap(); match exists { - true => PathDatabase::load_from_path(db_path).expect("Database loading failed"), + true => + match PathDatabase::load_from_path(db_path.clone()) { + Ok(db) => db, + Err(e) => handle_invalid_database(e, db_path, games_base_dir), + }, false => { let default = Database { settings: Settings::default(), @@ -148,6 +154,7 @@ impl DatabaseImpls for DatabaseInterface { game_versions: HashMap::new(), installed_game_version: HashMap::new(), }, + prev_database: None, }; debug!( "Creating database at path {}", @@ -238,3 +245,32 @@ pub fn set_game_status, &Downloada push_game_update(app_handle, &meta, status); } + +// TODO: Make the error relelvant rather than just assume that it's a Deserialize error +fn handle_invalid_database(_e: RustbreakError, db_path: PathBuf, games_base_dir: PathBuf) -> rustbreak::Database { + let new_path = { + let time = Utc::now().timestamp(); + let mut base = db_path.clone().into_os_string(); + base.push(time.to_string()); + base + }; + fs::copy(&db_path, &new_path); + + let db = Database { + auth: None, + base_url: "".to_string(), + applications: DatabaseApplications { + install_dirs: vec![games_base_dir.to_str().unwrap().to_string()], + game_statuses: HashMap::new(), + transient_statuses: HashMap::new(), + game_versions: HashMap::new(), + installed_game_version: HashMap::new(), + }, + prev_database: Some(new_path.into()), + }; + + PathDatabase::create_at_path(db_path, db) + .expect("Database could not be created") + + +} \ No newline at end of file diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ff9eb76..72e9a9d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -50,7 +50,7 @@ use std::{ }; use tauri::menu::{Menu, MenuItem, PredefinedMenuItem}; use tauri::tray::TrayIconBuilder; -use tauri::{AppHandle, Manager, RunEvent, WindowEvent}; +use tauri::{AppHandle, Emitter, Manager, RunEvent, WindowEvent}; use tauri_plugin_deep_link::DeepLinkExt; #[derive(Clone, Copy, Serialize)] @@ -181,7 +181,14 @@ fn setup(handle: AppHandle) -> AppState<'static> { .entry(game_id) .and_modify(|v| *v = GameDownloadStatus::Remote {}); } + + if let Some(original) = db_handle.prev_database.take() { + warn!("Database corrupted. Original file at {}", original.canonicalize().unwrap().to_string_lossy().to_string()); + handle.emit("database_corrupted", original.to_string_lossy().to_string()).unwrap(); + } drop(db_handle); + + info!("finished setup!"); // Sync autostart state