Skip to content

Commit

Permalink
Merge pull request #48 from oskardotglobal/feat-errors
Browse files Browse the repository at this point in the history
feat: error handling
  • Loading branch information
LDprg authored Oct 10, 2023
2 parents d603ce4 + 488f152 commit eed3c31
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 114 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ members = [
"winapps-cli",
"winapps-gui",
]
resolver = "2"
18 changes: 11 additions & 7 deletions winapps-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::{arg, Command};
use winapps::freerdp::freerdp_back::Freerdp;
use winapps::quickemu::{create_vm, kill_vm, start_vm};
use winapps::RemoteClient;
use winapps::{unwrap_or_panic, RemoteClient};

fn cli() -> Command {
Command::new("winapps-cli")
Expand Down Expand Up @@ -69,18 +69,22 @@ fn main() {
}

Some((_, _)) => {
cli.about("Command not found, try existing ones!")
.print_help()
.expect("Couldn't print help");
unwrap_or_panic!(
cli.about("Command not found, try existing ones!")
.print_help(),
"Couldn't print help"
);
}
_ => unreachable!(),
};
}

Some((_, _)) => {
cli.about("Command not found, try existing ones!")
.print_help()
.expect("Couldn't print help");
unwrap_or_panic!(
cli.about("Command not found, try existing ones!")
.print_help(),
"Couldn't print help"
);
}
_ => unreachable!(),
}
Expand Down
4 changes: 1 addition & 3 deletions winapps-gui/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
fn main() {
println!("Test lib: {}", winapps::add(1, 2));
}
fn main() {}
5 changes: 4 additions & 1 deletion winapps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.75"
derive-new = "0.5.9"
home = "0.5.5"
serde = { version = "1.0.171", features = ["derive"] }
toml = "0.7.6"
thiserror = "1.0.49"
toml = "0.8.2"
tracing = "0.1.37"
158 changes: 158 additions & 0 deletions winapps/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use std::error::Error;
use std::fmt::Debug;
use std::process::exit;

/// This enum represents all possible errors that can occur in this crate.
/// It is used as a return type for most functions should they return an error.
/// There's 2 base variants: `Message` and `WithError`.
/// `Message` is used for simple errors that don't have an underlying cause.
/// `WithError` is used for errors that occur from another error.
#[derive(thiserror::Error, Debug)]
pub enum WinappsError {
#[error("{0}")]
Message(String),
#[error("{0}\n{1}")]
WithError(#[source] anyhow::Error, String),
}

impl WinappsError {
/// This function prints the error to the console.
/// All lines are logged as seperate messages, and the source of the error is also logged if it exists.
fn error(&self) {
let messages: Vec<String> = self.to_string().split('\n').map(|s| s.into()).collect();
messages.iter().for_each(|s| tracing::error!("{}", s));

if self.source().is_some() {
tracing::error!("Caused by: {}", self.source().unwrap());
}
}

/// This function prints the error to the console and exits the program with an exit code of 1.
pub fn exit(&self) -> ! {
self.error();

tracing::error!("Unrecoverable error, exiting...");
exit(1);
}

/// This function prints the error to the console and panics.
pub fn panic(&self) -> ! {
self.error();

panic!("Program crashed, see log above");
}
}

/// This macro is a shortcut for creating a `WinappsError` from a string.
/// You can use normal `format!` syntax inside the macro.
#[macro_export]
macro_rules! error {
($($fmt:tt)*) => {
$crate::errors::WinappsError::Message(format!($($fmt)*))
};
}

/// This macro is a shortcut for creating a `WinappsError` from a string.
/// The first argument is the source error.
/// You can use normal `format!` syntax inside the macro.
#[macro_export]
macro_rules! error_from {
($err:expr, $($fmt:tt)*) => {
$crate::errors::WinappsError::WithError(anyhow::Error::new($err), format!($($fmt)*))
};
}

/// This trait serves as a generic way to convert a `Result` or `Option` into a `WinappsError`.
pub trait IntoError<T> {
fn into_error(self, msg: String) -> Result<T, WinappsError>;
}

impl<T, U> IntoError<T> for Result<T, U>
where
T: Debug,
U: Error + Send + Sync + 'static,
{
fn into_error(self, msg: String) -> Result<T, WinappsError> {
if let Err(error) = self {
return Err(WinappsError::WithError(anyhow::Error::new(error), msg));
}

Ok(self.unwrap())
}
}

impl<T> IntoError<T> for Option<T> {
fn into_error(self, msg: String) -> Result<T, WinappsError> {
if self.is_none() {
return Err(WinappsError::Message(msg));
}

Ok(self.unwrap())
}
}

/// This macro creates a `Result<_, WinappsError>` from either a `Result` or an `Option`.
/// It also works for all other types that implement `IntoError`.
/// Used internally by `winapps::unwrap_or_exit!` and `winapps::unwrap_or_panic!`.
#[macro_export]
macro_rules! into_error {
($val:expr) => {{
fn into_error_impl<T, U>(val: U) -> std::result::Result<T, $crate::errors::WinappsError>
where
T: std::marker::Sized + std::fmt::Debug,
U: $crate::errors::IntoError<T>,
{
val.into_error(
"Expected a value, got None / an Error. \
See log above for more detail."
.into(),
)
}

into_error_impl($val)
}};
($val:expr, $msg:expr) => {{
fn into_error_impl<T, U>(
val: U,
msg: String,
) -> std::result::Result<T, $crate::errors::WinappsError>
where
T: std::marker::Sized + std::fmt::Debug,
U: $crate::errors::IntoError<T>,
{
val.into_error(msg)
}

into_error_impl($val, $msg.into())
}};
}

/// This macro unwraps a `Result` or `Option` and returns the value if it exists.
/// Should the value not exist, then the program will exit with exit code 1.
/// Optionally, a message can be passed to the function using standard `format!` syntax.
/// The result type has to implement `Debug` and `Sized`, and the source error type has to implement `Error`, `Send`, `Sync` and has to be `'static`.
/// See `winapps::unwrap_or_panic!` for a version that panics instead of exiting.
#[macro_export]
macro_rules! unwrap_or_exit {
($expr:expr) => {{
$crate::into_error!($expr).unwrap_or_else(|e| e.exit())
}};
($expr:expr, $($fmt:tt)*) => {{
$crate::into_error!($expr, format!($($fmt)*)).unwrap_or_else(|e| e.exit())
}};
}

/// This macro unwraps a `Result` or `Option` and returns the value if it exists.
/// Should the value not exist, then the program will panic.
/// Optionally, a message can be passed to the function using standard `format!` syntax.
/// The result type has to implement `Debug` and `Sized`, and the error type has to implement `Error`, `Send`, `Sync` and has to be `'static`.
/// See `winapps::unwrap_or_exit!` for a version that exits instead of panicking.
#[macro_export]
macro_rules! unwrap_or_panic {
($expr:expr) => {{
$crate::into_error!($expr).unwrap_or_else(|e| e.panic())
}};
($expr:expr, $($fmt:tt)*) => {{
$crate::into_error!($expr, format!($($fmt)*)).unwrap_or_else(|e| e.panic())
}};
}
28 changes: 18 additions & 10 deletions winapps/src/freerdp.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
pub mod freerdp_back {
use std::process::{Command, Stdio};
use tracing::{info, warn};

use crate::{Config, RemoteClient};
use crate::{unwrap_or_exit, Config, RemoteClient};

pub struct Freerdp {}

Expand All @@ -11,18 +12,21 @@ pub mod freerdp_back {
xfreerdp.stdout(Stdio::null());
xfreerdp.stderr(Stdio::null());
xfreerdp.args(["-h"]);
xfreerdp
.spawn()
.expect("Freerdp execution failed! It needs to be installed!");
println!("Freerdp found!");

println!("All dependencies found!");
println!("Running explorer as test!");
println!("Check yourself if it appears correctly!");
unwrap_or_exit!(
xfreerdp.spawn(),
"Freerdp execution failed! It needs to be installed!",
);

info!("Freerdp found!");

info!("All dependencies found!");
info!("Running explorer as test!");
warn!("Check yourself if it appears correctly!");

self.run_app(config, Some(&"explorer.exe".to_string()));

println!("Test finished!");
info!("Test finished!");
}

fn run_app(&self, config: Config, app: Option<&String>) {
Expand Down Expand Up @@ -56,7 +60,11 @@ pub mod freerdp_back {
]);
}
}
xfreerdp.spawn().expect("Freerdp execution failed!");

unwrap_or_exit!(
xfreerdp.spawn(),
"Freerdp execution failed, check logs above!",
);
}
}
}
Loading

0 comments on commit eed3c31

Please sign in to comment.