diff --git a/src-tauri/src/downloads/download_agent.rs b/src-tauri/src/downloads/download_agent.rs index 0b5e610..81b1955 100644 --- a/src-tauri/src/downloads/download_agent.rs +++ b/src-tauri/src/downloads/download_agent.rs @@ -7,14 +7,17 @@ use log::info; use rayon::ThreadPoolBuilder; use std::fmt::{Display, Formatter}; use std::fs::{create_dir_all, File}; +use std::io; use std::path::Path; -use std::sync::Mutex; +use std::sync::mpsc::Sender; +use std::sync::{Arc, Mutex}; use urlencoding::encode; #[cfg(target_os = "linux")] use rustix::fs::{fallocate, FallocateFlags}; use super::download_logic::download_game_chunk; +use super::download_manager::DownloadManagerSignal; use super::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}; use super::progress_object::ProgressObject; @@ -26,6 +29,7 @@ pub struct GameDownloadAgent { contexts: Mutex>, pub manifest: Mutex>, pub progress: ProgressObject, + sender: Sender } #[derive(Debug)] @@ -34,6 +38,8 @@ pub enum GameDownloadError { Checksum, Setup(SetupError), Lock, + IoError(io::Error), + DownloadError } #[derive(Debug)] @@ -48,12 +54,14 @@ impl Display for GameDownloadError { GameDownloadError::Setup(error) => write!(f, "{:?}", error), GameDownloadError::Lock => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"), GameDownloadError::Checksum => write!(f, "Checksum failed to validate for download"), + GameDownloadError::IoError(error) => write!(f, "{}", error), + GameDownloadError::DownloadError => write!(f, "Download failed. See Download Manager status for specific error"), } } } impl GameDownloadAgent { - pub fn new(id: String, version: String, target_download_dir: usize) -> Self { + pub fn new(id: String, version: String, target_download_dir: usize, sender: Sender) -> Self { // Don't run by default let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop); Self { @@ -64,11 +72,11 @@ impl GameDownloadAgent { target_download_dir, contexts: Mutex::new(Vec::new()), progress: ProgressObject::new(0, 0), + sender } } // Blocking - // Requires mutable self pub fn setup_download(&self) -> Result<(), GameDownloadError> { self.ensure_manifest_exists()?; info!("Ensured manifest exists"); @@ -84,7 +92,7 @@ impl GameDownloadAgent { // Blocking pub fn download(&self) -> Result<(), GameDownloadError> { self.setup_download()?; - self.run(); + self.run().map_err(|_| GameDownloadError::DownloadError)?; Ok(()) } @@ -197,7 +205,7 @@ impl GameDownloadAgent { Err(GameDownloadError::Setup(SetupError::Context)) } - pub fn run(&self) { + pub fn run(&self) -> Result<(), ()> { const DOWNLOAD_MAX_THREADS: usize = 4; let pool = ThreadPoolBuilder::new() @@ -205,22 +213,48 @@ impl GameDownloadAgent { .build() .unwrap(); + let new_contexts = Arc::new(Mutex::new(Vec::new())); + let new_contexts_ref = new_contexts.clone(); + pool.scope(move |scope| { let contexts = self.contexts.lock().unwrap(); + for (index, context) in contexts.iter().enumerate() { let context = context.clone(); let control_flag = self.control_flag.clone(); // Clone arcs let progress = self.progress.get(index); // Clone arcs + let new_contexts_ref = new_contexts_ref.clone(); scope.spawn(move |_| { info!( "starting download for file {} {}", context.file_name, context.index ); - download_game_chunk(context, control_flag, progress).unwrap(); + match download_game_chunk(context.clone(), control_flag, progress) { + Ok(res) => { + match res { + true => {}, + false => new_contexts_ref.lock().unwrap().push(context), + } + }, + Err(e) => { + info!("GameDownloadError: {}", e); + self.sender.send(DownloadManagerSignal::Error(e)).unwrap(); + new_contexts_ref.lock().unwrap().push(context); + }, + } }); } }); + info!("Acquiring lock"); + if !new_contexts.lock().unwrap().is_empty() { + info!("New contexts not empty"); + *self.contexts.lock().unwrap() = Arc::into_inner(new_contexts).unwrap().into_inner().unwrap(); + info!("Contexts: {:?}", *self.contexts.lock().unwrap()); + return Err(()) + } + info!("Contexts: {:?}", *self.contexts.lock().unwrap()); + Ok(()) } } diff --git a/src-tauri/src/downloads/download_commands.rs b/src-tauri/src/downloads/download_commands.rs index d7d9155..f98a9fc 100644 --- a/src-tauri/src/downloads/download_commands.rs +++ b/src-tauri/src/downloads/download_commands.rs @@ -1,7 +1,5 @@ use std::sync::Mutex; -use serde::Serialize; - use crate::{AppError, AppState}; #[tauri::command] @@ -32,7 +30,18 @@ pub fn get_current_game_download_progress( Some(progress) => Ok(progress), None => Err(AppError::DoesNotExist), } +} +#[tauri::command] +pub fn stop_game_download( + state: tauri::State<'_, Mutex>, + game_id: String +) { + state + .lock() + .unwrap() + .download_manager + .cancel_download(game_id); } /* fn use_download_agent( diff --git a/src-tauri/src/downloads/download_logic.rs b/src-tauri/src/downloads/download_logic.rs index f094c96..6c99ec3 100644 --- a/src-tauri/src/downloads/download_logic.rs +++ b/src-tauri/src/downloads/download_logic.rs @@ -171,12 +171,12 @@ pub fn download_game_chunk( content_length.unwrap().try_into().unwrap(), ); - let completed = pipeline.copy().unwrap(); + let completed = pipeline.copy().map_err(|e| GameDownloadError::IoError(e))?; if !completed { return Ok(false); }; - let checksum = pipeline.finish().unwrap(); + let checksum = pipeline.finish().map_err(|e| GameDownloadError::IoError(e))?; let res = hex::encode(checksum.0); if res != ctx.checksum { diff --git a/src-tauri/src/downloads/download_manager.rs b/src-tauri/src/downloads/download_manager.rs index 885ed44..0b674e3 100644 --- a/src-tauri/src/downloads/download_manager.rs +++ b/src-tauri/src/downloads/download_manager.rs @@ -91,7 +91,7 @@ pub enum GameDownloadStatus { Downloading, Paused, Uninitialised, - Error(GameDownloadError), + Error, } impl DownloadManagerBuilder { @@ -143,9 +143,9 @@ impl DownloadManagerBuilder { } return Ok(()); } - DownloadManagerSignal::Error(game_download_error) => { - self.manage_error_signal(game_download_error); - } + DownloadManagerSignal::Error(e) => { + self.manage_error_signal(e); + }, }; } } @@ -178,6 +178,7 @@ impl DownloadManagerBuilder { id.clone(), version, target_download_dir, + self.sender.clone() )); let agent_status = GameDownloadStatus::Uninitialised; let interface_data = Arc::new(AgentInterfaceData { @@ -217,11 +218,14 @@ impl DownloadManagerBuilder { info!("Spawning download"); spawn(move || { - let signal = match download_agent.download() { - Ok(_) => DownloadManagerSignal::Completed(download_agent.id.clone()), - Err(e) => DownloadManagerSignal::Error(e), + match download_agent.download() { + Ok(_) => { + sender.send(DownloadManagerSignal::Completed(download_agent.id.clone())); + }, + Err(_) => { + todo!() // Account for if the setup_download function fails + }, }; - sender.send(signal).unwrap(); }); info!("Finished spawning Download"); @@ -237,7 +241,7 @@ impl DownloadManagerBuilder { fn manage_error_signal(&self, error: GameDownloadError) { let current_status = self.current_game_interface.clone().unwrap(); let mut lock = current_status.status.lock().unwrap(); - *lock = GameDownloadStatus::Error(error); + *lock = GameDownloadStatus::Error; self.set_status(DownloadManagerStatus::Error); } fn set_status(&self, status: DownloadManagerStatus) { diff --git a/src-tauri/src/downloads/download_manager_interface.rs b/src-tauri/src/downloads/download_manager_interface.rs index 00fd204..d626267 100644 --- a/src-tauri/src/downloads/download_manager_interface.rs +++ b/src-tauri/src/downloads/download_manager_interface.rs @@ -74,6 +74,12 @@ impl DownloadManager { ))?; self.command_sender.send(DownloadManagerSignal::Go) } + pub fn cancel_download( + &self, + id: String + ) { + todo!() + } pub fn edit(&self) -> MutexGuard<'_, VecDeque>> { self.download_queue.lock().unwrap() }