mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-14 00:31:33 +10:00
@ -14,7 +14,8 @@
|
||||
"@tauri-apps/plugin-os": "^2.3.0",
|
||||
"@tauri-apps/plugin-shell": "^2.3.0",
|
||||
"pino": "^9.7.0",
|
||||
"pino-pretty": "^13.1.1"
|
||||
"pino-pretty": "^13.1.1",
|
||||
"tauri": "^0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.7.1"
|
||||
|
||||
12
src-tauri/Cargo.lock
generated
12
src-tauri/Cargo.lock
generated
@ -5668,9 +5668,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.2.2"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33318fe222fc2a612961de8b0419e2982767f213f54a4d3a21b0d7b85c41df8"
|
||||
checksum = "37e5858cc7b455a73ab4ea2ebc08b5be33682c00ff1bf4cad5537d4fb62499d9"
|
||||
dependencies = [
|
||||
"log",
|
||||
"raw-window-handle",
|
||||
@ -5686,9 +5686,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.3.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ead0daec5d305adcefe05af9d970fc437bcc7996052d564e7393eb291252da"
|
||||
checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
@ -5748,9 +5748,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-shell"
|
||||
version = "2.2.1"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69d5eb3368b959937ad2aeaf6ef9a8f5d11e01ffe03629d3530707bbcb27ff5d"
|
||||
checksum = "2b9ffadec5c3523f11e8273465cacb3d86ea7652a28e6e2a2e9b5c182f791d25"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"log",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use log::{debug, error};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::{lock, AppState};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn quit(app: tauri::AppHandle, state: tauri::State<'_, std::sync::Mutex<AppState<'_>>>) {
|
||||
@ -10,7 +10,7 @@ pub fn quit(app: tauri::AppHandle, state: tauri::State<'_, std::sync::Mutex<AppS
|
||||
|
||||
pub fn cleanup_and_exit(app: &AppHandle, state: &tauri::State<'_, std::sync::Mutex<AppState<'_>>>) {
|
||||
debug!("cleaning up and exiting application");
|
||||
let download_manager = state.lock().unwrap().download_manager.clone();
|
||||
let download_manager = lock!(state).download_manager.clone();
|
||||
match download_manager.ensure_terminated() {
|
||||
Ok(res) => match res {
|
||||
Ok(()) => debug!("download manager terminated correctly"),
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use crate::AppState;
|
||||
use crate::{lock, AppState};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fetch_state(
|
||||
state: tauri::State<'_, std::sync::Mutex<AppState<'_>>>,
|
||||
) -> Result<String, String> {
|
||||
let guard = state.lock().unwrap();
|
||||
let guard = lock!(state);
|
||||
let cloned_state = serde_json::to_string(&guard.clone()).map_err(|e| e.to_string())?;
|
||||
drop(guard);
|
||||
Ok(cloned_state)
|
||||
|
||||
@ -67,11 +67,15 @@ pub fn add_download_dir(new_dir: PathBuf) -> Result<(), DownloadManagerError<()>
|
||||
#[tauri::command]
|
||||
pub fn update_settings(new_settings: Value) {
|
||||
let mut db_lock = borrow_db_mut_checked();
|
||||
let mut current_settings = serde_json::to_value(db_lock.settings.clone()).unwrap();
|
||||
for (key, value) in new_settings.as_object().unwrap() {
|
||||
let mut current_settings = serde_json::to_value(db_lock.settings.clone()).expect("Failed to parse existing settings");
|
||||
let values = match new_settings.as_object() {
|
||||
Some(values) => values,
|
||||
None => { panic!("Could not parse settings values"); },
|
||||
};
|
||||
for (key, value) in values {
|
||||
current_settings[key] = value.clone();
|
||||
}
|
||||
let new_settings: Settings = serde_json::from_value(current_settings).unwrap();
|
||||
let new_settings: Settings = serde_json::from_value(current_settings).unwrap_or_else(|e| panic!("Failed to parse settings with error {}", e));
|
||||
db_lock.settings = new_settings;
|
||||
}
|
||||
#[tauri::command]
|
||||
|
||||
@ -17,8 +17,13 @@ use crate::DB;
|
||||
|
||||
use super::models::data::Database;
|
||||
|
||||
pub static DATA_ROOT_DIR: LazyLock<Arc<PathBuf>> =
|
||||
LazyLock::new(|| Arc::new(dirs::data_dir().unwrap().join("drop")));
|
||||
pub static DATA_ROOT_DIR: LazyLock<Arc<PathBuf>> = LazyLock::new(|| {
|
||||
Arc::new(
|
||||
dirs::data_dir()
|
||||
.expect("Failed to get data dir")
|
||||
.join("drop"),
|
||||
)
|
||||
});
|
||||
|
||||
// Custom JSON serializer to support everything we need
|
||||
#[derive(Debug, Default, Clone)]
|
||||
@ -59,13 +64,49 @@ impl DatabaseImpls for DatabaseInterface {
|
||||
let pfx_dir = DATA_ROOT_DIR.join("pfx");
|
||||
|
||||
debug!("creating data directory at {DATA_ROOT_DIR:?}");
|
||||
create_dir_all(DATA_ROOT_DIR.as_path()).unwrap();
|
||||
create_dir_all(&games_base_dir).unwrap();
|
||||
create_dir_all(&logs_root_dir).unwrap();
|
||||
create_dir_all(&cache_dir).unwrap();
|
||||
create_dir_all(&pfx_dir).unwrap();
|
||||
create_dir_all(DATA_ROOT_DIR.as_path()).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to create directory {} with error {}",
|
||||
DATA_ROOT_DIR.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
create_dir_all(&games_base_dir).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to create directory {} with error {}",
|
||||
games_base_dir.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
create_dir_all(&logs_root_dir).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to create directory {} with error {}",
|
||||
logs_root_dir.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
create_dir_all(&cache_dir).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to create directory {} with error {}",
|
||||
cache_dir.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
create_dir_all(&pfx_dir).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to create directory {} with error {}",
|
||||
pfx_dir.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let exists = fs::exists(db_path.clone()).unwrap();
|
||||
let exists = fs::exists(db_path.clone()).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to find if {} exists with error {}",
|
||||
db_path.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
if exists {
|
||||
match PathDatabase::load_from_path(db_path.clone()) {
|
||||
@ -74,21 +115,19 @@ impl DatabaseImpls for DatabaseInterface {
|
||||
}
|
||||
} else {
|
||||
let default = Database::new(games_base_dir, None, cache_dir);
|
||||
debug!(
|
||||
"Creating database at path {}",
|
||||
db_path.as_os_str().to_str().unwrap()
|
||||
);
|
||||
debug!("Creating database at path {}", db_path.display());
|
||||
PathDatabase::create_at_path(db_path, default).expect("Database could not be created")
|
||||
}
|
||||
}
|
||||
|
||||
fn database_is_set_up(&self) -> bool {
|
||||
!self.borrow_data().unwrap().base_url.is_empty()
|
||||
!borrow_db_checked().base_url.is_empty()
|
||||
}
|
||||
|
||||
fn fetch_base_url(&self) -> Url {
|
||||
let handle = self.borrow_data().unwrap();
|
||||
Url::parse(&handle.base_url).unwrap()
|
||||
let handle = borrow_db_checked();
|
||||
Url::parse(&handle.base_url)
|
||||
.unwrap_or_else(|_| panic!("Failed to parse base url {}", handle.base_url))
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,13 +146,16 @@ fn handle_invalid_database(
|
||||
base
|
||||
};
|
||||
info!("old database stored at: {}", new_path.to_string_lossy());
|
||||
fs::rename(&db_path, &new_path).unwrap();
|
||||
fs::rename(&db_path, &new_path).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Could not rename database {} to {} with error {}",
|
||||
db_path.display(),
|
||||
new_path.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let db = Database::new(
|
||||
games_base_dir.into_os_string().into_string().unwrap(),
|
||||
Some(new_path),
|
||||
cache_dir,
|
||||
);
|
||||
let db = Database::new(games_base_dir, Some(new_path), cache_dir);
|
||||
|
||||
PathDatabase::create_at_path(db_path, db).expect("Database could not be created")
|
||||
}
|
||||
|
||||
@ -24,11 +24,11 @@ pub fn scan_install_dirs() {
|
||||
if !drop_data_file.exists() {
|
||||
continue;
|
||||
}
|
||||
let game_id = game.file_name().into_string().unwrap();
|
||||
let game_id = game.file_name().display().to_string();
|
||||
let Ok(drop_data) = DropData::read(&game.path()) else {
|
||||
warn!(
|
||||
".dropdata exists for {}, but couldn't read it. is it corrupted?",
|
||||
game.file_name().into_string().unwrap()
|
||||
game.file_name().display()
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::{database::models::data::DownloadableMetadata, AppState};
|
||||
use crate::{AppState, database::models::data::DownloadableMetadata, lock};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn pause_downloads(state: tauri::State<'_, Mutex<AppState>>) {
|
||||
state.lock().unwrap().download_manager.pause_downloads();
|
||||
lock!(state).download_manager.pause_downloads();
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn resume_downloads(state: tauri::State<'_, Mutex<AppState>>) {
|
||||
state.lock().unwrap().download_manager.resume_downloads();
|
||||
lock!(state).download_manager.resume_downloads();
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@ -18,14 +18,12 @@ pub fn move_download_in_queue(
|
||||
old_index: usize,
|
||||
new_index: usize,
|
||||
) {
|
||||
state
|
||||
.lock()
|
||||
.unwrap()
|
||||
lock!(state)
|
||||
.download_manager
|
||||
.rearrange(old_index, new_index);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn cancel_game(state: tauri::State<'_, Mutex<AppState>>, meta: DownloadableMetadata) {
|
||||
state.lock().unwrap().download_manager.cancel(meta);
|
||||
lock!(state).download_manager.cancel(meta);
|
||||
}
|
||||
|
||||
@ -11,9 +11,7 @@ use log::{debug, error, info, warn};
|
||||
use tauri::{AppHandle, Emitter};
|
||||
|
||||
use crate::{
|
||||
database::models::data::DownloadableMetadata,
|
||||
error::application_download_error::ApplicationDownloadError,
|
||||
games::library::{QueueUpdateEvent, QueueUpdateEventQueueData, StatsUpdateEvent},
|
||||
app_emit, database::models::data::DownloadableMetadata, error::application_download_error::ApplicationDownloadError, games::library::{QueueUpdateEvent, QueueUpdateEventQueueData, StatsUpdateEvent}, lock, send
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -106,7 +104,7 @@ impl DownloadManagerBuilder {
|
||||
}
|
||||
|
||||
fn set_status(&self, status: DownloadManagerStatus) {
|
||||
*self.status.lock().unwrap() = status;
|
||||
*lock!(self.status) = status;
|
||||
}
|
||||
|
||||
fn remove_and_cleanup_front_download(&mut self, meta: &DownloadableMetadata) -> DownloadAgent {
|
||||
@ -120,10 +118,10 @@ impl DownloadManagerBuilder {
|
||||
// Make sure the download thread is terminated
|
||||
fn cleanup_current_download(&mut self) {
|
||||
self.active_control_flag = None;
|
||||
*self.progress.lock().unwrap() = None;
|
||||
*lock!(self.progress) = None;
|
||||
self.current_download_agent = None;
|
||||
|
||||
let mut download_thread_lock = self.current_download_thread.lock().unwrap();
|
||||
let mut download_thread_lock = lock!(self.current_download_thread);
|
||||
|
||||
if let Some(unfinished_thread) = download_thread_lock.take()
|
||||
&& !unfinished_thread.is_finished()
|
||||
@ -139,7 +137,7 @@ impl DownloadManagerBuilder {
|
||||
current_flag.set(DownloadThreadControlFlag::Stop);
|
||||
}
|
||||
|
||||
let mut download_thread_lock = self.current_download_thread.lock().unwrap();
|
||||
let mut download_thread_lock = lock!(self.current_download_thread);
|
||||
if let Some(current_download_thread) = download_thread_lock.take() {
|
||||
return current_download_thread.join().is_ok();
|
||||
};
|
||||
@ -201,9 +199,7 @@ impl DownloadManagerBuilder {
|
||||
self.download_queue.append(meta.clone());
|
||||
self.download_agent_registry.insert(meta, download_agent);
|
||||
|
||||
self.sender
|
||||
.send(DownloadManagerSignal::UpdateUIQueue)
|
||||
.unwrap();
|
||||
send!(self.sender, DownloadManagerSignal::UpdateUIQueue);
|
||||
}
|
||||
|
||||
fn manage_go_signal(&mut self) {
|
||||
@ -241,7 +237,7 @@ impl DownloadManagerBuilder {
|
||||
|
||||
let sender = self.sender.clone();
|
||||
|
||||
let mut download_thread_lock = self.current_download_thread.lock().unwrap();
|
||||
let mut download_thread_lock = lock!(self.current_download_thread);
|
||||
let app_handle = self.app_handle.clone();
|
||||
|
||||
*download_thread_lock = Some(spawn(move || {
|
||||
@ -252,7 +248,7 @@ impl DownloadManagerBuilder {
|
||||
Err(e) => {
|
||||
error!("download {:?} has error {}", download_agent.metadata(), &e);
|
||||
download_agent.on_error(&app_handle, &e);
|
||||
sender.send(DownloadManagerSignal::Error(e)).unwrap();
|
||||
send!(sender, DownloadManagerSignal::Error(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -276,7 +272,7 @@ impl DownloadManagerBuilder {
|
||||
&e
|
||||
);
|
||||
download_agent.on_error(&app_handle, &e);
|
||||
sender.send(DownloadManagerSignal::Error(e)).unwrap();
|
||||
send!(sender, DownloadManagerSignal::Error(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -287,10 +283,8 @@ impl DownloadManagerBuilder {
|
||||
|
||||
if validate_result {
|
||||
download_agent.on_complete(&app_handle);
|
||||
sender
|
||||
.send(DownloadManagerSignal::Completed(download_agent.metadata()))
|
||||
.unwrap();
|
||||
sender.send(DownloadManagerSignal::UpdateUIQueue).unwrap();
|
||||
send!(sender, DownloadManagerSignal::Completed(download_agent.metadata()));
|
||||
send!(sender, DownloadManagerSignal::UpdateUIQueue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -317,7 +311,7 @@ impl DownloadManagerBuilder {
|
||||
}
|
||||
|
||||
self.push_ui_queue_update();
|
||||
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
||||
send!(self.sender, DownloadManagerSignal::Go);
|
||||
}
|
||||
fn manage_error_signal(&mut self, error: ApplicationDownloadError) {
|
||||
debug!("got signal Error");
|
||||
@ -349,7 +343,7 @@ impl DownloadManagerBuilder {
|
||||
let index = self.download_queue.get_by_meta(meta);
|
||||
if let Some(index) = index {
|
||||
download_agent.on_cancelled(&self.app_handle);
|
||||
let _ = self.download_queue.edit().remove(index).unwrap();
|
||||
let _ = self.download_queue.edit().remove(index);
|
||||
let removed = self.download_agent_registry.remove(meta);
|
||||
debug!(
|
||||
"removed {:?} from queue {:?}",
|
||||
@ -362,7 +356,7 @@ impl DownloadManagerBuilder {
|
||||
let index = self.download_queue.get_by_meta(meta);
|
||||
if let Some(index) = index {
|
||||
download_agent.on_cancelled(&self.app_handle);
|
||||
let _ = self.download_queue.edit().remove(index).unwrap();
|
||||
let _ = self.download_queue.edit().remove(index);
|
||||
let removed = self.download_agent_registry.remove(meta);
|
||||
debug!(
|
||||
"removed {:?} from queue {:?}",
|
||||
@ -376,7 +370,7 @@ impl DownloadManagerBuilder {
|
||||
fn push_ui_stats_update(&self, kbs: usize, time: usize) {
|
||||
let event_data = StatsUpdateEvent { speed: kbs, time };
|
||||
|
||||
self.app_handle.emit("update_stats", event_data).unwrap();
|
||||
app_emit!(self.app_handle, "update_stats", event_data);
|
||||
}
|
||||
fn push_ui_queue_update(&self) {
|
||||
let queue = &self.download_queue.read();
|
||||
@ -395,6 +389,6 @@ impl DownloadManagerBuilder {
|
||||
.collect();
|
||||
|
||||
let event_data = QueueUpdateEvent { queue: queue_objs };
|
||||
self.app_handle.emit("update_queue", event_data).unwrap();
|
||||
app_emit!(self.app_handle, "update_queue", event_data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ use std::{
|
||||
collections::VecDeque,
|
||||
fmt::Debug,
|
||||
sync::{
|
||||
mpsc::{SendError, Sender},
|
||||
Mutex, MutexGuard,
|
||||
mpsc::{SendError, Sender},
|
||||
},
|
||||
thread::JoinHandle,
|
||||
};
|
||||
@ -14,7 +14,7 @@ use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
database::models::data::DownloadableMetadata,
|
||||
error::application_download_error::ApplicationDownloadError,
|
||||
error::application_download_error::ApplicationDownloadError, lock, send,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -119,22 +119,18 @@ impl DownloadManager {
|
||||
self.download_queue.read()
|
||||
}
|
||||
pub fn get_current_download_progress(&self) -> Option<f64> {
|
||||
let progress_object = (*self.progress.lock().unwrap()).clone()?;
|
||||
let progress_object = (*lock!(self.progress)).clone()?;
|
||||
Some(progress_object.get_progress())
|
||||
}
|
||||
pub fn rearrange_string(&self, meta: &DownloadableMetadata, new_index: usize) {
|
||||
let mut queue = self.edit();
|
||||
let current_index = get_index_from_id(&mut queue, meta).unwrap();
|
||||
let to_move = queue.remove(current_index).unwrap();
|
||||
let current_index = get_index_from_id(&mut queue, meta).expect("Failed to get meta index from id");
|
||||
let to_move = queue.remove(current_index).expect("Failed to remove meta at index from queue");
|
||||
queue.insert(new_index, to_move);
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::UpdateUIQueue)
|
||||
.unwrap();
|
||||
send!(self.command_sender, DownloadManagerSignal::UpdateUIQueue);
|
||||
}
|
||||
pub fn cancel(&self, meta: DownloadableMetadata) {
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::Cancel(meta))
|
||||
.unwrap();
|
||||
send!(self.command_sender, DownloadManagerSignal::Cancel(meta));
|
||||
}
|
||||
pub fn rearrange(&self, current_index: usize, new_index: usize) {
|
||||
if current_index == new_index {
|
||||
@ -143,39 +139,31 @@ impl DownloadManager {
|
||||
|
||||
let needs_pause = current_index == 0 || new_index == 0;
|
||||
if needs_pause {
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::Stop)
|
||||
.unwrap();
|
||||
send!(self.command_sender, DownloadManagerSignal::Stop);
|
||||
}
|
||||
|
||||
debug!("moving download at index {current_index} to index {new_index}");
|
||||
|
||||
let mut queue = self.edit();
|
||||
let to_move = queue.remove(current_index).unwrap();
|
||||
let to_move = queue.remove(current_index).expect("Failed to get");
|
||||
queue.insert(new_index, to_move);
|
||||
drop(queue);
|
||||
|
||||
if needs_pause {
|
||||
self.command_sender.send(DownloadManagerSignal::Go).unwrap();
|
||||
send!(self.command_sender, DownloadManagerSignal::Go);
|
||||
}
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::UpdateUIQueue)
|
||||
.unwrap();
|
||||
self.command_sender.send(DownloadManagerSignal::Go).unwrap();
|
||||
send!(self.command_sender, DownloadManagerSignal::UpdateUIQueue);
|
||||
send!(self.command_sender, DownloadManagerSignal::Go);
|
||||
}
|
||||
pub fn pause_downloads(&self) {
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::Stop)
|
||||
.unwrap();
|
||||
send!(self.command_sender, DownloadManagerSignal::Stop);
|
||||
}
|
||||
pub fn resume_downloads(&self) {
|
||||
self.command_sender.send(DownloadManagerSignal::Go).unwrap();
|
||||
send!(self.command_sender, DownloadManagerSignal::Go);
|
||||
}
|
||||
pub fn ensure_terminated(&self) -> Result<Result<(), ()>, Box<dyn Any + Send>> {
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::Finish)
|
||||
.unwrap();
|
||||
let terminator = self.terminator.lock().unwrap().take();
|
||||
send!(self.command_sender, DownloadManagerSignal::Finish);
|
||||
let terminator = lock!(self.terminator).take();
|
||||
terminator.unwrap().join()
|
||||
}
|
||||
pub fn get_sender(&self) -> Sender<DownloadManagerSignal> {
|
||||
|
||||
@ -10,7 +10,7 @@ use std::{
|
||||
use atomic_instant_full::AtomicInstant;
|
||||
use throttle_my_fn::throttle;
|
||||
|
||||
use crate::download_manager::download_manager_frontend::DownloadManagerSignal;
|
||||
use crate::{download_manager::download_manager_frontend::DownloadManagerSignal, lock, send};
|
||||
|
||||
use super::rolling_progress_updates::RollingProgressWindow;
|
||||
|
||||
@ -74,12 +74,10 @@ impl ProgressObject {
|
||||
}
|
||||
|
||||
pub fn set_time_now(&self) {
|
||||
*self.start.lock().unwrap() = Instant::now();
|
||||
*lock!(self.start) = Instant::now();
|
||||
}
|
||||
pub fn sum(&self) -> usize {
|
||||
self.progress_instances
|
||||
.lock()
|
||||
.unwrap()
|
||||
lock!(self.progress_instances)
|
||||
.iter()
|
||||
.map(|instance| instance.load(Ordering::Acquire))
|
||||
.sum()
|
||||
@ -88,27 +86,25 @@ impl ProgressObject {
|
||||
self.set_time_now();
|
||||
self.bytes_last_update.store(0, Ordering::Release);
|
||||
self.rolling.reset();
|
||||
self.progress_instances
|
||||
.lock()
|
||||
.unwrap()
|
||||
lock!(self.progress_instances)
|
||||
.iter()
|
||||
.for_each(|x| x.store(0, Ordering::SeqCst));
|
||||
}
|
||||
pub fn get_max(&self) -> usize {
|
||||
*self.max.lock().unwrap()
|
||||
*lock!(self.max)
|
||||
}
|
||||
pub fn set_max(&self, new_max: usize) {
|
||||
*self.max.lock().unwrap() = new_max;
|
||||
*lock!(self.max) = new_max;
|
||||
}
|
||||
pub fn set_size(&self, length: usize) {
|
||||
*self.progress_instances.lock().unwrap() =
|
||||
*lock!(self.progress_instances) =
|
||||
(0..length).map(|_| Arc::new(AtomicUsize::new(0))).collect();
|
||||
}
|
||||
pub fn get_progress(&self) -> f64 {
|
||||
self.sum() as f64 / self.get_max() as f64
|
||||
}
|
||||
pub fn get(&self, index: usize) -> Arc<AtomicUsize> {
|
||||
self.progress_instances.lock().unwrap()[index].clone()
|
||||
lock!(self.progress_instances)[index].clone()
|
||||
}
|
||||
fn update_window(&self, kilobytes_per_second: usize) {
|
||||
self.rolling.update(kilobytes_per_second);
|
||||
@ -148,18 +144,12 @@ pub fn push_update(progress: &ProgressObject, bytes_remaining: usize) {
|
||||
}
|
||||
|
||||
fn update_ui(progress_object: &ProgressObject, kilobytes_per_second: usize, time_remaining: usize) {
|
||||
progress_object
|
||||
.sender
|
||||
.send(DownloadManagerSignal::UpdateUIStats(
|
||||
kilobytes_per_second,
|
||||
time_remaining,
|
||||
))
|
||||
.unwrap();
|
||||
send!(
|
||||
progress_object.sender,
|
||||
DownloadManagerSignal::UpdateUIStats(kilobytes_per_second, time_remaining)
|
||||
);
|
||||
}
|
||||
|
||||
fn update_queue(progress: &ProgressObject) {
|
||||
progress
|
||||
.sender
|
||||
.send(DownloadManagerSignal::UpdateUIQueue)
|
||||
.unwrap();
|
||||
send!(progress.sender, DownloadManagerSignal::UpdateUIQueue)
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use std::{
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
};
|
||||
|
||||
use crate::database::models::data::DownloadableMetadata;
|
||||
use crate::{database::models::data::DownloadableMetadata, lock};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Queue {
|
||||
@ -24,10 +24,10 @@ impl Queue {
|
||||
}
|
||||
}
|
||||
pub fn read(&self) -> VecDeque<DownloadableMetadata> {
|
||||
self.inner.lock().unwrap().clone()
|
||||
lock!(self.inner).clone()
|
||||
}
|
||||
pub fn edit(&self) -> MutexGuard<'_, VecDeque<DownloadableMetadata>> {
|
||||
self.inner.lock().unwrap()
|
||||
lock!(self.inner)
|
||||
}
|
||||
pub fn pop_front(&self) -> Option<DownloadableMetadata> {
|
||||
self.edit().pop_front()
|
||||
|
||||
@ -18,7 +18,7 @@ pub enum ApplicationDownloadError {
|
||||
Checksum,
|
||||
Lock,
|
||||
IoError(Arc<io::Error>),
|
||||
DownloadError,
|
||||
DownloadError(RemoteAccessError),
|
||||
}
|
||||
|
||||
impl Display for ApplicationDownloadError {
|
||||
@ -40,10 +40,16 @@ impl Display for ApplicationDownloadError {
|
||||
write!(f, "checksum failed to validate for download")
|
||||
}
|
||||
ApplicationDownloadError::IoError(error) => write!(f, "io error: {error}"),
|
||||
ApplicationDownloadError::DownloadError => write!(
|
||||
ApplicationDownloadError::DownloadError(error) => write!(
|
||||
f,
|
||||
"Download failed. See Download Manager status for specific error"
|
||||
"Download failed with error {error}"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for ApplicationDownloadError {
|
||||
fn from(value: io::Error) -> Self {
|
||||
ApplicationDownloadError::IoError(Arc::new(value))
|
||||
}
|
||||
}
|
||||
@ -9,16 +9,18 @@ use crate::error::remote_access_error::RemoteAccessError;
|
||||
pub enum CacheError {
|
||||
HeaderNotFound(HeaderName),
|
||||
ParseError(ToStrError),
|
||||
Remote(RemoteAccessError)
|
||||
Remote(RemoteAccessError),
|
||||
ConstructionError(http::Error)
|
||||
}
|
||||
|
||||
impl Display for CacheError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CacheError::HeaderNotFound(header_name) => write!(f, "Could not find header {} in cache", header_name),
|
||||
CacheError::ParseError(to_str_error) => write!(f, "Could not parse cache with error {}", to_str_error),
|
||||
CacheError::Remote(remote_access_error) => write!(f, "Cache got remote access error: {}", remote_access_error),
|
||||
}
|
||||
let s = match self {
|
||||
CacheError::HeaderNotFound(header_name) => format!("Could not find header {header_name} in cache"),
|
||||
CacheError::ParseError(to_str_error) => format!("Could not parse cache with error {to_str_error}"),
|
||||
CacheError::Remote(remote_access_error) => format!("Cache got remote access error: {remote_access_error}"),
|
||||
CacheError::ConstructionError(error) => format!("Could not construct cache body with error {error}"),
|
||||
};
|
||||
write!(f, "{s}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,18 +1,21 @@
|
||||
use std::fmt::Display;
|
||||
use std::fmt::{Display};
|
||||
|
||||
use serde_with::SerializeDisplay;
|
||||
|
||||
#[derive(SerializeDisplay)]
|
||||
pub enum LibraryError {
|
||||
MetaNotFound(String),
|
||||
VersionNotFound(String),
|
||||
}
|
||||
impl Display for LibraryError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LibraryError::MetaNotFound(id) => write!(
|
||||
f,
|
||||
"Could not locate any installed version of game ID {id} in the database"
|
||||
),
|
||||
}
|
||||
write!(f, "{}", match self {
|
||||
LibraryError::MetaNotFound(id) => {
|
||||
format!("Could not locate any installed version of game ID {id} in the database")
|
||||
}
|
||||
LibraryError::VersionNotFound(game_id) => {
|
||||
format!("Could not locate any installed version for game id {game_id} in the database")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,8 +44,7 @@ impl Display for RemoteAccessError {
|
||||
error
|
||||
.source()
|
||||
.map(std::string::ToString::to_string)
|
||||
.or_else(|| Some("Unknown error".to_string()))
|
||||
.unwrap()
|
||||
.unwrap_or("Unknown error".to_string())
|
||||
)
|
||||
}
|
||||
RemoteAccessError::FetchErrorWS(error) => write!(
|
||||
@ -54,9 +53,8 @@ impl Display for RemoteAccessError {
|
||||
error,
|
||||
error
|
||||
.source()
|
||||
.map(|e| e.to_string())
|
||||
.or_else(|| Some("Unknown error".to_string()))
|
||||
.unwrap()
|
||||
.map(std::string::ToString::to_string)
|
||||
.unwrap_or("Unknown error".to_string())
|
||||
),
|
||||
RemoteAccessError::ParsingError(parse_error) => {
|
||||
write!(f, "{parse_error}")
|
||||
|
||||
@ -5,13 +5,10 @@ use std::{
|
||||
|
||||
|
||||
use crate::{
|
||||
AppState,
|
||||
database::{
|
||||
db::borrow_db_checked,
|
||||
models::data::GameDownloadStatus,
|
||||
},
|
||||
download_manager::downloadable::Downloadable,
|
||||
error::application_download_error::ApplicationDownloadError,
|
||||
}, download_manager::downloadable::Downloadable, error::application_download_error::ApplicationDownloadError, lock, AppState
|
||||
};
|
||||
|
||||
use super::download_agent::GameDownloadAgent;
|
||||
@ -23,16 +20,14 @@ pub async fn download_game(
|
||||
install_dir: usize,
|
||||
state: tauri::State<'_, Mutex<AppState<'_>>>,
|
||||
) -> Result<(), ApplicationDownloadError> {
|
||||
let sender = { state.lock().unwrap().download_manager.get_sender().clone() };
|
||||
let sender = { lock!(state).download_manager.get_sender().clone() };
|
||||
|
||||
let game_download_agent =
|
||||
GameDownloadAgent::new_from_index(game_id.clone(), game_version.clone(), install_dir, sender).await?;
|
||||
|
||||
let game_download_agent =
|
||||
Arc::new(Box::new(game_download_agent) as Box<dyn Downloadable + Send + Sync>);
|
||||
state
|
||||
.lock()
|
||||
.unwrap()
|
||||
lock!(state)
|
||||
.download_manager
|
||||
.queue_download(game_download_agent.clone())
|
||||
.unwrap();
|
||||
@ -62,22 +57,20 @@ pub async fn resume_download(
|
||||
} => (version_name, install_dir),
|
||||
};
|
||||
|
||||
let sender = state.lock().unwrap().download_manager.get_sender();
|
||||
let sender = lock!(state).download_manager.get_sender();
|
||||
let parent_dir: PathBuf = install_dir.into();
|
||||
|
||||
let game_download_agent = Arc::new(Box::new(
|
||||
GameDownloadAgent::new(
|
||||
game_id,
|
||||
version_name.clone(),
|
||||
parent_dir.parent().unwrap().to_path_buf(),
|
||||
parent_dir.parent().unwrap_or_else(|| panic!("Failed to get parent directry of {}", parent_dir.display())).to_path_buf(),
|
||||
sender,
|
||||
)
|
||||
.await?,
|
||||
) as Box<dyn Downloadable + Send + Sync>);
|
||||
|
||||
state
|
||||
.lock()
|
||||
.unwrap()
|
||||
lock!(state)
|
||||
.download_manager
|
||||
.queue_download(game_download_agent)
|
||||
.unwrap();
|
||||
|
||||
@ -20,15 +20,18 @@ use crate::games::state::GameStatusManager;
|
||||
use crate::process::utils::get_disk_available;
|
||||
use crate::remote::requests::generate_url;
|
||||
use crate::remote::utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC};
|
||||
use crate::{app_emit, lock, send};
|
||||
use log::{debug, error, info, warn};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::{create_dir_all, OpenOptions};
|
||||
use std::fs::{OpenOptions, create_dir_all};
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
use tauri::{AppHandle, Emitter};
|
||||
use tauri::{App, AppHandle, Emitter};
|
||||
use uuid::timestamp::context;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use rustix::fs::{FallocateFlags, fallocate};
|
||||
@ -98,10 +101,8 @@ impl GameDownloadAgent {
|
||||
|
||||
result.ensure_manifest_exists().await?;
|
||||
|
||||
let required_space = result
|
||||
.manifest
|
||||
.lock()
|
||||
.unwrap()
|
||||
let required_space = lock!(result
|
||||
.manifest)
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.values()
|
||||
@ -162,11 +163,11 @@ impl GameDownloadAgent {
|
||||
}
|
||||
|
||||
pub fn check_manifest_exists(&self) -> bool {
|
||||
self.manifest.lock().unwrap().is_some()
|
||||
lock!(self.manifest).is_some()
|
||||
}
|
||||
|
||||
pub async fn ensure_manifest_exists(&self) -> Result<(), ApplicationDownloadError> {
|
||||
if self.manifest.lock().unwrap().is_some() {
|
||||
if lock!(self.manifest).is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -197,7 +198,10 @@ impl GameDownloadAgent {
|
||||
));
|
||||
}
|
||||
|
||||
let manifest_download: DropManifest = response.json().await.unwrap();
|
||||
let manifest_download: DropManifest = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| ApplicationDownloadError::Communication(e.into()))?;
|
||||
|
||||
if let Ok(mut manifest) = self.manifest.lock() {
|
||||
*manifest = Some(manifest_download);
|
||||
@ -209,7 +213,7 @@ impl GameDownloadAgent {
|
||||
|
||||
// Sets it up for both download and validate
|
||||
fn setup_progress(&self) {
|
||||
let buckets = self.buckets.lock().unwrap();
|
||||
let buckets = lock!(self.buckets);
|
||||
|
||||
let chunk_count = buckets.iter().map(|e| e.drops.len()).sum();
|
||||
|
||||
@ -224,21 +228,23 @@ impl GameDownloadAgent {
|
||||
}
|
||||
|
||||
pub fn ensure_buckets(&self) -> Result<(), ApplicationDownloadError> {
|
||||
if self.buckets.lock().unwrap().is_empty() {
|
||||
if lock!(self.buckets).is_empty() {
|
||||
self.generate_buckets()?;
|
||||
}
|
||||
|
||||
*self.context_map.lock().unwrap() = self.dropdata.get_contexts();
|
||||
*lock!(self.context_map) = self.dropdata.get_contexts();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_buckets(&self) -> Result<(), ApplicationDownloadError> {
|
||||
let manifest = self.manifest.lock().unwrap().clone().unwrap();
|
||||
let manifest = lock!(self.manifest)
|
||||
.clone()
|
||||
.ok_or(ApplicationDownloadError::NotInitialized)?;
|
||||
let game_id = self.id.clone();
|
||||
|
||||
let base_path = Path::new(&self.dropdata.base_path);
|
||||
create_dir_all(base_path).unwrap();
|
||||
create_dir_all(base_path)?;
|
||||
|
||||
let mut buckets = Vec::new();
|
||||
|
||||
@ -248,8 +254,13 @@ impl GameDownloadAgent {
|
||||
for (raw_path, chunk) in manifest {
|
||||
let path = base_path.join(Path::new(&raw_path));
|
||||
|
||||
let container = path.parent().unwrap();
|
||||
create_dir_all(container).unwrap();
|
||||
let container = path
|
||||
.parent()
|
||||
.ok_or(ApplicationDownloadError::IoError(Arc::new(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"no parent directory",
|
||||
))))?;
|
||||
create_dir_all(container)?;
|
||||
|
||||
let already_exists = path.exists();
|
||||
let file = OpenOptions::new()
|
||||
@ -257,8 +268,7 @@ impl GameDownloadAgent {
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(false)
|
||||
.open(path.clone())
|
||||
.unwrap();
|
||||
.open(&path)?;
|
||||
let mut file_running_offset = 0;
|
||||
|
||||
for (index, length) in chunk.lengths.iter().enumerate() {
|
||||
@ -341,7 +351,7 @@ impl GameDownloadAgent {
|
||||
.collect::<Vec<(String, bool)>>(),
|
||||
);
|
||||
|
||||
*self.buckets.lock().unwrap() = buckets;
|
||||
*lock!(self.buckets) = buckets;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -357,9 +367,11 @@ impl GameDownloadAgent {
|
||||
let pool = ThreadPoolBuilder::new()
|
||||
.num_threads(max_download_threads)
|
||||
.build()
|
||||
.unwrap();
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("failed to build thread pool with {max_download_threads} threads")
|
||||
});
|
||||
|
||||
let buckets = self.buckets.lock().unwrap();
|
||||
let buckets = lock!(self.buckets);
|
||||
|
||||
let mut download_contexts = HashMap::<String, DownloadContext>::new();
|
||||
|
||||
@ -367,7 +379,8 @@ impl GameDownloadAgent {
|
||||
.iter()
|
||||
.map(|e| &e.version)
|
||||
.collect::<HashSet<_>>()
|
||||
.into_iter().cloned()
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
info!("downloading across these versions: {versions:?}");
|
||||
@ -377,7 +390,7 @@ impl GameDownloadAgent {
|
||||
|
||||
for version in versions {
|
||||
let download_context = DROP_CLIENT_SYNC
|
||||
.post(generate_url(&["/api/v2/client/context"], &[]).unwrap())
|
||||
.post(generate_url(&["/api/v2/client/context"], &[])?)
|
||||
.json(&ManifestBody {
|
||||
game: self.id.clone(),
|
||||
version: version.clone(),
|
||||
@ -400,7 +413,7 @@ impl GameDownloadAgent {
|
||||
let download_contexts = &download_contexts;
|
||||
|
||||
pool.scope(|scope| {
|
||||
let context_map = self.context_map.lock().unwrap();
|
||||
let context_map = lock!(self.context_map);
|
||||
for (index, bucket) in buckets.iter().enumerate() {
|
||||
let mut bucket = (*bucket).clone();
|
||||
let completed_contexts = completed_indexes_loop_arc.clone();
|
||||
@ -430,10 +443,23 @@ impl GameDownloadAgent {
|
||||
|
||||
let sender = self.sender.clone();
|
||||
|
||||
let download_context = download_contexts
|
||||
let download_context = match download_contexts
|
||||
.get(&bucket.version)
|
||||
.ok_or(RemoteAccessError::CorruptedState)
|
||||
.unwrap();
|
||||
{
|
||||
Ok(context) => context,
|
||||
Err(e) => {
|
||||
error!("Could not get download context with error {e}");
|
||||
|
||||
send!(
|
||||
sender,
|
||||
DownloadManagerSignal::Error(ApplicationDownloadError::DownloadError(
|
||||
e
|
||||
))
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
scope.spawn(move |_| {
|
||||
// 3 attempts
|
||||
@ -464,7 +490,7 @@ impl GameDownloadAgent {
|
||||
|
||||
if i == RETRY_COUNT - 1 || !retry {
|
||||
warn!("retry logic failed, not re-attempting.");
|
||||
sender.send(DownloadManagerSignal::Error(e)).unwrap();
|
||||
send!(sender, DownloadManagerSignal::Error(e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -477,7 +503,7 @@ impl GameDownloadAgent {
|
||||
let newly_completed = completed_contexts.clone();
|
||||
|
||||
let completed_lock_len = {
|
||||
let mut context_map_lock = self.context_map.lock().unwrap();
|
||||
let mut context_map_lock = lock!(self.context_map);
|
||||
for (_, item) in newly_completed.iter() {
|
||||
context_map_lock.insert(item.clone(), true);
|
||||
}
|
||||
@ -485,7 +511,7 @@ impl GameDownloadAgent {
|
||||
context_map_lock.values().filter(|x| **x).count()
|
||||
};
|
||||
|
||||
let context_map_lock = self.context_map.lock().unwrap();
|
||||
let context_map_lock = lock!(self.context_map);
|
||||
let contexts = buckets
|
||||
.iter()
|
||||
.flat_map(|x| x.drops.iter().map(|e| e.checksum.clone()))
|
||||
@ -534,7 +560,7 @@ impl GameDownloadAgent {
|
||||
pub fn validate(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {
|
||||
self.setup_validate(app_handle);
|
||||
|
||||
let buckets = self.buckets.lock().unwrap();
|
||||
let buckets = lock!(self.buckets);
|
||||
let contexts: Vec<DropValidateContext> = buckets
|
||||
.clone()
|
||||
.into_iter()
|
||||
@ -546,7 +572,9 @@ impl GameDownloadAgent {
|
||||
let pool = ThreadPoolBuilder::new()
|
||||
.num_threads(max_download_threads)
|
||||
.build()
|
||||
.unwrap();
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("failed to build thread pool with {max_download_threads} threads")
|
||||
});
|
||||
|
||||
let invalid_chunks = Arc::new(boxcar::Vec::new());
|
||||
pool.scope(|scope| {
|
||||
@ -564,7 +592,7 @@ impl GameDownloadAgent {
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
sender.send(DownloadManagerSignal::Error(e)).unwrap();
|
||||
send!(sender, DownloadManagerSignal::Error(e));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -591,7 +619,7 @@ impl GameDownloadAgent {
|
||||
// See docs on usage
|
||||
set_partially_installed(
|
||||
&self.metadata(),
|
||||
self.dropdata.base_path.to_str().unwrap().to_string(),
|
||||
self.dropdata.base_path.display().to_string(),
|
||||
Some(app_handle),
|
||||
);
|
||||
|
||||
@ -601,12 +629,12 @@ impl GameDownloadAgent {
|
||||
|
||||
impl Downloadable for GameDownloadAgent {
|
||||
fn download(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {
|
||||
*self.status.lock().unwrap() = DownloadStatus::Downloading;
|
||||
*lock!(self.status) = DownloadStatus::Downloading;
|
||||
self.download(app_handle)
|
||||
}
|
||||
|
||||
fn validate(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {
|
||||
*self.status.lock().unwrap() = DownloadStatus::Validating;
|
||||
*lock!(self.status) = DownloadStatus::Validating;
|
||||
self.validate(app_handle)
|
||||
}
|
||||
|
||||
@ -627,14 +655,12 @@ impl Downloadable for GameDownloadAgent {
|
||||
}
|
||||
|
||||
fn on_initialised(&self, _app_handle: &tauri::AppHandle) {
|
||||
*self.status.lock().unwrap() = DownloadStatus::Queued;
|
||||
*lock!(self.status) = DownloadStatus::Queued;
|
||||
}
|
||||
|
||||
fn on_error(&self, app_handle: &tauri::AppHandle, error: &ApplicationDownloadError) {
|
||||
*self.status.lock().unwrap() = DownloadStatus::Error;
|
||||
app_handle
|
||||
.emit("download_error", error.to_string())
|
||||
.unwrap();
|
||||
*lock!(self.status) = DownloadStatus::Error;
|
||||
app_emit!(app_handle, "download_error", error.to_string());
|
||||
|
||||
error!("error while managing download: {error}");
|
||||
|
||||
@ -653,12 +679,17 @@ impl Downloadable for GameDownloadAgent {
|
||||
}
|
||||
|
||||
fn on_complete(&self, app_handle: &tauri::AppHandle) {
|
||||
on_game_complete(
|
||||
match on_game_complete(
|
||||
&self.metadata(),
|
||||
self.dropdata.base_path.to_string_lossy().to_string(),
|
||||
app_handle,
|
||||
)
|
||||
.unwrap();
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("could not mark game as complete: {e}");
|
||||
self.on_error(app_handle, &ApplicationDownloadError::DownloadError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_cancelled(&self, app_handle: &tauri::AppHandle) {
|
||||
@ -674,6 +705,6 @@ impl Downloadable for GameDownloadAgent {
|
||||
}
|
||||
|
||||
fn status(&self) -> DownloadStatus {
|
||||
self.status.lock().unwrap().clone()
|
||||
lock!(self.status).clone()
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,11 +105,10 @@ impl<'a> DropDownloadPipeline<'a, Response, File> {
|
||||
let destination = self
|
||||
.destination
|
||||
.get_mut(index)
|
||||
.ok_or(io::Error::other("no destination"))
|
||||
.unwrap();
|
||||
.ok_or(io::Error::other("no destination"))?;
|
||||
let mut remaining = drop.length;
|
||||
if drop.start != 0 {
|
||||
destination.seek(SeekFrom::Start(drop.start.try_into().unwrap()))?;
|
||||
destination.seek(SeekFrom::Start(drop.start as u64))?;
|
||||
}
|
||||
loop {
|
||||
let size = MAX_PACKET_LENGTH.min(remaining);
|
||||
@ -190,22 +189,39 @@ pub fn download_game_bucket(
|
||||
RemoteAccessError::UnparseableResponse("missing Content-Lengths header".to_owned()),
|
||||
))?
|
||||
.to_str()
|
||||
.unwrap();
|
||||
.map_err(|e| {
|
||||
ApplicationDownloadError::Communication(RemoteAccessError::UnparseableResponse(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
for (i, raw_length) in lengths.split(",").enumerate() {
|
||||
let length = raw_length.parse::<usize>().unwrap_or(0);
|
||||
let Some(drop) = bucket.drops.get(i) else {
|
||||
warn!(
|
||||
"invalid number of Content-Lengths recieved: {i}, {lengths}"
|
||||
);
|
||||
return Err(ApplicationDownloadError::DownloadError);
|
||||
warn!("invalid number of Content-Lengths recieved: {i}, {lengths}");
|
||||
return Err(ApplicationDownloadError::DownloadError(
|
||||
RemoteAccessError::InvalidResponse(DropServerError {
|
||||
status_code: 400,
|
||||
status_message: format!(
|
||||
"invalid number of Content-Lengths recieved: {i}, {lengths}"
|
||||
),
|
||||
}),
|
||||
));
|
||||
};
|
||||
if drop.length != length {
|
||||
warn!(
|
||||
"for {}, expected {}, got {} ({})",
|
||||
drop.filename, drop.length, raw_length, length
|
||||
);
|
||||
return Err(ApplicationDownloadError::DownloadError);
|
||||
return Err(ApplicationDownloadError::DownloadError(
|
||||
RemoteAccessError::InvalidResponse(DropServerError {
|
||||
status_code: 400,
|
||||
status_message: format!(
|
||||
"for {}, expected {}, got {} ({})",
|
||||
drop.filename, drop.length, raw_length, length
|
||||
),
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@ use std::{
|
||||
use log::error;
|
||||
use native_model::{Decode, Encode};
|
||||
|
||||
use crate::lock;
|
||||
|
||||
pub type DropData = v1::DropData;
|
||||
|
||||
pub static DROP_DATA_PATH: &str = ".dropdata";
|
||||
@ -49,7 +51,12 @@ impl DropData {
|
||||
let mut s = Vec::new();
|
||||
file.read_to_end(&mut s)?;
|
||||
|
||||
Ok(native_model::rmp_serde_1_3::RmpSerde::decode(s).unwrap())
|
||||
native_model::rmp_serde_1_3::RmpSerde::decode(s).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Failed to decode drop data: {e}"),
|
||||
)
|
||||
})
|
||||
}
|
||||
pub fn write(&self) {
|
||||
let manifest_raw = match native_model::rmp_serde_1_3::RmpSerde::encode(&self) {
|
||||
@ -71,12 +78,12 @@ impl DropData {
|
||||
}
|
||||
}
|
||||
pub fn set_contexts(&self, completed_contexts: &[(String, bool)]) {
|
||||
*self.contexts.lock().unwrap() = completed_contexts.iter().map(|s| (s.0.clone(), s.1)).collect();
|
||||
*lock!(self.contexts) = completed_contexts.iter().map(|s| (s.0.clone(), s.1)).collect();
|
||||
}
|
||||
pub fn set_context(&self, context: String, state: bool) {
|
||||
self.contexts.lock().unwrap().entry(context).insert_entry(state);
|
||||
lock!(self.contexts).entry(context).insert_entry(state);
|
||||
}
|
||||
pub fn get_contexts(&self) -> HashMap<String, bool> {
|
||||
self.contexts.lock().unwrap().clone()
|
||||
lock!(self.contexts).clone()
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,14 +36,14 @@ pub fn validate_game_chunk(
|
||||
|
||||
if ctx.offset != 0 {
|
||||
source
|
||||
.seek(SeekFrom::Start(ctx.offset.try_into().unwrap()))
|
||||
.seek(SeekFrom::Start(ctx.offset as u64))
|
||||
.expect("Failed to seek to file offset");
|
||||
}
|
||||
|
||||
let mut hasher = md5::Context::new();
|
||||
|
||||
let completed =
|
||||
validate_copy(&mut source, &mut hasher, ctx.length, control_flag, progress).unwrap();
|
||||
validate_copy(&mut source, &mut hasher, ctx.length, control_flag, progress)?;
|
||||
if !completed {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ use tauri::AppHandle;
|
||||
use tauri::Emitter;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::app_emit;
|
||||
use crate::database::db::{borrow_db_checked, borrow_db_mut_checked};
|
||||
use crate::database::models::data::Database;
|
||||
use crate::database::models::data::{
|
||||
@ -18,6 +19,7 @@ use crate::error::drop_server_error::DropServerError;
|
||||
use crate::error::library_error::LibraryError;
|
||||
use crate::error::remote_access_error::RemoteAccessError;
|
||||
use crate::games::state::{GameStatusManager, GameStatusWithTransient};
|
||||
use crate::lock;
|
||||
use crate::remote::auth::generate_authorization_header;
|
||||
use crate::remote::cache::cache_object_db;
|
||||
use crate::remote::cache::{cache_object, get_cached_object, get_cached_object_db};
|
||||
@ -100,7 +102,7 @@ pub async fn fetch_library_logic(
|
||||
|
||||
let mut games: Vec<Game> = response.json().await?;
|
||||
|
||||
let mut handle = state.lock().unwrap();
|
||||
let mut handle = lock!(state);
|
||||
|
||||
let mut db_handle = borrow_db_mut_checked();
|
||||
|
||||
@ -165,7 +167,7 @@ pub async fn fetch_game_logic(
|
||||
state: tauri::State<'_, Mutex<AppState<'_>>>,
|
||||
) -> Result<FetchGameStruct, RemoteAccessError> {
|
||||
let version = {
|
||||
let state_handle = state.lock().unwrap();
|
||||
let state_handle = lock!(state);
|
||||
|
||||
let db_lock = borrow_db_checked();
|
||||
|
||||
@ -215,14 +217,14 @@ pub async fn fetch_game_logic(
|
||||
return Err(RemoteAccessError::GameNotFound(id));
|
||||
}
|
||||
if response.status() != 200 {
|
||||
let err = response.json().await.unwrap();
|
||||
let err = response.json().await?;
|
||||
warn!("{err:?}");
|
||||
return Err(RemoteAccessError::InvalidResponse(err));
|
||||
}
|
||||
|
||||
let game: Game = response.json().await?;
|
||||
|
||||
let mut state_handle = state.lock().unwrap();
|
||||
let mut state_handle = lock!(state);
|
||||
state_handle.games.insert(id.clone(), game.clone());
|
||||
|
||||
let mut db_handle = borrow_db_mut_checked();
|
||||
@ -290,22 +292,18 @@ pub async fn fetch_game_version_options_logic(
|
||||
.await?;
|
||||
|
||||
if response.status() != 200 {
|
||||
let err = response.json().await.unwrap();
|
||||
let err = response.json().await?;
|
||||
warn!("{err:?}");
|
||||
return Err(RemoteAccessError::InvalidResponse(err));
|
||||
}
|
||||
|
||||
let data: Vec<GameVersion> = response.json().await?;
|
||||
|
||||
let state_lock = state.lock().unwrap();
|
||||
let process_manager_lock = state_lock.process_manager.lock().unwrap();
|
||||
let state_lock = lock!(state);
|
||||
let process_manager_lock = lock!(state_lock.process_manager);
|
||||
let data: Vec<GameVersion> = data
|
||||
.into_iter()
|
||||
.filter(|v| {
|
||||
process_manager_lock
|
||||
.valid_platform(&v.platform, &state_lock)
|
||||
.unwrap()
|
||||
})
|
||||
.filter(|v| process_manager_lock.valid_platform(&v.platform, &state_lock))
|
||||
.collect();
|
||||
drop(process_manager_lock);
|
||||
drop(state_lock);
|
||||
@ -372,11 +370,13 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
|
||||
);
|
||||
|
||||
let previous_state = db_handle.applications.game_statuses.get(&meta.id).cloned();
|
||||
if previous_state.is_none() {
|
||||
|
||||
let previous_state = if let Some(state) = previous_state {
|
||||
state
|
||||
} else {
|
||||
warn!("uninstall job doesn't have previous state, failing silently");
|
||||
return;
|
||||
}
|
||||
let previous_state = previous_state.unwrap();
|
||||
};
|
||||
|
||||
if let Some((_, install_dir)) = match previous_state {
|
||||
GameDownloadStatus::Installed {
|
||||
@ -425,7 +425,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
|
||||
);
|
||||
|
||||
debug!("uninstalled game id {}", &meta.id);
|
||||
app_handle.emit("update_library", ()).unwrap();
|
||||
app_emit!(app_handle, "update_library", ());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -498,17 +498,15 @@ pub fn on_game_complete(
|
||||
.game_statuses
|
||||
.insert(meta.id.clone(), status.clone());
|
||||
drop(db_handle);
|
||||
|
||||
app_handle
|
||||
.emit(
|
||||
&format!("update_game/{}", meta.id),
|
||||
GameUpdateEvent {
|
||||
game_id: meta.id.clone(),
|
||||
status: (Some(status), None),
|
||||
version: Some(game_version),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
app_emit!(
|
||||
app_handle,
|
||||
&format!("update_game/{}", meta.id),
|
||||
GameUpdateEvent {
|
||||
game_id: meta.id.clone(),
|
||||
status: (Some(status), None),
|
||||
version: Some(game_version),
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -521,20 +519,20 @@ pub fn push_game_update(
|
||||
) {
|
||||
if let Some(GameDownloadStatus::Installed { .. } | GameDownloadStatus::SetupRequired { .. }) =
|
||||
&status.0
|
||||
&& version.is_none() {
|
||||
panic!("pushed game for installed game that doesn't have version information");
|
||||
}
|
||||
&& version.is_none()
|
||||
{
|
||||
panic!("pushed game for installed game that doesn't have version information");
|
||||
}
|
||||
|
||||
app_handle
|
||||
.emit(
|
||||
&format!("update_game/{game_id}"),
|
||||
GameUpdateEvent {
|
||||
game_id: game_id.clone(),
|
||||
status,
|
||||
version,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
app_emit!(
|
||||
app_handle,
|
||||
&format!("update_game/{game_id}"),
|
||||
GameUpdateEvent {
|
||||
game_id: game_id.clone(),
|
||||
status,
|
||||
version,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -556,7 +554,7 @@ pub fn update_game_configuration(
|
||||
.ok_or(LibraryError::MetaNotFound(game_id))?;
|
||||
|
||||
let id = installed_version.id.clone();
|
||||
let version = installed_version.version.clone().unwrap();
|
||||
let version = installed_version.version.clone().ok_or(LibraryError::VersionNotFound(id.clone()))?;
|
||||
|
||||
let mut existing_configuration = handle
|
||||
.applications
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::{error::process_error::ProcessError, AppState};
|
||||
use crate::{error::process_error::ProcessError, lock, AppState};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn launch_game(
|
||||
id: String,
|
||||
state: tauri::State<'_, Mutex<AppState>>,
|
||||
) -> Result<(), ProcessError> {
|
||||
let state_lock = state.lock().unwrap();
|
||||
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
||||
let state_lock = lock!(state);
|
||||
let mut process_manager_lock = lock!(state_lock.process_manager);
|
||||
|
||||
//let meta = DownloadableMetadata {
|
||||
// id,
|
||||
@ -32,8 +32,8 @@ pub fn kill_game(
|
||||
game_id: String,
|
||||
state: tauri::State<'_, Mutex<AppState>>,
|
||||
) -> Result<(), ProcessError> {
|
||||
let state_lock = state.lock().unwrap();
|
||||
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
||||
let state_lock = lock!(state);
|
||||
let mut process_manager_lock = lock!(state_lock.process_manager);
|
||||
process_manager_lock
|
||||
.kill_game(game_id)
|
||||
.map_err(ProcessError::IOError)
|
||||
@ -44,7 +44,7 @@ pub fn open_process_logs(
|
||||
game_id: String,
|
||||
state: tauri::State<'_, Mutex<AppState>>,
|
||||
) -> Result<(), ProcessError> {
|
||||
let state_lock = state.lock().unwrap();
|
||||
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
||||
let state_lock = lock!(state);
|
||||
let mut process_manager_lock = lock!(state_lock.process_manager);
|
||||
process_manager_lock.open_process_logs(game_id)
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
|
||||
use crate::{
|
||||
AppState, DB,
|
||||
AppState,
|
||||
database::{
|
||||
db::{DATA_ROOT_DIR, borrow_db_checked, borrow_db_mut_checked},
|
||||
models::data::{
|
||||
@ -33,6 +33,7 @@ use crate::{
|
||||
format::DropFormatArgs,
|
||||
process_handlers::{AsahiMuvmLauncher, NativeGameLauncher, UMULauncher},
|
||||
},
|
||||
lock,
|
||||
};
|
||||
|
||||
pub struct RunningProcess {
|
||||
@ -118,7 +119,7 @@ impl ProcessManager<'_> {
|
||||
let dir = self.get_log_dir(game_id);
|
||||
self.app_handle
|
||||
.opener()
|
||||
.open_path(dir.to_str().unwrap(), None::<&str>)
|
||||
.open_path(dir.display().to_string(), None::<&str>)
|
||||
.map_err(ProcessError::OpenerError)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -133,7 +134,13 @@ impl ProcessManager<'_> {
|
||||
|
||||
debug!("process for {:?} exited with {:?}", &game_id, result);
|
||||
|
||||
let process = self.processes.remove(&game_id).unwrap();
|
||||
let process = match self.processes.remove(&game_id) {
|
||||
Some(process) => process,
|
||||
None => {
|
||||
info!("Attempted to stop process {game_id} which didn't exist");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut db_handle = borrow_db_mut_checked();
|
||||
let meta = db_handle
|
||||
@ -141,7 +148,7 @@ impl ProcessManager<'_> {
|
||||
.installed_game_version
|
||||
.get(&game_id)
|
||||
.cloned()
|
||||
.unwrap();
|
||||
.unwrap_or_else(|| panic!("Could not get installed version of {}", &game_id));
|
||||
db_handle.applications.transient_statuses.remove(&meta);
|
||||
|
||||
let current_state = db_handle.applications.game_statuses.get(&game_id).cloned();
|
||||
@ -166,20 +173,17 @@ impl ProcessManager<'_> {
|
||||
// Or if the status isn't 0
|
||||
// Or if it's an error
|
||||
if !process.manually_killed
|
||||
&& (elapsed.as_secs() <= 2 || result.is_err() || !result.unwrap().success())
|
||||
&& (elapsed.as_secs() <= 2 || result.map_or(true, |r| !r.success()))
|
||||
{
|
||||
warn!("drop detected that the game {game_id} may have failed to launch properly");
|
||||
let _ = self.app_handle.emit("launch_external_error", &game_id);
|
||||
}
|
||||
|
||||
// This is too many unwraps for me to be comfortable
|
||||
let version_data = db_handle
|
||||
.applications
|
||||
.game_versions
|
||||
.get(&game_id)
|
||||
.unwrap()
|
||||
.get(&meta.version.unwrap())
|
||||
.unwrap();
|
||||
let version_data = match db_handle.applications.game_versions.get(&game_id) {
|
||||
// This unwrap here should be resolved by just making the hashmap accept an option rather than just a String
|
||||
Some(res) => res.get(&meta.version.unwrap()).expect("Failed to get game version from installed game versions. Is the database corrupted?"),
|
||||
None => todo!(),
|
||||
};
|
||||
|
||||
let status = GameStatusManager::fetch_state(&game_id, &db_handle);
|
||||
|
||||
@ -210,10 +214,10 @@ impl ProcessManager<'_> {
|
||||
.1)
|
||||
}
|
||||
|
||||
pub fn valid_platform(&self, platform: &Platform, state: &AppState) -> Result<bool, String> {
|
||||
pub fn valid_platform(&self, platform: &Platform, state: &AppState) -> bool {
|
||||
let db_lock = borrow_db_checked();
|
||||
let process_handler = self.fetch_process_handler(&db_lock, state, platform);
|
||||
Ok(process_handler.is_ok())
|
||||
process_handler.is_ok()
|
||||
}
|
||||
|
||||
pub fn launch_process(
|
||||
@ -225,9 +229,7 @@ impl ProcessManager<'_> {
|
||||
return Err(ProcessError::AlreadyRunning);
|
||||
}
|
||||
|
||||
let version = match DB
|
||||
.borrow_data()
|
||||
.unwrap()
|
||||
let version = match borrow_db_checked()
|
||||
.applications
|
||||
.game_statuses
|
||||
.get(&game_id)
|
||||
@ -266,7 +268,7 @@ impl ProcessManager<'_> {
|
||||
debug!(
|
||||
"Launching process {:?} with version {:?}",
|
||||
&game_id,
|
||||
db_lock.applications.game_versions.get(&game_id).unwrap()
|
||||
db_lock.applications.game_versions.get(&game_id)
|
||||
);
|
||||
|
||||
let game_version = db_lock
|
||||
@ -322,8 +324,9 @@ impl ProcessManager<'_> {
|
||||
GameDownloadStatus::Remote {} => unreachable!("Game registered as 'Remote'"),
|
||||
};
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let launch = PathBuf::from_str(install_dir).unwrap().join(launch);
|
||||
let launch = launch.to_str().unwrap();
|
||||
let launch = launch.display().to_string();
|
||||
|
||||
let launch_string = process_handler.create_launch_process(
|
||||
&meta,
|
||||
@ -392,9 +395,12 @@ impl ProcessManager<'_> {
|
||||
let result: Result<ExitStatus, std::io::Error> = launch_process_handle.wait();
|
||||
|
||||
let app_state = wait_thread_apphandle.state::<Mutex<AppState>>();
|
||||
let app_state_handle = app_state.lock().unwrap();
|
||||
let app_state_handle = lock!(app_state);
|
||||
|
||||
let mut process_manager_handle = app_state_handle.process_manager.lock().unwrap();
|
||||
let mut process_manager_handle = app_state_handle
|
||||
.process_manager
|
||||
.lock()
|
||||
.expect("Failed to lock onto process manager");
|
||||
process_manager_handle.on_process_finish(wait_thread_game_id.id, result);
|
||||
|
||||
// As everything goes out of scope, they should get dropped
|
||||
|
||||
@ -9,13 +9,17 @@ use tauri::{AppHandle, Emitter, Manager};
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
app_emit, database::{
|
||||
AppState, AppStatus, User, app_emit,
|
||||
database::{
|
||||
db::{borrow_db_checked, borrow_db_mut_checked},
|
||||
models::data::DatabaseAuth,
|
||||
}, error::{drop_server_error::DropServerError, remote_access_error::RemoteAccessError}, remote::{
|
||||
},
|
||||
error::{drop_server_error::DropServerError, remote_access_error::RemoteAccessError},
|
||||
lock,
|
||||
remote::{
|
||||
requests::make_authenticated_get,
|
||||
utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC},
|
||||
}, state_lock, AppState, AppStatus, User
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -118,6 +122,16 @@ async fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), Re
|
||||
}
|
||||
let response_struct: HandshakeResponse = response.json().await?;
|
||||
|
||||
{
|
||||
let mut handle = borrow_db_mut_checked();
|
||||
handle.auth = Some(DatabaseAuth {
|
||||
private: response_struct.private,
|
||||
cert: response_struct.certificate,
|
||||
client_id: response_struct.id,
|
||||
web_token: None,
|
||||
});
|
||||
}
|
||||
|
||||
let web_token = {
|
||||
let header = generate_authorization_header();
|
||||
let token = client
|
||||
@ -128,14 +142,8 @@ async fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), Re
|
||||
|
||||
token.text().await?
|
||||
};
|
||||
|
||||
let mut handle = borrow_db_mut_checked();
|
||||
handle.auth = Some(DatabaseAuth {
|
||||
private: response_struct.private,
|
||||
cert: response_struct.certificate,
|
||||
client_id: response_struct.id,
|
||||
web_token: Some(web_token),
|
||||
});
|
||||
handle.auth.as_mut().unwrap().web_token = Some(web_token);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -155,7 +163,7 @@ pub async fn recieve_handshake(app: AppHandle, path: String) {
|
||||
|
||||
let (app_status, user) = setup().await;
|
||||
|
||||
let mut state_lock = state_lock!(app_state);
|
||||
let mut state_lock = lock!(app_state);
|
||||
|
||||
state_lock.status = app_status;
|
||||
state_lock.user = user;
|
||||
|
||||
@ -16,7 +16,7 @@ use http::{header::{CONTENT_TYPE}, response::Builder as ResponseBuilder, Respons
|
||||
macro_rules! offline {
|
||||
($var:expr, $func1:expr, $func2:expr, $( $arg:expr ),* ) => {
|
||||
|
||||
async move { if $crate::borrow_db_checked().settings.force_offline || $crate::state_lock!($var).status == $crate::AppStatus::Offline {
|
||||
async move { if $crate::borrow_db_checked().settings.force_offline || $crate::lock!($var).status == $crate::AppStatus::Offline {
|
||||
$func2( $( $arg ), *).await
|
||||
} else {
|
||||
$func1( $( $arg ), *).await
|
||||
@ -105,15 +105,18 @@ impl TryFrom<Response<Vec<u8>>> for ObjectCache {
|
||||
|
||||
}
|
||||
}
|
||||
impl From<ObjectCache> for Response<Vec<u8>> {
|
||||
fn from(value: ObjectCache) -> Self {
|
||||
impl TryFrom<ObjectCache> for Response<Vec<u8>> {
|
||||
type Error = CacheError;
|
||||
fn try_from(value: ObjectCache) -> Result<Self, Self::Error> {
|
||||
let resp_builder = ResponseBuilder::new().header(CONTENT_TYPE, value.content_type);
|
||||
resp_builder.body(value.body).unwrap()
|
||||
resp_builder.body(value.body).map_err(CacheError::ConstructionError)
|
||||
}
|
||||
}
|
||||
impl From<&ObjectCache> for Response<Vec<u8>> {
|
||||
fn from(value: &ObjectCache) -> Self {
|
||||
impl TryFrom<&ObjectCache> for Response<Vec<u8>> {
|
||||
type Error = CacheError;
|
||||
|
||||
fn try_from(value: &ObjectCache) -> Result<Self, Self::Error> {
|
||||
let resp_builder = ResponseBuilder::new().header(CONTENT_TYPE, value.content_type.clone());
|
||||
resp_builder.body(value.body.clone()).unwrap()
|
||||
resp_builder.body(value.body.clone()).map_err(CacheError::ConstructionError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,11 +8,16 @@ use tauri::{AppHandle, Emitter, Manager};
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
app_emit, database::db::{borrow_db_checked, borrow_db_mut_checked}, error::remote_access_error::RemoteAccessError, remote::{
|
||||
AppState, AppStatus, app_emit,
|
||||
database::db::{borrow_db_checked, borrow_db_mut_checked},
|
||||
error::remote_access_error::RemoteAccessError,
|
||||
lock,
|
||||
remote::{
|
||||
auth::generate_authorization_header,
|
||||
requests::generate_url,
|
||||
utils::{DROP_CLIENT_SYNC, DROP_CLIENT_WS_CLIENT},
|
||||
}, state_lock, utils::webbrowser_open::webbrowser_open, AppState, AppStatus
|
||||
},
|
||||
utils::webbrowser_open::webbrowser_open,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -74,7 +79,7 @@ pub fn sign_out(app: AppHandle) {
|
||||
// Update app state
|
||||
{
|
||||
let app_state = app.state::<Mutex<AppState>>();
|
||||
let mut app_state_handle = state_lock!(app_state);
|
||||
let mut app_state_handle = lock!(app_state);
|
||||
app_state_handle.status = AppStatus::SignedOut;
|
||||
app_state_handle.user = None;
|
||||
}
|
||||
@ -87,7 +92,7 @@ pub fn sign_out(app: AppHandle) {
|
||||
pub async fn retry_connect(state: tauri::State<'_, Mutex<AppState<'_>>>) -> Result<(), ()> {
|
||||
let (app_status, user) = setup().await;
|
||||
|
||||
let mut guard = state_lock!(state);
|
||||
let mut guard = lock!(state);
|
||||
guard.status = app_status;
|
||||
guard.user = user;
|
||||
drop(guard);
|
||||
@ -121,9 +126,8 @@ struct CodeWebsocketResponse {
|
||||
pub fn auth_initiate_code(app: AppHandle) -> Result<String, RemoteAccessError> {
|
||||
let base_url = {
|
||||
let db_lock = borrow_db_checked();
|
||||
Url::parse(&db_lock.base_url.clone())?
|
||||
Url::parse(&db_lock.base_url.clone())?.clone()
|
||||
};
|
||||
|
||||
|
||||
let code = auth_initiate_logic("code".to_string())?;
|
||||
let header_code = code.clone();
|
||||
@ -149,14 +153,13 @@ pub fn auth_initiate_code(app: AppHandle) -> Result<String, RemoteAccessError> {
|
||||
match response.response_type.as_str() {
|
||||
"token" => {
|
||||
let recieve_app = app.clone();
|
||||
manual_recieve_handshake(recieve_app, response.value).await.unwrap();
|
||||
manual_recieve_handshake(recieve_app, response.value).await;
|
||||
return Ok(());
|
||||
}
|
||||
_ => return Err(RemoteAccessError::HandshakeFailed(response.value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(RemoteAccessError::HandshakeFailed(
|
||||
"Failed to connect to websocket".to_string(),
|
||||
))
|
||||
@ -173,8 +176,6 @@ pub fn auth_initiate_code(app: AppHandle) -> Result<String, RemoteAccessError> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn manual_recieve_handshake(app: AppHandle, token: String) -> Result<(), ()> {
|
||||
pub async fn manual_recieve_handshake(app: AppHandle, token: String) {
|
||||
recieve_handshake(app, format!("handshake/{token}")).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ pub async fn fetch_object(request: http::Request<Vec<u8>>) -> Result<Response<Ve
|
||||
if let Ok(cache_result) = &cache_result
|
||||
&& !cache_result.has_expired()
|
||||
{
|
||||
return Ok(cache_result.into());
|
||||
return cache_result.try_into();
|
||||
}
|
||||
|
||||
let header = generate_authorization_header();
|
||||
@ -64,7 +64,7 @@ pub async fn fetch_object(request: http::Request<Vec<u8>>) -> Result<Response<Ve
|
||||
Err(e) => {
|
||||
debug!("Object fetch failed with error {e}. Attempting to download from cache");
|
||||
match cache_result {
|
||||
Ok(cache_result) => Ok(cache_result.into()),
|
||||
Ok(cache_result) => cache_result.try_into(),
|
||||
Err(e) => {
|
||||
warn!("{e}");
|
||||
Err(CacheError::Remote(e))
|
||||
|
||||
@ -11,7 +11,7 @@ use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
database::db::{borrow_db_mut_checked, DATA_ROOT_DIR}, error::remote_access_error::RemoteAccessError, state_lock, AppState, AppStatus
|
||||
database::db::{borrow_db_mut_checked, DATA_ROOT_DIR}, error::remote_access_error::RemoteAccessError, lock, AppState, AppStatus
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -134,7 +134,7 @@ pub async fn use_remote_logic(
|
||||
return Err(RemoteAccessError::InvalidEndpoint);
|
||||
}
|
||||
|
||||
let mut app_state = state_lock!(state);
|
||||
let mut app_state = lock!(state);
|
||||
app_state.status = AppStatus::SignedOut;
|
||||
drop(app_state);
|
||||
|
||||
|
||||
6
src-tauri/src/utils/download_manager_send.rs
Normal file
6
src-tauri/src/utils/download_manager_send.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#[macro_export]
|
||||
macro_rules! send {
|
||||
($download_manager:expr, $signal:expr) => {
|
||||
$download_manager.send($signal).unwrap_or_else(|_| panic!("Failed to send signal {} to the download manager", stringify!(signal)))
|
||||
};
|
||||
}
|
||||
6
src-tauri/src/utils/lock.rs
Normal file
6
src-tauri/src/utils/lock.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#[macro_export]
|
||||
macro_rules! lock {
|
||||
($mutex:expr) => {
|
||||
$mutex.lock().unwrap_or_else(|_| panic!("Failed to lock onto {}", stringify!($mutex)))
|
||||
};
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
mod app_emit;
|
||||
mod state_lock;
|
||||
mod download_manager_send;
|
||||
mod lock;
|
||||
pub mod webbrowser_open;
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
#[macro_export]
|
||||
macro_rules! state_lock {
|
||||
($state:expr) => {
|
||||
$state.lock().expect("Failed to lock onto state")
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user