style(downloads): Made all errors type-based

Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
quexeky
2024-11-18 13:21:20 +11:00
parent bd3deacf38
commit ec2f4148e8
9 changed files with 89 additions and 100 deletions

View File

@ -93,9 +93,7 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc
let path_chunks: Vec<&str> = path.split("/").collect(); let path_chunks: Vec<&str> = path.split("/").collect();
if path_chunks.len() != 3 { if path_chunks.len() != 3 {
app.emit("auth/failed", ()).unwrap(); app.emit("auth/failed", ()).unwrap();
return Err(RemoteAccessError::GenericErrror( return Err(RemoteAccessError::InvalidResponse);
"Invalid number of handshake chunks".to_string(),
));
} }
let base_url = { 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?; let response = client.post(endpoint.to_string()).json(&body).send().await?;
if response.status() != 200 { if response.status() != 200 {
return Err("Failed to create redirect URL. Please try again later." return Err(RemoteAccessError::InvalidRedirect);
.to_string()
.into());
} }
let redir_url = response.text().await?; let redir_url = response.text().await?;

View File

@ -28,21 +28,26 @@ pub struct GameDownloadAgent {
pub progress: ProgressObject, pub progress: ProgressObject,
} }
#[derive(Debug, Clone)] #[derive(Debug)]
pub enum GameDownloadError { pub enum GameDownloadError {
CommunicationError(RemoteAccessError), Communication(RemoteAccessError),
ChecksumError, Checksum,
SetupError(String), Setup(SetupError),
LockError, Lock,
}
#[derive(Debug)]
pub enum SetupError {
Context
} }
impl Display for GameDownloadError { impl Display for GameDownloadError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
GameDownloadError::CommunicationError(error) => write!(f, "{}", error), GameDownloadError::Communication(error) => write!(f, "{}", error),
GameDownloadError::SetupError(error) => write!(f, "{}", error), GameDownloadError::Setup(error) => write!(f, "{:?}", error),
GameDownloadError::LockError => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"), GameDownloadError::Lock => 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::Checksum => write!(f, "Checksum failed to validate for download"),
} }
} }
} }
@ -114,13 +119,8 @@ impl GameDownloadAgent {
.unwrap(); .unwrap();
if response.status() != 200 { if response.status() != 200 {
return Err(GameDownloadError::CommunicationError( return Err(GameDownloadError::Communication(
format!( RemoteAccessError::ManifestDownloadFailed(response.status(), response.text().unwrap())
"Failed to download game manifest: {} {}",
response.status(),
response.text().unwrap()
)
.into(),
)); ));
} }
@ -143,7 +143,7 @@ impl GameDownloadAgent {
return Ok(()); return Ok(());
} }
Err(GameDownloadError::LockError) Err(GameDownloadError::Lock)
} }
pub fn generate_contexts(&self) -> Result<(), GameDownloadError> { pub fn generate_contexts(&self) -> Result<(), GameDownloadError> {
@ -194,9 +194,7 @@ impl GameDownloadAgent {
return Ok(()); return Ok(());
} }
Err(GameDownloadError::SetupError( Err(GameDownloadError::Setup(SetupError::Context))
"Failed to generate download contexts".to_owned(),
))
} }
pub fn run(&self) { pub fn run(&self) {

View File

@ -1,52 +1,38 @@
use std::sync::Mutex; use std::sync::Mutex;
use crate::AppState; use serde::Serialize;
use crate::{AppError, AppState};
#[tauri::command] #[tauri::command]
pub fn download_game( pub fn download_game(
game_id: String, game_id: String,
game_version: String, game_version: String,
state: tauri::State<'_, Mutex<AppState>>, state: tauri::State<'_, Mutex<AppState>>,
) -> Result<(), String> { ) -> Result<(), AppError> {
/*
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();
});
*/
state state
.lock() .lock()
.unwrap() .unwrap()
.download_manager .download_manager
.queue_game(game_id, game_version, 0) .queue_game(game_id, game_version, 0)
.unwrap(); .map_err(|_| AppError::Signal)
Ok(())
} }
#[tauri::command] #[tauri::command]
pub fn get_current_game_download_progress( pub fn get_current_game_download_progress(
state: tauri::State<'_, Mutex<AppState>>, state: tauri::State<'_, Mutex<AppState>>,
) -> Result<f64, String> { ) -> Result<f64, AppError> {
let progress = state match state
.lock() .lock()
.unwrap() .unwrap()
.download_manager .download_manager
.get_current_game_download_progress() .get_current_game_download_progress()
.unwrap_or(0.0); {
Some(progress) => Ok(progress),
None => Err(AppError::DoesNotExist),
}
Ok(progress)
} }
/* /*
fn use_download_agent( fn use_download_agent(

View File

@ -148,7 +148,7 @@ pub fn download_game_chunk(
.get(chunk_url) .get(chunk_url)
.header("Authorization", header) .header("Authorization", header)
.send() .send()
.map_err(|e| GameDownloadError::CommunicationError(e.into()))?; .map_err(|e| GameDownloadError::Communication(e.into()))?;
let mut destination = DropWriter::new(ctx.path); let mut destination = DropWriter::new(ctx.path);
@ -160,11 +160,7 @@ pub fn download_game_chunk(
let content_length = response.content_length(); let content_length = response.content_length();
if content_length.is_none() { if content_length.is_none() {
return Err(GameDownloadError::CommunicationError( return Err(GameDownloadError::Communication(RemoteAccessError::InvalidResponse));
RemoteAccessError::GenericErrror(
"Invalid download endpoint, missing Content-Length header.".to_owned(),
),
));
} }
let mut pipeline = DropDownloadPipeline::new( let mut pipeline = DropDownloadPipeline::new(
@ -184,7 +180,7 @@ pub fn download_game_chunk(
let res = hex::encode(checksum.0); let res = hex::encode(checksum.0);
if res != ctx.checksum { if res != ctx.checksum {
return Err(GameDownloadError::ChecksumError); return Err(GameDownloadError::Checksum);
} }
Ok(true) Ok(true)

View File

@ -11,7 +11,7 @@ use log::info;
use super::{ use super::{
download_agent::{GameDownloadAgent, GameDownloadError}, download_agent::{GameDownloadAgent, GameDownloadError},
download_manager_interface::{AgentInterfaceData, DownloadManagerInterface}, download_manager_interface::{AgentInterfaceData, DownloadManager},
download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}, download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag},
progress_object::ProgressObject, progress_object::ProgressObject,
}; };
@ -53,7 +53,7 @@ Behold, my madness - quexeky
*/ */
pub struct DownloadManager { pub struct DownloadManagerBuilder {
download_agent_registry: HashMap<String, Arc<GameDownloadAgent>>, download_agent_registry: HashMap<String, Arc<GameDownloadAgent>>,
download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>, download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>,
command_receiver: Receiver<DownloadManagerSignal>, command_receiver: Receiver<DownloadManagerSignal>,
@ -85,9 +85,8 @@ pub enum DownloadManagerStatus {
Downloading, Downloading,
Paused, Paused,
Empty, Empty,
Error(GameDownloadError), Error,
} }
#[derive(Clone)]
pub enum GameDownloadStatus { pub enum GameDownloadStatus {
Downloading, Downloading,
Paused, Paused,
@ -95,8 +94,8 @@ pub enum GameDownloadStatus {
Error(GameDownloadError), Error(GameDownloadError),
} }
impl DownloadManager { impl DownloadManagerBuilder {
pub fn generate() -> DownloadManagerInterface { pub fn build() -> DownloadManager {
let queue = Arc::new(Mutex::new(VecDeque::new())); let queue = Arc::new(Mutex::new(VecDeque::new()));
let (command_sender, command_receiver) = channel(); let (command_sender, command_receiver) = channel();
let active_progress = Arc::new(Mutex::new(None)); let active_progress = Arc::new(Mutex::new(None));
@ -115,7 +114,7 @@ impl DownloadManager {
let terminator = spawn(|| manager.manage_queue()); 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<(), ()> { fn manage_queue(mut self) -> Result<(), ()> {
@ -238,8 +237,8 @@ impl DownloadManager {
fn manage_error_signal(&self, error: GameDownloadError) { fn manage_error_signal(&self, error: GameDownloadError) {
let current_status = self.current_game_interface.clone().unwrap(); let current_status = self.current_game_interface.clone().unwrap();
let mut lock = current_status.status.lock().unwrap(); let mut lock = current_status.status.lock().unwrap();
*lock = GameDownloadStatus::Error(error.clone()); *lock = GameDownloadStatus::Error(error);
self.set_status(DownloadManagerStatus::Error(error)); self.set_status(DownloadManagerStatus::Error);
} }
fn set_status(&self, status: DownloadManagerStatus) { fn set_status(&self, status: DownloadManagerStatus) {
*self.status.lock().unwrap() = status; *self.status.lock().unwrap() = status;

View File

@ -26,7 +26,7 @@ use super::{
/// The actual download queue may be accessed through the .edit() function, /// The actual download queue may be accessed through the .edit() function,
/// which provides raw access to the underlying queue. /// which provides raw access to the underlying queue.
/// THIS EDITING IS BLOCKING!!! /// THIS EDITING IS BLOCKING!!!
pub struct DownloadManagerInterface { pub struct DownloadManager {
terminator: JoinHandle<Result<(), ()>>, terminator: JoinHandle<Result<(), ()>>,
download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>, download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>,
progress: Arc<Mutex<Option<ProgressObject>>>, progress: Arc<Mutex<Option<ProgressObject>>>,
@ -45,7 +45,7 @@ impl From<Arc<GameDownloadAgent>> for AgentInterfaceData {
} }
} }
impl DownloadManagerInterface { impl DownloadManager {
pub fn new( pub fn new(
terminator: JoinHandle<Result<(), ()>>, terminator: JoinHandle<Result<(), ()>>,
download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>, download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>,

View File

@ -12,13 +12,13 @@ use crate::db::DatabaseImpls;
use auth::{auth_initiate, generate_authorization_header, recieve_handshake}; use auth::{auth_initiate, generate_authorization_header, recieve_handshake};
use db::{add_new_download_dir, DatabaseInterface, DATA_ROOT_DIR}; use db::{add_new_download_dir, DatabaseInterface, DATA_ROOT_DIR};
use downloads::download_commands::*; use downloads::download_commands::*;
use downloads::download_manager::DownloadManager; use downloads::download_manager::DownloadManagerBuilder;
use downloads::download_manager_interface::DownloadManagerInterface; use downloads::download_manager_interface::DownloadManager;
use env_logger::Env; use env_logger::Env;
use http::{header::*, response::Builder as ResponseBuilder}; use http::{header::*, response::Builder as ResponseBuilder};
use library::{fetch_game, fetch_library, Game}; use library::{fetch_game, fetch_library, Game};
use log::info; use log::info;
use remote::{gen_drop_url, use_remote}; use remote::{gen_drop_url, use_remote, RemoteAccessError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
use std::{ use std::{
@ -36,6 +36,13 @@ pub enum AppStatus {
SignedInNeedsReauth, SignedInNeedsReauth,
ServerUnavailable, ServerUnavailable,
} }
#[derive(Debug, Serialize)]
pub enum AppError {
DoesNotExist,
Signal,
RemoteAccess(String)
}
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct User { pub struct User {
@ -54,11 +61,11 @@ pub struct AppState {
games: HashMap<String, Game>, games: HashMap<String, Game>,
#[serde(skip_serializing)] #[serde(skip_serializing)]
download_manager: Arc<DownloadManagerInterface>, download_manager: Arc<DownloadManager>,
} }
#[tauri::command] #[tauri::command]
fn fetch_state(state: tauri::State<'_, Mutex<AppState>>) -> Result<AppState, String> { fn fetch_state(state: tauri::State<'_, Mutex<AppState>>) -> Result<AppState, AppError> {
let guard = state.lock().unwrap(); let guard = state.lock().unwrap();
let cloned_state = guard.clone(); let cloned_state = guard.clone();
drop(guard); drop(guard);
@ -69,7 +76,7 @@ fn setup() -> AppState {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let games = HashMap::new(); 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(); let is_set_up = DB.database_is_set_up();
if !is_set_up { if !is_set_up {

View File

@ -7,6 +7,7 @@ use tauri::{AppHandle, Manager};
use crate::db::DatabaseGameStatus; use crate::db::DatabaseGameStatus;
use crate::db::DatabaseImpls; use crate::db::DatabaseImpls;
use crate::remote::RemoteAccessError; use crate::remote::RemoteAccessError;
use crate::AppError;
use crate::{auth::generate_authorization_header, AppState, DB}; use crate::{auth::generate_authorization_header, AppState, DB};
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
@ -18,7 +19,7 @@ struct FetchGameStruct {
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Game { pub struct Game {
id: String, game_id: String,
m_name: String, m_name: String,
m_short_description: String, m_short_description: String,
m_description: String, m_description: String,
@ -54,12 +55,12 @@ fn fetch_library_logic(app: AppHandle) -> Result<String, RemoteAccessError> {
let mut db_handle = DB.borrow_data_mut().unwrap(); let mut db_handle = DB.borrow_data_mut().unwrap();
for game in games.iter() { for game in games.iter() {
handle.games.insert(game.id.clone(), game.clone()); handle.games.insert(game.game_id.clone(), game.clone());
if !db_handle.games.games_statuses.contains_key(&game.id) { if !db_handle.games.games_statuses.contains_key(&game.game_id) {
db_handle db_handle
.games .games
.games_statuses .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<String, RemoteAccessError> {
} }
#[tauri::command] #[tauri::command]
pub fn fetch_library(app: AppHandle) -> Result<String, String> { pub fn fetch_library(app: AppHandle) -> Result<String, AppError> {
let result = fetch_library_logic(app); fetch_library_logic(app)
.map_err(|e| AppError::RemoteAccess(e.to_string()))
if result.is_err() {
return Err(result.err().unwrap().to_string());
}
Ok(result.unwrap())
} }
fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result<String, RemoteAccessError> { fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result<String, RemoteAccessError> {
@ -92,7 +88,7 @@ fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result<String, RemoteA
status: db_handle status: db_handle
.games .games
.games_statuses .games_statuses
.get(&game.id) .get(&game.game_id)
.unwrap() .unwrap()
.clone(), .clone(),
}; };
@ -101,7 +97,7 @@ fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result<String, RemoteA
} }
// TODO request games that aren't found from remote server // TODO request games that aren't found from remote server
Err("".to_string().into()) Err(RemoteAccessError::GameNotFound)
} }
#[tauri::command] #[tauri::command]

View File

@ -3,29 +3,45 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use http::StatusCode;
use log::{info, warn}; use log::{info, warn};
use serde::Deserialize; use serde::{Deserialize, Serialize};
use url::{ParseError, Url}; use url::{ParseError, Url};
use crate::{AppState, AppStatus, DB}; use crate::{AppError, AppState, AppStatus, DB};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum RemoteAccessError { pub enum RemoteAccessError {
FetchError(Arc<reqwest::Error>), FetchError(Arc<reqwest::Error>),
ParsingError(ParseError), ParsingError(ParseError),
InvalidCodeError(u16), InvalidCodeError(u16),
GenericErrror(String), InvalidEndpoint,
HandshakeFailed,
GameNotFound,
InvalidResponse,
InvalidRedirect,
ManifestDownloadFailed(StatusCode, String)
} }
impl Display for RemoteAccessError { impl Display for RemoteAccessError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
RemoteAccessError::FetchError(error) => write!(f, "{}", error), RemoteAccessError::FetchError(error) => write!(f, "{}", error),
RemoteAccessError::GenericErrror(error) => write!(f, "{}", error),
RemoteAccessError::ParsingError(parse_error) => { RemoteAccessError::ParsingError(parse_error) => {
write!(f, "{}", parse_error) write!(f, "{}", parse_error)
} }
RemoteAccessError::InvalidCodeError(error) => write!(f, "HTTP {}", 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<reqwest::Error> for RemoteAccessError {
RemoteAccessError::FetchError(Arc::new(err)) RemoteAccessError::FetchError(Arc::new(err))
} }
} }
impl From<String> for RemoteAccessError {
fn from(err: String) -> Self {
RemoteAccessError::GenericErrror(err)
}
}
impl From<ParseError> for RemoteAccessError { impl From<ParseError> for RemoteAccessError {
fn from(err: ParseError) -> Self { fn from(err: ParseError) -> Self {
RemoteAccessError::ParsingError(err) RemoteAccessError::ParsingError(err)
@ -74,7 +85,7 @@ async fn use_remote_logic<'a>(
if result.app_name != "Drop" { if result.app_name != "Drop" {
warn!("user entered drop endpoint that connected, but wasn't identified as 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(); let mut app_state = state.lock().unwrap();