From 2124e49db1666ce03313cb2f39e35175e9023591 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:32:50 +0200 Subject: [PATCH 01/12] feat: custom error handling, logging with tracing --- Cargo.toml | 1 + winapps/Cargo.toml | 6 +- winapps/src/errors.rs | 145 ++++++++++++++++++++++++++++++++++++++++++ winapps/src/lib.rs | 82 ++++++++++++++++-------- 4 files changed, 205 insertions(+), 29 deletions(-) create mode 100644 winapps/src/errors.rs diff --git a/Cargo.toml b/Cargo.toml index 8cd05f6..2d1fc48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ members = [ "winapps-cli", "winapps-gui", ] +resolver = "2" \ No newline at end of file diff --git a/winapps/Cargo.toml b/winapps/Cargo.toml index abd213a..24f4774 100644 --- a/winapps/Cargo.toml +++ b/winapps/Cargo.toml @@ -6,7 +6,11 @@ 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" +lazy_static = "1.4.0" serde = { version = "1.0.171", features = ["derive"] } -toml = "0.7.6" +thiserror = "1.0.49" +toml = "0.8.2" +tracing = "0.1.37" diff --git a/winapps/src/errors.rs b/winapps/src/errors.rs new file mode 100644 index 0000000..0545fed --- /dev/null +++ b/winapps/src/errors.rs @@ -0,0 +1,145 @@ +#![allow(clippy::crate_in_macro_def)] + +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. + /// It is used internally by the `unrecoverable` and `panic` functions. + /// 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 = 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 unrecoverable(&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 { + fn into_error(self, msg: String) -> Result; +} + +impl IntoError for Result +where + T: Debug, + U: Error + Send + Sync + 'static, +{ + fn into_error(self, msg: String) -> Result { + if let Err(error) = self { + return Err(WinappsError::WithError(anyhow::Error::new(error), msg)); + } + + Ok(self.unwrap()) + } +} + +impl IntoError for Option { + fn into_error(self, msg: String) -> Result { + if self.is_none() { + return Err(WinappsError::Message(msg)); + } + + Ok(self.unwrap()) + } +} + +/// This function unwraps a `Result` or `Option` and returns the value if it exists. +/// Should the value not exist, then the program will exit with an exit code of 1. +/// Called internally by the `unwrap_or_exit!` macro, which you should probably use instead. +pub fn unwrap_or_exit(val: U, msg: String) -> T +where + T: Sized + Debug, + U: IntoError, +{ + val.into_error(msg).unwrap_or_else(|e| e.unrecoverable()) +} + +/// This function unwraps a `Result` or `Option` and returns the value if it exists. +/// Should the value not exist, then the program will panic. +/// Called internally by the `unwrap_or_panic!` macro, which you should probably use instead. +pub fn unwrap_or_panic(val: U, msg: String) -> T +where + T: Sized + Debug, + U: IntoError, +{ + val.into_error(msg).unwrap_or_else(|e| e.panic()) +} + +/// 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 which uses standard `format!` syntax. +/// The result type has to implement `Debug` and `Sized`, and the error type has to implement `Error`, `Send`, `Sync` has to be `'static`. +#[macro_export] +macro_rules! unwrap_or_exit { + ($expr:expr) => {{ + crate::errors::unwrap_or_exit($expr, "Expected a value, got None / Error".into()) + }}; + ($expr:expr, $($fmt:tt)*) => {{ + crate::errors::unwrap_or_exit($expr, format!($($fmt)*)) + }}; +} + +/// 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 which uses standard `format!` syntax. +/// The result type has to implement `Debug` and `Sized`, and the error type has to implement `Error`, `Send`, `Sync` has to be `'static`. +#[macro_export] +macro_rules! unwrap_or_panic { + ($expr:expr) => {{ + crate::errors::unwrap_or_panic($expr, "Expected a value, got None / Error".into()) + }}; + ($expr:expr, $($fmt:tt)*) => {{ + crate::errors::unwrap_or_panic($expr, format!($($fmt)*)) + }}; +} diff --git a/winapps/src/lib.rs b/winapps/src/lib.rs index 41347d6..1cf5b4c 100644 --- a/winapps/src/lib.rs +++ b/winapps/src/lib.rs @@ -1,5 +1,8 @@ +pub mod errors; +pub mod freerdp; pub mod quickemu; +use crate::errors::WinappsError; use derive_new::new; use home::home_dir; use serde::{Deserialize, Serialize}; @@ -10,8 +13,7 @@ use std::{ fs::{self, File}, path::Path, }; - -pub mod freerdp; +use tracing::{info, warn}; pub trait RemoteClient { fn check_depends(&self, config: Config); @@ -59,22 +61,24 @@ pub fn get_config_file(path: Option<&str>) -> PathBuf { let default = match env::var("XDG_CONFIG_HOME") { Ok(dir) => PathBuf::from(dir).join("winapps"), Err(_) => { - println!("Couldn't read XDG_CONFIG_HOME, falling back to ~/.config"); - home_dir() - .expect("Could not find the home path!") - .join(".config/winapps") + warn!("Couldn't read XDG_CONFIG_HOME, falling back to ~/.config"); + unwrap_or_panic!(home_dir(), "Couldn't find the home directory").join(".config/winapps") } }; - let path = Path::new(path.unwrap_or(default.to_str().unwrap())); + let path = Path::new(path.unwrap_or(unwrap_or_panic!( + default.to_str(), + "Couldn't convert path {:?} to string", + default + ))); if !path.exists() { - println!("{:?} does not exist! Creating...", path); + info!("{:?} does not exist! Creating...", path); fs::create_dir_all(path).expect("Failed to create directory"); } if !path.is_dir() { - panic!("Config directory {:?} is not a directory!", path); + error!("Config directory {:?} is not a directory", path).panic(); } path.join("config.toml") @@ -85,54 +89,76 @@ pub fn load_config(path: Option<&str>) -> Config { let config_path = get_config_file(path); if !config_path.exists() { - save_config(&config, path).expect("Failed to write default configuration"); + unwrap_or_panic!( + save_config(&config, path), + "Failed to write default configuration" + ); + return config; } - let config_file = fs::read_to_string(config_path).expect("Failed to read configuration file"); - let config: Config = - toml::from_str(config_file.as_str()).expect("Failed to parse the configuration"); + let config_file = unwrap_or_panic!( + fs::read_to_string(config_path), + "Failed to read configuration file" + ); + + let config: Config = unwrap_or_panic!( + toml::from_str(config_file.as_str()), + "Failed to parse configuration file", + ); config } -pub fn save_config(config: &Config, path: Option<&str>) -> std::io::Result<()> { +pub fn save_config(config: &Config, path: Option<&str>) -> Result<(), WinappsError> { let config_path = get_config_file(path); - let serialized_config = toml::to_string(&config).expect("Failed to serialize configuration"); + let serialized_config = unwrap_or_panic!( + toml::to_string(&config), + "Failed to serialize configuration" + ); let mut config_file = match config_path.exists() { - true => File::open(&config_path).expect("Failed to open configuration file"), - false => File::create(&config_path).expect("Failed to create configuration file"), + true => unwrap_or_panic!( + File::open(&config_path), + "Failed to open configuration file" + ), + false => unwrap_or_panic!( + File::create(&config_path), + "Failed to create configuration file" + ), }; - write!(config_file, "{}", serialized_config) + if let Err(e) = write!(config_file, "{}", serialized_config) { + return Err(error_from!(e, "Failed to write configuration file")); + } + + Ok(()) } pub fn get_data_dir() -> PathBuf { - let data_dir = match env::var("XDG_DATA_HOME") { + let path = match env::var("XDG_DATA_HOME") { Ok(dir) => PathBuf::from(dir).join("winapps"), Err(_) => { - println!("Couldn't read XDG_DATA_HOME, falling back to ~/.local/share"); - home_dir() - .expect("Could not find the home path!") + warn!("Couldn't read XDG_DATA_HOME, falling back to ~/.local/share"); + unwrap_or_panic!(home_dir(), "Couldn't find the home directory") .join(".local/share/winapps") } }; - if !data_dir.exists() { - let dir = data_dir.clone(); - println!( + if !path.exists() { + let dir = path.clone(); + info!( "Data directory {:?} does not exist! Creating...", dir.to_str() ); fs::create_dir_all(dir).expect("Failed to create directory"); } - if !data_dir.is_dir() { - panic!("Data directory {:?} is not a directory!", data_dir); + if !path.is_dir() { + error!("Data directory {:?} is not a directory", path).panic(); } - data_dir + path } pub fn add(left: usize, right: usize) -> usize { From 8f30ea0e83becccdf4c73c604aaec21f2b828687 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:41:50 +0200 Subject: [PATCH 02/12] fix: rename unrecoverable to exit --- winapps/src/errors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winapps/src/errors.rs b/winapps/src/errors.rs index 0545fed..4d65570 100644 --- a/winapps/src/errors.rs +++ b/winapps/src/errors.rs @@ -31,7 +31,7 @@ impl WinappsError { } /// This function prints the error to the console and exits the program with an exit code of 1. - pub fn unrecoverable(&self) -> ! { + pub fn exit(&self) -> ! { self.error(); tracing::error!("Unrecoverable error, exiting..."); @@ -102,7 +102,7 @@ where T: Sized + Debug, U: IntoError, { - val.into_error(msg).unwrap_or_else(|e| e.unrecoverable()) + val.into_error(msg).unwrap_or_else(|e| e.exit()) } /// This function unwraps a `Result` or `Option` and returns the value if it exists. From 5087c62795178503951ffc0c74df7f14a9878099 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:43:21 +0200 Subject: [PATCH 03/12] fix: use newly created macros to handle errors from quickemu --- winapps/src/quickemu.rs | 100 ++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/winapps/src/quickemu.rs b/winapps/src/quickemu.rs index 00b9feb..0219b30 100644 --- a/winapps/src/quickemu.rs +++ b/winapps/src/quickemu.rs @@ -1,29 +1,25 @@ -use crate::{get_data_dir, save_config, Config}; +use crate::{get_data_dir, save_config, unwrap_or_exit, Config}; use std::fs; -use std::process::exit; use std::process::Command; +use tracing::info; pub fn create_vm(mut config: Config) { let data_dir = get_data_dir(); - let output = match Command::new("quickget") - .current_dir(data_dir) - .arg("windows") - .arg("10") - .output() - { - Ok(o) => o, - Err(e) => { - println!("Failed to execute quickget: {}", e); - println!("Please make sure quickget is installed and in your PATH"); - exit(1); - } - }; + let output = unwrap_or_exit!( + Command::new("quickget") + .current_dir(data_dir) + .arg("windows") + .arg("10") + .output(), + "Failed to execute quickget: \n\ + Please make sure quickget is installed and in your PATH" + ); config.vm.name = "windows-10-22H2".to_string(); config.vm.short_name = "windows-10".to_string(); - save_config(&config, None).expect("Failed to save config, VM will not start properly"); + unwrap_or_exit!(save_config(&config, None), "Failed to save config"); println!("{}", String::from_utf8_lossy(&output.stdout)); } @@ -31,32 +27,24 @@ pub fn create_vm(mut config: Config) { pub fn start_vm(config: Config) { let data_dir = get_data_dir(); - let command = match Command::new("quickemu") - .current_dir(data_dir) - .args([ - "--vm", - &format!("{}.conf", config.vm.name), - "--display", - "none", - ]) - .spawn() - { - Ok(c) => c, - Err(e) => { - println!("Failed to execute quickemu: {}", e); - println!("Please make sure quickemu is installed and in your PATH"); - exit(1); - } - }; + let command = unwrap_or_exit!( + Command::new("quickemu") + .current_dir(data_dir) + .args([ + "--vm", + &format!("{}.conf", config.vm.name), + "--display", + "none", + ]) + .spawn(), + "Failed to execute quickemu: \n\ + Please make sure quickemu is installed and in your PATH" + ); - let output = match command.wait_with_output() { - Ok(o) => o, - Err(e) => { - println!("Failed to gather output from quickemu: {}", e); - println!("Please make sure quickemu is installed and in your PATH"); - exit(1); - } - }; + let output = unwrap_or_exit!( + command.wait_with_output(), + "Failed to gather output from quickemu / stdout" + ); println!("{}", String::from_utf8_lossy(&output.stdout)); } @@ -64,25 +52,17 @@ pub fn start_vm(config: Config) { pub fn kill_vm(config: Config) { let data_dir = get_data_dir(); - match fs::read_to_string( - data_dir.join(format!("{}/{}.pid", config.vm.short_name, config.vm.name)), - ) { - Ok(pid) => { - let pid = pid.trim(); + let pid = unwrap_or_exit!( + fs::read_to_string( + data_dir.join(format!("{}/{}.pid", config.vm.short_name, config.vm.name)), + ), + "Failed to read PID file, is the VM running and the config correct?" + ); - println!("Killing VM with PID {}", pid); + info!("Killing VM with PID {}", pid); - match Command::new("kill").arg(pid).spawn() { - Ok(_) => (), - Err(e) => { - println!("Failed to kill VM: {}", e); - exit(1); - } - } - } - Err(e) => { - println!("Failed to read PID file: {}", e); - exit(1); - } - } + unwrap_or_exit!( + Command::new("kill").arg(pid).spawn(), + "Failed to kill VM (execute kill)" + ); } From b34339a240903f7e1711757c1ffec97d1f33a522 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 22:45:08 +0000 Subject: [PATCH 04/12] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2d1fc48..0e5b907 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ members = [ "winapps-cli", "winapps-gui", ] -resolver = "2" \ No newline at end of file +resolver = "2" From c4603c8a898bad8748b30bb0a08d204ed95b9356 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:10:50 +0200 Subject: [PATCH 05/12] fix: remove boilerplate --- winapps-gui/src/main.rs | 4 +--- winapps/src/lib.rs | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/winapps-gui/src/main.rs b/winapps-gui/src/main.rs index acf379e..f328e4d 100644 --- a/winapps-gui/src/main.rs +++ b/winapps-gui/src/main.rs @@ -1,3 +1 @@ -fn main() { - println!("Test lib: {}", winapps::add(1, 2)); -} +fn main() {} diff --git a/winapps/src/lib.rs b/winapps/src/lib.rs index 0368450..148f331 100644 --- a/winapps/src/lib.rs +++ b/winapps/src/lib.rs @@ -160,7 +160,3 @@ pub fn get_data_dir() -> PathBuf { path } - -pub fn add(left: usize, right: usize) -> usize { - left + right -} From 90079a498242fe5ac06641116e63010b1c683672 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:11:10 +0200 Subject: [PATCH 06/12] fix: use $crate instead of crate --- winapps/src/errors.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/winapps/src/errors.rs b/winapps/src/errors.rs index 4d65570..d66053b 100644 --- a/winapps/src/errors.rs +++ b/winapps/src/errors.rs @@ -1,5 +1,3 @@ -#![allow(clippy::crate_in_macro_def)] - use std::error::Error; use std::fmt::Debug; use std::process::exit; @@ -51,7 +49,7 @@ impl WinappsError { #[macro_export] macro_rules! error { ($($fmt:tt)*) => { - crate::errors::WinappsError::Message(format!($($fmt)*)) + $crate::errors::WinappsError::Message(format!($($fmt)*)) }; } @@ -61,7 +59,7 @@ macro_rules! error { #[macro_export] macro_rules! error_from { ($err:expr, $($fmt:tt)*) => { - crate::errors::WinappsError::WithError(anyhow::Error::new($err), format!($($fmt)*)) + $crate::errors::WinappsError::WithError(anyhow::Error::new($err), format!($($fmt)*)) }; } @@ -123,10 +121,10 @@ where #[macro_export] macro_rules! unwrap_or_exit { ($expr:expr) => {{ - crate::errors::unwrap_or_exit($expr, "Expected a value, got None / Error".into()) + $crate::errors::unwrap_or_exit($expr, "Expected a value, got None / Error".into()) }}; ($expr:expr, $($fmt:tt)*) => {{ - crate::errors::unwrap_or_exit($expr, format!($($fmt)*)) + $crate::errors::unwrap_or_exit($expr, format!($($fmt)*)) }}; } @@ -137,9 +135,9 @@ macro_rules! unwrap_or_exit { #[macro_export] macro_rules! unwrap_or_panic { ($expr:expr) => {{ - crate::errors::unwrap_or_panic($expr, "Expected a value, got None / Error".into()) + $crate::errors::unwrap_or_panic($expr, "Expected a value, got None / Error".into()) }}; ($expr:expr, $($fmt:tt)*) => {{ - crate::errors::unwrap_or_panic($expr, format!($($fmt)*)) + $crate::errors::unwrap_or_panic($expr, format!($($fmt)*)) }}; } From 0e5276c7442c614992dfd8324fd2b15b5a054917 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:11:50 +0200 Subject: [PATCH 07/12] feat: use new error handling & logging for freerdp & the cli --- winapps-cli/src/main.rs | 18 +++++++++++------- winapps/src/freerdp.rs | 28 ++++++++++++++++++---------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/winapps-cli/src/main.rs b/winapps-cli/src/main.rs index 3887292..3fb78ee 100644 --- a/winapps-cli/src/main.rs +++ b/winapps-cli/src/main.rs @@ -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") @@ -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!(), } diff --git a/winapps/src/freerdp.rs b/winapps/src/freerdp.rs index cf7baa4..b88936a 100644 --- a/winapps/src/freerdp.rs +++ b/winapps/src/freerdp.rs @@ -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 {} @@ -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>) { @@ -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!", + ); } } } From 9e8d43c6b8da4c70a9cc34d48ae8f6cefb5aea49 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:31:18 +0200 Subject: [PATCH 08/12] fix: re-add ignore msrs and trim pidfile --- winapps/src/quickemu.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/winapps/src/quickemu.rs b/winapps/src/quickemu.rs index 0219b30..d0b6206 100644 --- a/winapps/src/quickemu.rs +++ b/winapps/src/quickemu.rs @@ -31,6 +31,7 @@ pub fn start_vm(config: Config) { Command::new("quickemu") .current_dir(data_dir) .args([ + "--ignore-msrs-always", "--vm", &format!("{}.conf", config.vm.name), "--display", @@ -53,9 +54,11 @@ pub fn kill_vm(config: Config) { let data_dir = get_data_dir(); let pid = unwrap_or_exit!( - fs::read_to_string( - data_dir.join(format!("{}/{}.pid", config.vm.short_name, config.vm.name)), - ), + fs::read_to_string(data_dir.join(format!( + "{}/{}.pid", + config.vm.short_name, + config.vm.name.trim() + ))), "Failed to read PID file, is the VM running and the config correct?" ); From 24b69bf9c6a859003b2b91b630f98ffe6b277e83 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:01:42 +0200 Subject: [PATCH 09/12] feat: get rid of the functions named like the macros --- winapps/src/errors.rs | 72 ++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/winapps/src/errors.rs b/winapps/src/errors.rs index d66053b..5665e8a 100644 --- a/winapps/src/errors.rs +++ b/winapps/src/errors.rs @@ -17,7 +17,6 @@ pub enum WinappsError { impl WinappsError { /// This function prints the error to the console. - /// It is used internally by the `unrecoverable` and `panic` functions. /// 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 = self.to_string().split('\n').map(|s| s.into()).collect(); @@ -92,52 +91,69 @@ impl IntoError for Option { } } -/// This function unwraps a `Result` or `Option` and returns the value if it exists. -/// Should the value not exist, then the program will exit with an exit code of 1. -/// Called internally by the `unwrap_or_exit!` macro, which you should probably use instead. -pub fn unwrap_or_exit(val: U, msg: String) -> T -where - T: Sized + Debug, - U: IntoError, -{ - val.into_error(msg).unwrap_or_else(|e| e.exit()) -} +/// This macro unwraps either a `Result` or an `Option` and returns the value if it exists. +/// It returns a `Result<_, WinappsError>` which can be used to return the error. +/// 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(val: U) -> std::result::Result + where + T: std::marker::Sized + std::fmt::Debug, + U: $crate::errors::IntoError, + { + val.into_error( + "Expected a value, got None / an Error. \ + See log above for more detail." + .into(), + ) + } -/// This function unwraps a `Result` or `Option` and returns the value if it exists. -/// Should the value not exist, then the program will panic. -/// Called internally by the `unwrap_or_panic!` macro, which you should probably use instead. -pub fn unwrap_or_panic(val: U, msg: String) -> T -where - T: Sized + Debug, - U: IntoError, -{ - val.into_error(msg).unwrap_or_else(|e| e.panic()) + into_error_impl($val) + }}; + ($val:expr, $msg:expr) => {{ + fn into_error_impl( + val: U, + msg: String, + ) -> std::result::Result + where + T: std::marker::Sized + std::fmt::Debug, + U: $crate::errors::IntoError, + { + 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 which uses standard `format!` syntax. -/// The result type has to implement `Debug` and `Sized`, and the error type has to implement `Error`, `Send`, `Sync` has to be `'static`. +/// 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::errors::unwrap_or_exit($expr, "Expected a value, got None / Error".into()) + $crate::into_error!($expr).unwrap_or_else(|e| e.exit()) }}; ($expr:expr, $($fmt:tt)*) => {{ - $crate::errors::unwrap_or_exit($expr, format!($($fmt)*)) + $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 which uses standard `format!` syntax. -/// The result type has to implement `Debug` and `Sized`, and the error type has to implement `Error`, `Send`, `Sync` has to be `'static`. +/// 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::errors::unwrap_or_panic($expr, "Expected a value, got None / Error".into()) + $crate::into_error!($expr).unwrap_or_else(|e| e.panic()) }}; ($expr:expr, $($fmt:tt)*) => {{ - $crate::errors::unwrap_or_panic($expr, format!($($fmt)*)) + $crate::into_error!($expr, format!($($fmt)*)).unwrap_or_else(|e| e.panic()) }}; } From 1ea9794e81a4b0d0dd73ec569baa91f19152761b Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:14:07 +0200 Subject: [PATCH 10/12] fix: docstring for `into_error` didn't make any sense --- winapps/src/errors.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/winapps/src/errors.rs b/winapps/src/errors.rs index 5665e8a..fa449db 100644 --- a/winapps/src/errors.rs +++ b/winapps/src/errors.rs @@ -91,8 +91,7 @@ impl IntoError for Option { } } -/// This macro unwraps either a `Result` or an `Option` and returns the value if it exists. -/// It returns a `Result<_, WinappsError>` which can be used to return the error. +/// 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] From 06071e3c22a2dc0bedb9f30f1f9d7bb33ee17ba3 Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:37:34 +0200 Subject: [PATCH 11/12] fix: remove trailing dependency --- winapps/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/winapps/Cargo.toml b/winapps/Cargo.toml index 24f4774..8d6d0a5 100644 --- a/winapps/Cargo.toml +++ b/winapps/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" anyhow = "1.0.75" derive-new = "0.5.9" home = "0.5.5" -lazy_static = "1.4.0" serde = { version = "1.0.171", features = ["derive"] } thiserror = "1.0.49" toml = "0.8.2" From 488f152d14353f54ad103d5a5fca89027115b0fe Mon Sep 17 00:00:00 2001 From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com> Date: Tue, 10 Oct 2023 19:22:10 +0200 Subject: [PATCH 12/12] fix: I'm an idiot --- winapps/src/quickemu.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/winapps/src/quickemu.rs b/winapps/src/quickemu.rs index d0b6206..d2cb54c 100644 --- a/winapps/src/quickemu.rs +++ b/winapps/src/quickemu.rs @@ -54,18 +54,16 @@ pub fn kill_vm(config: Config) { let data_dir = get_data_dir(); let pid = unwrap_or_exit!( - fs::read_to_string(data_dir.join(format!( - "{}/{}.pid", - config.vm.short_name, - config.vm.name.trim() - ))), + fs::read_to_string( + data_dir.join(format!("{}/{}.pid", config.vm.short_name, config.vm.name)) + ), "Failed to read PID file, is the VM running and the config correct?" ); info!("Killing VM with PID {}", pid); unwrap_or_exit!( - Command::new("kill").arg(pid).spawn(), + Command::new("kill").arg(pid.trim()).spawn(), "Failed to kill VM (execute kill)" ); }