Implement better error system and segregate errors and commands (#23)

* chore: Progress on amend_settings command

Signed-off-by: quexeky <git@quexeky.dev>

* chore(errors): Progress on better error handling with segragation of files

* chore: Progress on amend_settings command

Signed-off-by: quexeky <git@quexeky.dev>

* chore(commands): Separated commands under each subdirectory into respective commands.rs files

Signed-off-by: quexeky <git@quexeky.dev>

* chore(errors): Almost all errors and commands have been segregated

* chore(errors): Added drop server error

Signed-off-by: quexeky <git@quexeky.dev>

* feat(core): Update to using nightly compiler

Signed-off-by: quexeky <git@quexeky.dev>

* chore(errors): More progress on error handling

Signed-off-by: quexeky <git@quexeky.dev>

* chore(errors): Implementing Try and FromResidual for UserValue

Signed-off-by: quexeky <git@quexeky.dev>

* refactor(errors): Segregated errors and commands from code, and made commands return UserValue struct

Signed-off-by: quexeky <git@quexeky.dev>

* fix(errors): Added missing files

* chore(errors): Convert match statement to map_err

* feat(settings): Implemented settings editing from UI

* feat(errors): Clarified return values from retry_connect command

* chore(errors): Moved autostart commands to autostart.rs

* chore(process manager): Converted launch_process function for games to use game_id

---------

Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
quexeky
2025-01-13 21:44:57 +11:00
committed by GitHub
parent fd3e539205
commit f1bba5cc74
45 changed files with 822 additions and 600 deletions
@@ -0,0 +1,30 @@
use std::{
fmt::{Display, Formatter},
io,
};
use super::{remote_access_error::RemoteAccessError, setup_error::SetupError};
// TODO: Rename / separate from downloads
#[derive(Debug, Clone)]
pub enum ApplicationDownloadError {
Communication(RemoteAccessError),
Checksum,
Setup(SetupError),
Lock,
IoError(io::ErrorKind),
DownloadError,
}
impl Display for ApplicationDownloadError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ApplicationDownloadError::Communication(error) => write!(f, "{}", error),
ApplicationDownloadError::Setup(error) => write!(f, "An error occurred while setting up the download: {}", error),
ApplicationDownloadError::Lock => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"),
ApplicationDownloadError::Checksum => write!(f, "Checksum failed to validate for download"),
ApplicationDownloadError::IoError(error) => write!(f, "{}", error),
ApplicationDownloadError::DownloadError => write!(f, "Download failed. See Download Manager status for specific error"),
}
}
}
@@ -0,0 +1,10 @@
use serde::Deserialize;
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DropServerError {
pub status_code: usize,
pub status_message: String,
pub message: String,
pub url: String,
}
@@ -0,0 +1,16 @@
use std::fmt::Display;
pub enum LibraryError {
MetaNotFound(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 {} in the database",
id
),
}
}
}
+7
View File
@@ -0,0 +1,7 @@
pub mod application_download_error;
pub mod drop_server_error;
pub mod library_error;
pub mod process_error;
pub mod remote_access_error;
pub mod setup_error;
pub mod user_error;
@@ -0,0 +1,28 @@
use std::{fmt::Display, io::Error};
pub enum ProcessError {
SetupRequired,
NotInstalled,
AlreadyRunning,
NotDownloaded,
InvalidID,
InvalidVersion,
IOError(Error),
InvalidPlatform,
}
impl Display for ProcessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
ProcessError::SetupRequired => "Game not set up",
ProcessError::NotInstalled => "Game not installed",
ProcessError::AlreadyRunning => "Game already running",
ProcessError::NotDownloaded => "Game not downloaded",
ProcessError::InvalidID => "Invalid Game ID",
ProcessError::InvalidVersion => "Invalid Game version",
ProcessError::IOError(error) => &error.to_string(),
ProcessError::InvalidPlatform => "This Game cannot be played on the current platform",
};
write!(f, "{}", s)
}
}
@@ -0,0 +1,68 @@
use std::{
error::Error,
fmt::{Display, Formatter},
sync::Arc,
};
use http::StatusCode;
use url::ParseError;
use super::drop_server_error::DropServerError;
#[derive(Debug, Clone)]
pub enum RemoteAccessError {
FetchError(Arc<reqwest::Error>),
ParsingError(ParseError),
InvalidEndpoint,
HandshakeFailed(String),
GameNotFound,
InvalidResponse(DropServerError),
InvalidRedirect,
ManifestDownloadFailed(StatusCode, String),
OutOfSync,
Generic(String),
}
impl Display for RemoteAccessError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
RemoteAccessError::FetchError(error) => write!(
f,
"{}: {}",
error,
error
.source()
.map(|e| e.to_string())
.or_else(|| Some("Unknown error".to_string()))
.unwrap()
),
RemoteAccessError::ParsingError(parse_error) => {
write!(f, "{}", parse_error)
}
RemoteAccessError::InvalidEndpoint => write!(f, "Invalid drop endpoint"),
RemoteAccessError::HandshakeFailed(message) => write!(f, "Failed to complete handshake: {}", message),
RemoteAccessError::GameNotFound => write!(f, "Could not find game on server"),
RemoteAccessError::InvalidResponse(error) => write!(f, "Server returned an invalid response: {} {}", error.status_code, error.status_message),
RemoteAccessError::InvalidRedirect => write!(f, "Server redirect was invalid"),
RemoteAccessError::ManifestDownloadFailed(status, response) => write!(
f,
"Failed to download game manifest: {} {}",
status, response
),
RemoteAccessError::OutOfSync => write!(f, "Server's and client's time are out of sync. Please ensure they are within at least 30 seconds of each other."),
RemoteAccessError::Generic(message) => write!(f, "{}", message),
}
}
}
impl From<reqwest::Error> for RemoteAccessError {
fn from(err: reqwest::Error) -> Self {
RemoteAccessError::FetchError(Arc::new(err))
}
}
impl From<ParseError> for RemoteAccessError {
fn from(err: ParseError) -> Self {
RemoteAccessError::ParsingError(err)
}
}
impl std::error::Error for RemoteAccessError {}
@@ -0,0 +1,14 @@
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone)]
pub enum SetupError {
Context,
}
impl Display for SetupError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SetupError::Context => write!(f, "Failed to generate contexts for download"),
}
}
}
+66
View File
@@ -0,0 +1,66 @@
use std::{
fmt::Display,
ops::{FromResidual, Try},
};
use serde::Serialize;
pub enum UserValue<T, D>
where
T: Serialize,
D: Display,
{
Ok(T),
Err(D),
}
impl<T: Serialize, D: Display> Serialize for UserValue<T, D> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
UserValue::Ok(data) => data.serialize(serializer),
UserValue::Err(err) => serializer.serialize_str(err.to_string().as_ref()),
}
}
}
impl<T: Serialize, D: Display> From<Result<T, D>> for UserValue<T, D> {
fn from(value: Result<T, D>) -> Self {
match value {
Ok(data) => UserValue::Ok(data),
Err(data) => UserValue::Err(data),
}
}
}
impl<T: Serialize, D: Display> Try for UserValue<T, D> {
type Output = T;
type Residual = D;
fn from_output(output: Self::Output) -> Self {
Self::Ok(output)
}
fn branch(self) -> std::ops::ControlFlow<Self::Residual, Self::Output> {
match self {
UserValue::Ok(data) => std::ops::ControlFlow::Continue(data),
UserValue::Err(e) => std::ops::ControlFlow::Break(e),
}
}
}
impl<T: Serialize, D: Display> FromResidual for UserValue<T, D> {
fn from_residual(residual: <Self as std::ops::Try>::Residual) -> Self {
UserValue::Err(residual)
}
}
impl<T: Serialize, D: Display, U> FromResidual<Result<U, D>> for UserValue<T, D> {
fn from_residual(residual: Result<U, D>) -> Self {
match residual {
Ok(_) => unreachable!(),
Err(e) => UserValue::Err(e),
}
}
}