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();
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?;

View File

@ -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) {

View File

@ -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<AppState>>,
) -> 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<AppState>>,
) -> Result<f64, String> {
let progress = state
) -> Result<f64, AppError> {
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(

View File

@ -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)

View File

@ -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<String, Arc<GameDownloadAgent>>,
download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>,
command_receiver: Receiver<DownloadManagerSignal>,
@ -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;

View File

@ -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<Result<(), ()>>,
download_queue: Arc<Mutex<VecDeque<Arc<AgentInterfaceData>>>>,
progress: Arc<Mutex<Option<ProgressObject>>>,
@ -45,7 +45,7 @@ impl From<Arc<GameDownloadAgent>> for AgentInterfaceData {
}
}
impl DownloadManagerInterface {
impl DownloadManager {
pub fn new(
terminator: JoinHandle<Result<(), ()>>,
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 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<String, Game>,
#[serde(skip_serializing)]
download_manager: Arc<DownloadManagerInterface>,
download_manager: Arc<DownloadManager>,
}
#[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 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 {

View File

@ -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<String, RemoteAccessError> {
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<String, RemoteAccessError> {
}
#[tauri::command]
pub fn fetch_library(app: AppHandle) -> Result<String, String> {
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<String, AppError> {
fetch_library_logic(app)
.map_err(|e| AppError::RemoteAccess(e.to_string()))
}
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
.games
.games_statuses
.get(&game.id)
.get(&game.game_id)
.unwrap()
.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
Err("".to_string().into())
Err(RemoteAccessError::GameNotFound)
}
#[tauri::command]

View File

@ -3,29 +3,45 @@ use std::{
sync::{Arc, Mutex},
};
use http::StatusCode;
use log::{info, warn};
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use url::{ParseError, Url};
use crate::{AppState, AppStatus, DB};
use crate::{AppError, AppState, AppStatus, DB};
#[derive(Debug, Clone)]
pub enum RemoteAccessError {
FetchError(Arc<reqwest::Error>),
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<reqwest::Error> for RemoteAccessError {
RemoteAccessError::FetchError(Arc::new(err))
}
}
impl From<String> for RemoteAccessError {
fn from(err: String) -> Self {
RemoteAccessError::GenericErrror(err)
}
}
impl From<ParseError> 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();