Compare commits

..

4 Commits

Author SHA1 Message Date
e7c3b50a79 chore: Make clippy happy
Signed-off-by: quexeky <git@quexeky.dev>
2025-09-04 12:27:09 +10:00
cba23eab14 chore: Bump version to include logging
(Albeit, logging occurs before we initialise the logger, but oh well)

Signed-off-by: quexeky <git@quexeky.dev>
2025-09-04 12:23:31 +10:00
02af1996ec fix: Use Drop-OSS/native_model
Signed-off-by: quexeky <git@quexeky.dev>
2025-09-04 12:18:51 +10:00
a2a597d3d8 fix: Fix native_model from requirements and add version requirements for models
Signed-off-by: quexeky <git@quexeky.dev>
2025-09-04 12:16:37 +10:00
8 changed files with 68 additions and 128 deletions

12
src-tauri/Cargo.lock generated
View File

@ -3118,13 +3118,13 @@ dependencies = [
[[package]] [[package]]
name = "native_model" name = "native_model"
version = "0.6.1" version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/Drop-OSS/native_model.git#a91b422cbd53116df1f20b2459fb3d8257458bfd"
checksum = "7050d759e3da6673361dddda4f4a743492279dd2c6484a21fbee0a8278620df0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
"doc-comment", "doc-comment",
"log",
"native_model_macro", "native_model_macro",
"rmp-serde", "rmp-serde",
"serde", "serde",
@ -3134,10 +3134,10 @@ dependencies = [
[[package]] [[package]]
name = "native_model_macro" name = "native_model_macro"
version = "0.6.1" version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/Drop-OSS/native_model.git#a91b422cbd53116df1f20b2459fb3d8257458bfd"
checksum = "1577a0bebf5ed1754e240baf5d9b1845f51e598b20600aa894f55e11cd20cc6c"
dependencies = [ dependencies = [
"log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.101", "syn 2.0.101",

View File

@ -65,7 +65,7 @@ whoami = "1.6.0"
filetime = "0.2.25" filetime = "0.2.25"
walkdir = "2.5.0" walkdir = "2.5.0"
known-folders = "1.2.0" known-folders = "1.2.0"
native_model = { version = "0.6.1", features = ["rmp_serde_1_3"] } native_model = { version = "0.6.4", features = ["rmp_serde_1_3"], git = "https://github.com/Drop-OSS/native_model.git"}
tauri-plugin-opener = "2.4.0" tauri-plugin-opener = "2.4.0"
bitcode = "0.6.6" bitcode = "0.6.6"
reqwest-websocket = "0.5.0" reqwest-websocket = "0.5.0"

View File

@ -8,7 +8,6 @@ use std::{
use chrono::Utc; use chrono::Utc;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use native_model::{Decode, Encode};
use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError}; use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError};
use serde::{Serialize, de::DeserializeOwned}; use serde::{Serialize, de::DeserializeOwned};
use url::Url; use url::Url;
@ -28,7 +27,7 @@ impl<T: native_model::Model + Serialize + DeserializeOwned> DeSerializer<T>
for DropDatabaseSerializer for DropDatabaseSerializer
{ {
fn serialize(&self, val: &T) -> rustbreak::error::DeSerResult<Vec<u8>> { fn serialize(&self, val: &T) -> rustbreak::error::DeSerResult<Vec<u8>> {
native_model::rmp_serde_1_3::RmpSerde::encode(val) native_model::encode(val)
.map_err(|e| DeSerError::Internal(e.to_string())) .map_err(|e| DeSerError::Internal(e.to_string()))
} }
@ -36,7 +35,7 @@ impl<T: native_model::Model + Serialize + DeserializeOwned> DeSerializer<T>
let mut buf = Vec::new(); let mut buf = Vec::new();
s.read_to_end(&mut buf) s.read_to_end(&mut buf)
.map_err(|e| rustbreak::error::DeSerError::Other(e.into()))?; .map_err(|e| rustbreak::error::DeSerError::Other(e.into()))?;
let val = native_model::rmp_serde_1_3::RmpSerde::decode(buf) let (val, _version) = native_model::decode(buf)
.map_err(|e| DeSerError::Internal(e.to_string()))?; .map_err(|e| DeSerError::Internal(e.to_string()))?;
Ok(val) Ok(val)
} }

View File

@ -1,10 +1,5 @@
/**
* NEXT BREAKING CHANGE
*
* UPDATE DATABASE TO USE RPMSERDENAMED
*
* WE CAN'T DELETE ANY FIELDS
*/
pub mod data { pub mod data {
use std::path::PathBuf; use std::path::PathBuf;
@ -12,6 +7,9 @@ pub mod data {
use native_model::native_model; use native_model::native_model;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// NOTE: Within each version, you should NEVER use these types.
// Declare it using the actual version that it is from, i.e. v1::Settings rather than just Settings from here
pub type GameVersion = v1::GameVersion; pub type GameVersion = v1::GameVersion;
pub type Database = v3::Database; pub type Database = v3::Database;
pub type Settings = v1::Settings; pub type Settings = v1::Settings;
@ -22,7 +20,7 @@ pub mod data {
pub type DownloadableMetadata = v1::DownloadableMetadata; pub type DownloadableMetadata = v1::DownloadableMetadata;
pub type DownloadType = v1::DownloadType; pub type DownloadType = v1::DownloadType;
pub type DatabaseApplications = v2::DatabaseApplications; pub type DatabaseApplications = v2::DatabaseApplications;
pub type DatabaseCompatInfo = v2::DatabaseCompatInfo; // pub type DatabaseCompatInfo = v2::DatabaseCompatInfo;
use std::collections::HashMap; use std::collections::HashMap;
@ -180,16 +178,15 @@ pub mod data {
use serde_with::serde_as; use serde_with::serde_as;
use super::{ use super::{
ApplicationTransientStatus, DatabaseAuth, Deserialize, DownloadableMetadata, Deserialize, Serialize, native_model, v1,
GameVersion, Serialize, Settings, native_model, v1,
}; };
#[native_model(id = 1, version = 2, with = native_model::rmp_serde_1_3::RmpSerde)] #[native_model(id = 1, version = 2, with = native_model::rmp_serde_1_3::RmpSerde, from = v1::Database)]
#[derive(Serialize, Deserialize, Clone, Default)] #[derive(Serialize, Deserialize, Clone, Default)]
pub struct Database { pub struct Database {
#[serde(default)] #[serde(default)]
pub settings: Settings, pub settings: v1::Settings,
pub auth: Option<DatabaseAuth>, pub auth: Option<v1::DatabaseAuth>,
pub base_url: String, pub base_url: String,
pub applications: v1::DatabaseApplications, pub applications: v1::DatabaseApplications,
#[serde(skip)] #[serde(skip)]
@ -198,7 +195,7 @@ pub mod data {
pub compat_info: Option<DatabaseCompatInfo>, pub compat_info: Option<DatabaseCompatInfo>,
} }
#[native_model(id = 8, version = 2, with = native_model::rmp_serde_1_3::RmpSerde)] #[native_model(id = 9, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)]
#[derive(Serialize, Deserialize, Clone, Default)] #[derive(Serialize, Deserialize, Clone, Default)]
pub struct DatabaseCompatInfo { pub struct DatabaseCompatInfo {
@ -221,7 +218,7 @@ pub mod data {
// Strings are version names for a particular game // Strings are version names for a particular game
#[derive(Serialize, Clone, Deserialize, Debug)] #[derive(Serialize, Clone, Deserialize, Debug)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[native_model(id = 5, version = 2, with = native_model::rmp_serde_1_3::RmpSerde)] #[native_model(id = 5, version = 2, with = native_model::rmp_serde_1_3::RmpSerde, from = v1::GameDownloadStatus)]
pub enum GameDownloadStatus { pub enum GameDownloadStatus {
Remote {}, Remote {},
SetupRequired { SetupRequired {
@ -261,17 +258,17 @@ pub mod data {
#[serde_as] #[serde_as]
#[derive(Serialize, Clone, Deserialize, Default)] #[derive(Serialize, Clone, Deserialize, Default)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
#[native_model(id = 3, version = 2, with = native_model::rmp_serde_1_3::RmpSerde)] #[native_model(id = 3, version = 2, with = native_model::rmp_serde_1_3::RmpSerde, from=v1::DatabaseApplications)]
pub struct DatabaseApplications { pub struct DatabaseApplications {
pub install_dirs: Vec<PathBuf>, pub install_dirs: Vec<PathBuf>,
// Guaranteed to exist if the game also exists in the app state map // Guaranteed to exist if the game also exists in the app state map
pub game_statuses: HashMap<String, GameDownloadStatus>, pub game_statuses: HashMap<String, GameDownloadStatus>,
pub game_versions: HashMap<String, HashMap<String, GameVersion>>, pub game_versions: HashMap<String, HashMap<String, v1::GameVersion>>,
pub installed_game_version: HashMap<String, DownloadableMetadata>, pub installed_game_version: HashMap<String, v1::DownloadableMetadata>,
#[serde(skip)] #[serde(skip)]
pub transient_statuses: HashMap<DownloadableMetadata, ApplicationTransientStatus>, pub transient_statuses: HashMap<v1::DownloadableMetadata, v1::ApplicationTransientStatus>,
} }
impl From<v1::DatabaseApplications> for DatabaseApplications { impl From<v1::DatabaseApplications> for DatabaseApplications {
fn from(value: v1::DatabaseApplications) -> Self { fn from(value: v1::DatabaseApplications) -> Self {
@ -293,21 +290,21 @@ pub mod data {
use std::path::PathBuf; use std::path::PathBuf;
use super::{ use super::{
DatabaseApplications, DatabaseAuth, DatabaseCompatInfo, Deserialize, Serialize, Deserialize, Serialize,
Settings, native_model, v2, native_model, v2, v1,
}; };
#[native_model(id = 1, version = 3, with = native_model::rmp_serde_1_3::RmpSerde)] #[native_model(id = 1, version = 3, with = native_model::rmp_serde_1_3::RmpSerde, from = v2::Database)]
#[derive(Serialize, Deserialize, Clone, Default)] #[derive(Serialize, Deserialize, Clone, Default)]
pub struct Database { pub struct Database {
#[serde(default)] #[serde(default)]
pub settings: Settings, pub settings: v1::Settings,
pub auth: Option<DatabaseAuth>, pub auth: Option<v1::DatabaseAuth>,
pub base_url: String, pub base_url: String,
pub applications: DatabaseApplications, pub applications: v2::DatabaseApplications,
#[serde(skip)] #[serde(skip)]
pub prev_database: Option<PathBuf>, pub prev_database: Option<PathBuf>,
pub cache_dir: PathBuf, pub cache_dir: PathBuf,
pub compat_info: Option<DatabaseCompatInfo>, pub compat_info: Option<v2::DatabaseCompatInfo>,
} }
impl From<v2::Database> for Database { impl From<v2::Database> for Database {
@ -347,5 +344,6 @@ pub mod data {
compat_info: None, compat_info: None,
} }
} }
} }
} }

View File

@ -4,7 +4,6 @@
#![feature(duration_millis_float)] #![feature(duration_millis_float)]
#![feature(iterator_try_collect)] #![feature(iterator_try_collect)]
#![deny(clippy::all)] #![deny(clippy::all)]
#![deny(clippy::unwrap_used)]
mod database; mod database;
mod games; mod games;
@ -14,7 +13,6 @@ mod download_manager;
mod error; mod error;
mod process; mod process;
mod remote; mod remote;
mod utils;
use crate::database::scan::scan_install_dirs; use crate::database::scan::scan_install_dirs;
use crate::process::commands::open_process_logs; use crate::process::commands::open_process_logs;
@ -47,7 +45,7 @@ use games::commands::{
}; };
use games::downloads::commands::download_game; use games::downloads::commands::download_game;
use games::library::{Game, update_game_configuration}; use games::library::{Game, update_game_configuration};
use log::{LevelFilter, debug, info, warn, error}; use log::{LevelFilter, debug, info, warn};
use log4rs::Config; use log4rs::Config;
use log4rs::append::console::ConsoleAppender; use log4rs::append::console::ConsoleAppender;
use log4rs::append::file::FileAppender; use log4rs::append::file::FileAppender;
@ -139,7 +137,7 @@ async fn setup(handle: AppHandle) -> AppState<'static> {
))) )))
.append(false) .append(false)
.build(DATA_ROOT_DIR.join("./drop.log")) .build(DATA_ROOT_DIR.join("./drop.log"))
.expect("Failed to setup logfile"); .unwrap();
let console = ConsoleAppender::builder() let console = ConsoleAppender::builder()
.encoder(Box::new(PatternEncoder::new( .encoder(Box::new(PatternEncoder::new(
@ -159,9 +157,9 @@ async fn setup(handle: AppHandle) -> AppState<'static> {
.appenders(vec!["logfile", "console"]) .appenders(vec!["logfile", "console"])
.build(LevelFilter::from_str(&log_level).expect("Invalid log level")), .build(LevelFilter::from_str(&log_level).expect("Invalid log level")),
) )
.expect("Failed to build config"); .unwrap();
log4rs::init_config(config).expect("Failed to initialise log4rs"); log4rs::init_config(config).unwrap();
let games = HashMap::new(); let games = HashMap::new();
let download_manager = Arc::new(DownloadManagerBuilder::build(handle.clone())); let download_manager = Arc::new(DownloadManagerBuilder::build(handle.clone()));
@ -372,57 +370,42 @@ pub fn run() {
.shadow(false) .shadow(false)
.data_directory(DATA_ROOT_DIR.join(".webview")) .data_directory(DATA_ROOT_DIR.join(".webview"))
.build() .build()
.expect("Failed to build main window"); .unwrap();
app.deep_link().on_open_url(move |event| { app.deep_link().on_open_url(move |event| {
debug!("handling drop:// url"); debug!("handling drop:// url");
let binding = event.urls(); let binding = event.urls();
let url = match binding.first() { let url = binding.first().unwrap();
Some(url) => url, if url.host_str().unwrap() == "handshake" {
None => { tauri::async_runtime::spawn(recieve_handshake(
warn!("No value recieved from deep link. Is this a drop server?"); handle.clone(),
return; url.path().to_string(),
} ));
};
if let Some("handshake") = url.host_str() {
tauri::async_runtime::spawn(recieve_handshake(
handle.clone(),
url.path().to_string(),
));
} }
}); });
let open_menu_item = MenuItem::with_id(app, "open", "Open", true, None::<&str>).expect("Failed to generate open menu item");
let sep = PredefinedMenuItem::separator(app).expect("Failed to generate menu separator item");
let quit_menu_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>).expect("Failed to generate quit menu item");
let menu = Menu::with_items( let menu = Menu::with_items(
app, app,
&[ &[
&open_menu_item, &MenuItem::with_id(app, "open", "Open", true, None::<&str>).unwrap(),
&sep, &PredefinedMenuItem::separator(app).unwrap(),
/* /*
&MenuItem::with_id(app, "show_library", "Library", true, None::<&str>)?, &MenuItem::with_id(app, "show_library", "Library", true, None::<&str>)?,
&MenuItem::with_id(app, "show_settings", "Settings", true, None::<&str>)?, &MenuItem::with_id(app, "show_settings", "Settings", true, None::<&str>)?,
&PredefinedMenuItem::separator(app)?, &PredefinedMenuItem::separator(app)?,
*/ */
&quit_menu_item, &MenuItem::with_id(app, "quit", "Quit", true, None::<&str>).unwrap(),
], ],
) )
.expect("Failed to generate menu"); .unwrap();
run_on_tray(|| { run_on_tray(|| {
TrayIconBuilder::new() TrayIconBuilder::new()
.icon(app.default_window_icon().expect("Failed to get default window icon").clone()) .icon(app.default_window_icon().unwrap().clone())
.menu(&menu) .menu(&menu)
.on_menu_event(|app, event| match event.id.as_ref() { .on_menu_event(|app, event| match event.id.as_ref() {
"open" => { "open" => {
app.webview_windows() app.webview_windows().get("main").unwrap().show().unwrap();
.get("main")
.expect("Failed to get webview")
.show()
.expect("Failed to show window");
} }
"quit" => { "quit" => {
cleanup_and_exit(app, &app.state()); cleanup_and_exit(app, &app.state());
@ -439,19 +422,15 @@ pub fn run() {
{ {
let mut db_handle = borrow_db_mut_checked(); let mut db_handle = borrow_db_mut_checked();
if let Some(original) = db_handle.prev_database.take() { if let Some(original) = db_handle.prev_database.take() {
let canonicalised = match original.canonicalize() {
Ok(o) => o,
Err(_) => original,
};
warn!( warn!(
"Database corrupted. Original file at {}", "Database corrupted. Original file at {}",
canonicalised.display() original.canonicalize().unwrap().to_string_lossy()
); );
app.dialog() app.dialog()
.message(format!( .message(
"Database corrupted. A copy has been saved at: {}", "Database corrupted. A copy has been saved at: ".to_string()
canonicalised.display() + original.to_str().unwrap(),
)) )
.title("Database corrupted") .title("Database corrupted")
.show(|_| {}); .show(|_| {});
} }
@ -484,7 +463,7 @@ pub fn run() {
.on_window_event(|window, event| { .on_window_event(|window, event| {
if let WindowEvent::CloseRequested { api, .. } = event { if let WindowEvent::CloseRequested { api, .. } = event {
run_on_tray(|| { run_on_tray(|| {
window.hide().expect("Failed to close window in tray"); window.hide().unwrap();
api.prevent_close(); api.prevent_close();
}); });
} }

View File

@ -14,7 +14,6 @@ use crate::{
AppState, AppStatus, AppState, AppStatus,
database::db::{DATA_ROOT_DIR, borrow_db_mut_checked}, database::db::{DATA_ROOT_DIR, borrow_db_mut_checked},
error::remote_access_error::RemoteAccessError, error::remote_access_error::RemoteAccessError,
state_lock,
}; };
#[derive(Deserialize)] #[derive(Deserialize)]
@ -38,40 +37,16 @@ fn fetch_certificates() -> Vec<Certificate> {
match entry { match entry {
Ok(c) => { Ok(c) => {
let mut buf = Vec::new(); let mut buf = Vec::new();
match File::open(c.path()) { File::open(c.path()).unwrap().read_to_end(&mut buf).unwrap();
Ok(f) => f,
Err(e) => {
warn!(
"Failed to open file at {} with error {}",
c.path().display(),
e
);
continue;
}
}
.read_to_end(&mut buf)
.expect(&format!(
"Failed to read to end of certificate file {}",
c.path().display()
));
match Certificate::from_pem_bundle(&buf) { for cert in Certificate::from_pem_bundle(&buf).unwrap() {
Ok(certificates) => { certs.push(cert);
for cert in certificates {
certs.push(cert);
}
info!(
"added {} certificate(s) from {}",
certs.len(),
c.file_name().display()
);
}
Err(e) => warn!(
"Invalid certificate file {} with error {}",
c.path().display(),
e
),
} }
info!(
"added {} certificate(s) from {}",
certs.len(),
c.file_name().into_string().unwrap()
);
} }
Err(_) => todo!(), Err(_) => todo!(),
} }
@ -90,7 +65,7 @@ pub fn get_client_sync() -> reqwest::blocking::Client {
for cert in DROP_CERT_BUNDLE.iter() { for cert in DROP_CERT_BUNDLE.iter() {
client = client.add_root_certificate(cert.clone()); client = client.add_root_certificate(cert.clone());
} }
client.use_rustls_tls().build().expect("Failed to build synchronous client") client.use_rustls_tls().build().unwrap()
} }
pub fn get_client_async() -> reqwest::Client { pub fn get_client_async() -> reqwest::Client {
let mut client = reqwest::ClientBuilder::new(); let mut client = reqwest::ClientBuilder::new();
@ -98,7 +73,7 @@ pub fn get_client_async() -> reqwest::Client {
for cert in DROP_CERT_BUNDLE.iter() { for cert in DROP_CERT_BUNDLE.iter() {
client = client.add_root_certificate(cert.clone()); client = client.add_root_certificate(cert.clone());
} }
client.use_rustls_tls().build().expect("Failed to build asynchronous client") client.use_rustls_tls().build().unwrap()
} }
pub fn get_client_ws() -> reqwest::Client { pub fn get_client_ws() -> reqwest::Client {
let mut client = reqwest::ClientBuilder::new(); let mut client = reqwest::ClientBuilder::new();
@ -106,11 +81,7 @@ pub fn get_client_ws() -> reqwest::Client {
for cert in DROP_CERT_BUNDLE.iter() { for cert in DROP_CERT_BUNDLE.iter() {
client = client.add_root_certificate(cert.clone()); client = client.add_root_certificate(cert.clone());
} }
client client.use_rustls_tls().http1_only().build().unwrap()
.use_rustls_tls()
.http1_only()
.build()
.expect("Failed to build websocket client")
} }
pub async fn use_remote_logic( pub async fn use_remote_logic(
@ -136,7 +107,7 @@ pub async fn use_remote_logic(
return Err(RemoteAccessError::InvalidEndpoint); return Err(RemoteAccessError::InvalidEndpoint);
} }
let mut app_state = state_lock!(state); let mut app_state = state.lock().unwrap();
app_state.status = AppStatus::SignedOut; app_state.status = AppStatus::SignedOut;
drop(app_state); drop(app_state);

View File

@ -1 +0,0 @@
pub mod state_lock;

View File

@ -1,6 +0,0 @@
#[macro_export]
macro_rules! state_lock {
($state:expr) => {
$state.lock().expect("Failed to lock onto state")
};
}