From 639a1f52709829da8a7fd481cd2aa1fb3a57edd2 Mon Sep 17 00:00:00 2001 From: quexeky Date: Mon, 18 Nov 2024 13:21:20 +1100 Subject: [PATCH] style(downloads): Made all errors type-based Signed-off-by: quexeky --- desktop/src-tauri/src/auth.rs | 8 +--- .../src-tauri/src/downloads/download_agent.rs | 38 +++++++++---------- .../src/downloads/download_commands.rs | 38 ++++++------------- .../src-tauri/src/downloads/download_logic.rs | 10 ++--- .../src/downloads/download_manager.rs | 17 ++++----- .../downloads/download_manager_interface.rs | 4 +- desktop/src-tauri/src/lib.rs | 19 +++++++--- desktop/src-tauri/src/library.rs | 24 +++++------- desktop/src-tauri/src/remote.rs | 31 ++++++++++----- 9 files changed, 89 insertions(+), 100 deletions(-) diff --git a/desktop/src-tauri/src/auth.rs b/desktop/src-tauri/src/auth.rs index 7619e8ae..78da5b2a 100644 --- a/desktop/src-tauri/src/auth.rs +++ b/desktop/src-tauri/src/auth.rs @@ -93,9 +93,7 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc let path_chunks: Vec<&str> = path.split("/").collect(); if path_chunks.len() != 3 { app.emit("auth/failed", ()).unwrap(); - return Err(RemoteAccessError::GenericErrror( - "Invalid number of handshake chunks".to_string(), - )); + return Err(RemoteAccessError::InvalidResponse); } let base_url = { @@ -165,9 +163,7 @@ async fn auth_initiate_wrapper() -> Result<(), RemoteAccessError> { let response = client.post(endpoint.to_string()).json(&body).send().await?; if response.status() != 200 { - return Err("Failed to create redirect URL. Please try again later." - .to_string() - .into()); + return Err(RemoteAccessError::InvalidRedirect); } let redir_url = response.text().await?; diff --git a/desktop/src-tauri/src/downloads/download_agent.rs b/desktop/src-tauri/src/downloads/download_agent.rs index 14a61e0e..0b5e6104 100644 --- a/desktop/src-tauri/src/downloads/download_agent.rs +++ b/desktop/src-tauri/src/downloads/download_agent.rs @@ -28,21 +28,26 @@ pub struct GameDownloadAgent { pub progress: ProgressObject, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum GameDownloadError { - CommunicationError(RemoteAccessError), - ChecksumError, - SetupError(String), - LockError, + Communication(RemoteAccessError), + Checksum, + Setup(SetupError), + Lock, +} + +#[derive(Debug)] +pub enum SetupError { + Context } impl Display for GameDownloadError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - GameDownloadError::CommunicationError(error) => write!(f, "{}", error), - GameDownloadError::SetupError(error) => write!(f, "{}", error), - GameDownloadError::LockError => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"), - GameDownloadError::ChecksumError => write!(f, "Checksum failed to validate for download"), + GameDownloadError::Communication(error) => write!(f, "{}", error), + 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"), } } } @@ -114,13 +119,8 @@ impl GameDownloadAgent { .unwrap(); if response.status() != 200 { - return Err(GameDownloadError::CommunicationError( - format!( - "Failed to download game manifest: {} {}", - response.status(), - response.text().unwrap() - ) - .into(), + return Err(GameDownloadError::Communication( + RemoteAccessError::ManifestDownloadFailed(response.status(), response.text().unwrap()) )); } @@ -143,7 +143,7 @@ impl GameDownloadAgent { return Ok(()); } - Err(GameDownloadError::LockError) + Err(GameDownloadError::Lock) } pub fn generate_contexts(&self) -> Result<(), GameDownloadError> { @@ -194,9 +194,7 @@ impl GameDownloadAgent { return Ok(()); } - Err(GameDownloadError::SetupError( - "Failed to generate download contexts".to_owned(), - )) + Err(GameDownloadError::Setup(SetupError::Context)) } pub fn run(&self) { diff --git a/desktop/src-tauri/src/downloads/download_commands.rs b/desktop/src-tauri/src/downloads/download_commands.rs index df48f4ed..d7d91555 100644 --- a/desktop/src-tauri/src/downloads/download_commands.rs +++ b/desktop/src-tauri/src/downloads/download_commands.rs @@ -1,52 +1,38 @@ use std::sync::Mutex; -use crate::AppState; +use serde::Serialize; + +use crate::{AppError, AppState}; #[tauri::command] pub fn download_game( game_id: String, game_version: String, state: tauri::State<'_, Mutex>, -) -> Result<(), String> { - /* - info!("beginning game download..."); - - let mut download_agent = GameDownloadAgent::new(game_id.clone(), game_version.clone(), 0); - // Setup download requires mutable - download_agent.setup_download().unwrap(); - - let mut lock: std::sync::MutexGuard<'_, AppState> = state.lock().unwrap(); - let download_agent_ref = Arc::new(download_agent); - lock.download_manager - .insert(game_id, download_agent_ref.clone()); - - // Run it in another thread - spawn(move || { - // Run doesn't require mutable - download_agent_ref.clone().run(); - }); - */ +) -> Result<(), AppError> { + state .lock() .unwrap() .download_manager .queue_game(game_id, game_version, 0) - .unwrap(); - Ok(()) + .map_err(|_| AppError::Signal) } #[tauri::command] pub fn get_current_game_download_progress( state: tauri::State<'_, Mutex>, -) -> Result { - let progress = state +) -> Result { + match state .lock() .unwrap() .download_manager .get_current_game_download_progress() - .unwrap_or(0.0); + { + Some(progress) => Ok(progress), + None => Err(AppError::DoesNotExist), + } - Ok(progress) } /* fn use_download_agent( diff --git a/desktop/src-tauri/src/downloads/download_logic.rs b/desktop/src-tauri/src/downloads/download_logic.rs index 9478b1e7..f094c96d 100644 --- a/desktop/src-tauri/src/downloads/download_logic.rs +++ b/desktop/src-tauri/src/downloads/download_logic.rs @@ -148,7 +148,7 @@ pub fn download_game_chunk( .get(chunk_url) .header("Authorization", header) .send() - .map_err(|e| GameDownloadError::CommunicationError(e.into()))?; + .map_err(|e| GameDownloadError::Communication(e.into()))?; let mut destination = DropWriter::new(ctx.path); @@ -160,11 +160,7 @@ pub fn download_game_chunk( let content_length = response.content_length(); if content_length.is_none() { - return Err(GameDownloadError::CommunicationError( - RemoteAccessError::GenericErrror( - "Invalid download endpoint, missing Content-Length header.".to_owned(), - ), - )); + return Err(GameDownloadError::Communication(RemoteAccessError::InvalidResponse)); } let mut pipeline = DropDownloadPipeline::new( @@ -184,7 +180,7 @@ pub fn download_game_chunk( let res = hex::encode(checksum.0); if res != ctx.checksum { - return Err(GameDownloadError::ChecksumError); + return Err(GameDownloadError::Checksum); } Ok(true) diff --git a/desktop/src-tauri/src/downloads/download_manager.rs b/desktop/src-tauri/src/downloads/download_manager.rs index 48779953..885ed444 100644 --- a/desktop/src-tauri/src/downloads/download_manager.rs +++ b/desktop/src-tauri/src/downloads/download_manager.rs @@ -11,7 +11,7 @@ use log::info; use super::{ download_agent::{GameDownloadAgent, GameDownloadError}, - download_manager_interface::{AgentInterfaceData, DownloadManagerInterface}, + download_manager_interface::{AgentInterfaceData, DownloadManager}, download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}, progress_object::ProgressObject, }; @@ -53,7 +53,7 @@ Behold, my madness - quexeky */ -pub struct DownloadManager { +pub struct DownloadManagerBuilder { download_agent_registry: HashMap>, download_queue: Arc>>>, command_receiver: Receiver, @@ -85,9 +85,8 @@ pub enum DownloadManagerStatus { Downloading, Paused, Empty, - Error(GameDownloadError), + Error, } -#[derive(Clone)] pub enum GameDownloadStatus { Downloading, Paused, @@ -95,8 +94,8 @@ pub enum GameDownloadStatus { Error(GameDownloadError), } -impl DownloadManager { - pub fn generate() -> DownloadManagerInterface { +impl DownloadManagerBuilder { + pub fn build() -> DownloadManager { let queue = Arc::new(Mutex::new(VecDeque::new())); let (command_sender, command_receiver) = channel(); let active_progress = Arc::new(Mutex::new(None)); @@ -115,7 +114,7 @@ impl DownloadManager { let terminator = spawn(|| manager.manage_queue()); - DownloadManagerInterface::new(terminator, queue, active_progress, command_sender) + DownloadManager::new(terminator, queue, active_progress, command_sender) } fn manage_queue(mut self) -> Result<(), ()> { @@ -238,8 +237,8 @@ impl DownloadManager { 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.clone()); - self.set_status(DownloadManagerStatus::Error(error)); + *lock = GameDownloadStatus::Error(error); + self.set_status(DownloadManagerStatus::Error); } fn set_status(&self, status: DownloadManagerStatus) { *self.status.lock().unwrap() = status; diff --git a/desktop/src-tauri/src/downloads/download_manager_interface.rs b/desktop/src-tauri/src/downloads/download_manager_interface.rs index b5d56197..00fd2040 100644 --- a/desktop/src-tauri/src/downloads/download_manager_interface.rs +++ b/desktop/src-tauri/src/downloads/download_manager_interface.rs @@ -26,7 +26,7 @@ use super::{ /// The actual download queue may be accessed through the .edit() function, /// which provides raw access to the underlying queue. /// THIS EDITING IS BLOCKING!!! -pub struct DownloadManagerInterface { +pub struct DownloadManager { terminator: JoinHandle>, download_queue: Arc>>>, progress: Arc>>, @@ -45,7 +45,7 @@ impl From> for AgentInterfaceData { } } -impl DownloadManagerInterface { +impl DownloadManager { pub fn new( terminator: JoinHandle>, download_queue: Arc>>>, diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 8982d9d6..5f9bd1fc 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -12,13 +12,13 @@ use crate::db::DatabaseImpls; use auth::{auth_initiate, generate_authorization_header, recieve_handshake}; use db::{add_new_download_dir, DatabaseInterface, DATA_ROOT_DIR}; use downloads::download_commands::*; -use downloads::download_manager::DownloadManager; -use downloads::download_manager_interface::DownloadManagerInterface; +use downloads::download_manager::DownloadManagerBuilder; +use downloads::download_manager_interface::DownloadManager; use env_logger::Env; use http::{header::*, response::Builder as ResponseBuilder}; use library::{fetch_game, fetch_library, Game}; use log::info; -use remote::{gen_drop_url, use_remote}; +use remote::{gen_drop_url, use_remote, RemoteAccessError}; use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::{ @@ -36,6 +36,13 @@ pub enum AppStatus { SignedInNeedsReauth, ServerUnavailable, } +#[derive(Debug, Serialize)] +pub enum AppError { + DoesNotExist, + Signal, + RemoteAccess(String) +} + #[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct User { @@ -54,11 +61,11 @@ pub struct AppState { games: HashMap, #[serde(skip_serializing)] - download_manager: Arc, + download_manager: Arc, } #[tauri::command] -fn fetch_state(state: tauri::State<'_, Mutex>) -> Result { +fn fetch_state(state: tauri::State<'_, Mutex>) -> Result { let guard = state.lock().unwrap(); let cloned_state = guard.clone(); drop(guard); @@ -69,7 +76,7 @@ fn setup() -> AppState { env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); let games = HashMap::new(); - let download_manager = Arc::new(DownloadManager::generate()); + let download_manager = Arc::new(DownloadManagerBuilder::build()); let is_set_up = DB.database_is_set_up(); if !is_set_up { diff --git a/desktop/src-tauri/src/library.rs b/desktop/src-tauri/src/library.rs index 65f9ec10..11277494 100644 --- a/desktop/src-tauri/src/library.rs +++ b/desktop/src-tauri/src/library.rs @@ -7,6 +7,7 @@ use tauri::{AppHandle, Manager}; use crate::db::DatabaseGameStatus; use crate::db::DatabaseImpls; use crate::remote::RemoteAccessError; +use crate::AppError; use crate::{auth::generate_authorization_header, AppState, DB}; #[derive(serde::Serialize)] @@ -18,7 +19,7 @@ struct FetchGameStruct { #[derive(Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Game { - id: String, + game_id: String, m_name: String, m_short_description: String, m_description: String, @@ -54,12 +55,12 @@ fn fetch_library_logic(app: AppHandle) -> Result { let mut db_handle = DB.borrow_data_mut().unwrap(); for game in games.iter() { - handle.games.insert(game.id.clone(), game.clone()); - if !db_handle.games.games_statuses.contains_key(&game.id) { + handle.games.insert(game.game_id.clone(), game.clone()); + if !db_handle.games.games_statuses.contains_key(&game.game_id) { db_handle .games .games_statuses - .insert(game.id.clone(), DatabaseGameStatus::Remote); + .insert(game.game_id.clone(), DatabaseGameStatus::Remote); } } @@ -69,14 +70,9 @@ fn fetch_library_logic(app: AppHandle) -> Result { } #[tauri::command] -pub fn fetch_library(app: AppHandle) -> Result { - let result = fetch_library_logic(app); - - if result.is_err() { - return Err(result.err().unwrap().to_string()); - } - - Ok(result.unwrap()) +pub fn fetch_library(app: AppHandle) -> Result { + fetch_library_logic(app) + .map_err(|e| AppError::RemoteAccess(e.to_string())) } fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result { @@ -92,7 +88,7 @@ fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result Result), ParsingError(ParseError), InvalidCodeError(u16), - GenericErrror(String), + InvalidEndpoint, + HandshakeFailed, + GameNotFound, + InvalidResponse, + InvalidRedirect, + ManifestDownloadFailed(StatusCode, String) } impl Display for RemoteAccessError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { RemoteAccessError::FetchError(error) => write!(f, "{}", error), - RemoteAccessError::GenericErrror(error) => write!(f, "{}", error), RemoteAccessError::ParsingError(parse_error) => { write!(f, "{}", parse_error) } RemoteAccessError::InvalidCodeError(error) => write!(f, "HTTP {}", error), + RemoteAccessError::ParsingError(parse_error) => todo!(), + RemoteAccessError::InvalidEndpoint => write!(f, "Invalid drop endpoint"), + RemoteAccessError::HandshakeFailed => write!(f, "Failed to complete handshake"), + RemoteAccessError::GameNotFound => write!(f, "Could not find game on server"), + RemoteAccessError::InvalidResponse => write!(f, "Server returned an invalid response"), + RemoteAccessError::InvalidRedirect => write!(f, "Server redirect was invalid"), + RemoteAccessError::ManifestDownloadFailed(status, response) => + write!(f, "Failed to download game manifest: {} {}", + status, + response + ), } } } @@ -35,11 +51,6 @@ impl From for RemoteAccessError { RemoteAccessError::FetchError(Arc::new(err)) } } -impl From for RemoteAccessError { - fn from(err: String) -> Self { - RemoteAccessError::GenericErrror(err) - } -} impl From for RemoteAccessError { fn from(err: ParseError) -> Self { RemoteAccessError::ParsingError(err) @@ -74,7 +85,7 @@ async fn use_remote_logic<'a>( if result.app_name != "Drop" { warn!("user entered drop endpoint that connected, but wasn't identified as Drop"); - return Err("Not a valid Drop endpoint".to_string().into()); + return Err(RemoteAccessError::InvalidEndpoint); } let mut app_state = state.lock().unwrap();