From 518565f422e22cf090aae8ac65bc9c02bd840051 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 4 Aug 2018 17:03:32 +1000 Subject: [PATCH] Overhaul all unwraps/expects to use new logging interface --- build.rs | 2 +- src/installer.rs | 4 +- src/logging.rs | 42 ++++++++++- src/main.rs | 47 ++++++------ src/rest.rs | 137 ++++++++++++++++++++++++----------- src/tasks/download_pkg.rs | 4 +- src/tasks/install_dir.rs | 4 +- src/tasks/install_pkg.rs | 13 +++- src/tasks/resolver.rs | 4 +- src/tasks/save_executable.rs | 4 +- src/tasks/uninstall_pkg.rs | 4 +- 11 files changed, 188 insertions(+), 77 deletions(-) diff --git a/build.rs b/build.rs index 5122546..a0492ec 100644 --- a/build.rs +++ b/build.rs @@ -5,7 +5,7 @@ extern crate winres; fn main() { let mut res = winres::WindowsResource::new(); res.set_icon("static/favicon.ico"); - res.compile().unwrap(); + res.compile().expect("Failed to generate metadata"); } #[cfg(not(windows))] diff --git a/src/installer.rs b/src/installer.rs index 56c5d6d..bd5e223 100644 --- a/src/installer.rs +++ b/src/installer.rs @@ -22,6 +22,8 @@ use tasks::install::InstallTask; use tasks::uninstall::UninstallTask; use tasks::DependencyTree; +use logging::LoggingErrors; + /// A message thrown during the installation of packages. #[derive(Serialize)] pub enum InstallMessage { @@ -91,7 +93,7 @@ impl InstallerFramework { items, self.install_path .clone() - .expect("Install directory not initialised") + .log_expect("Install directory not initialised") ); // Calculate packages to *uninstall* diff --git a/src/logging.rs b/src/logging.rs index 3f6fc89..2216442 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -1,9 +1,10 @@ //! Contains functions to help with logging. -use fern; use chrono; +use fern; use log; +use std::fmt::Debug; use std::io; pub fn setup_logger() -> Result<(), fern::InitError> { @@ -23,3 +24,42 @@ pub fn setup_logger() -> Result<(), fern::InitError> { .apply()?; Ok(()) } + +/// An additional wrapper usable on Result/Option types to add logging to the regular +/// panic route. +pub trait LoggingErrors +where + Self: Sized, +{ + /// Unwraps this object. See `unwrap()`. + fn log_unwrap(self) -> T { + self.log_expect("Failed to unwrap") + } + + /// Unwraps this object, with a specified error message on failure. See `expect()`. + fn log_expect(self, msg: &str) -> T; +} + +impl LoggingErrors for Result { + fn log_expect(self, msg: &str) -> T { + match self { + Ok(v) => v, + Err(v) => { + error!("Fatal error: {}: {:?}", msg, v); + panic!("Expectation failed"); + } + } + } +} + +impl LoggingErrors for Option { + fn log_expect(self, msg: &str) -> T { + match self { + Some(v) => v, + None => { + error!("Fatal error: {}", msg); + panic!("Expectation failed"); + } + } + } +} diff --git a/src/main.rs b/src/main.rs index f5798d2..5acdef9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,10 +38,10 @@ mod assets; mod config; mod http; mod installer; +mod logging; mod rest; mod sources; mod tasks; -mod logging; use web_view::*; @@ -60,6 +60,8 @@ use std::net::TcpListener; use std::sync::Arc; use std::sync::RwLock; +use logging::LoggingErrors; + // TODO: Fetch this over a HTTP request? static RAW_CONFIG: &'static str = include_str!("../config.toml"); @@ -71,18 +73,20 @@ enum CallbackType { fn main() { logging::setup_logger().expect("Unable to setup logging!"); - let config = Config::from_toml_str(RAW_CONFIG).unwrap(); + let config = Config::from_toml_str(RAW_CONFIG).log_expect("Config file could not be read"); let app_name = config.general.name.clone(); info!("{} installer", app_name); - let current_exe = std::env::current_exe().unwrap(); - let current_path = current_exe.parent().unwrap(); + let current_exe = std::env::current_exe().log_expect("Current executable could not be found"); + let current_path = current_exe + .parent() + .log_expect("Parent directory of executable could not be found"); let metadata_file = current_path.join("metadata.json"); let framework = if metadata_file.exists() { info!("Using pre-existing metadata file: {:?}", metadata_file); - InstallerFramework::new_with_db(config, current_path).unwrap() + InstallerFramework::new_with_db(config, current_path).log_expect("Unable to parse metadata") } else { info!("Starting fresh install"); InstallerFramework::new(config) @@ -90,18 +94,18 @@ fn main() { // Firstly, allocate us an epidermal port let target_port = { - let listener = - TcpListener::bind("127.0.0.1:0").expect("At least one local address should be free"); + let listener = TcpListener::bind("127.0.0.1:0") + .log_expect("At least one local address should be free"); listener .local_addr() - .expect("Should be able to pull address from listener") + .log_expect("Should be able to pull address from listener") .port() }; // Now, iterate over all ports let addresses = "localhost:0" .to_socket_addrs() - .expect("No localhost address found"); + .log_expect("No localhost address found"); let mut servers = Vec::new(); let mut http_address = None; @@ -112,12 +116,12 @@ fn main() { for mut address in addresses { address.set_port(target_port); - let server = WebServer::with_addr(framework.clone(), address).unwrap(); + let server = WebServer::with_addr(framework.clone(), address.clone()) + .log_expect("Failed to bind to address"); - let addr = server.get_addr(); - debug!("Server: {:?}", addr); + debug!("Server: {:?}", address); - http_address = Some(addr); + http_address = Some(address); servers.push(server); } @@ -143,26 +147,27 @@ fn main() { |_| {}, |wv, msg, _| { let command: CallbackType = - serde_json::from_str(msg).expect(&format!("Unable to parse string: {:?}", msg)); + serde_json::from_str(msg).log_expect(&format!("Unable to parse string: {:?}", msg)); debug!("Incoming payload: {:?}", command); match command { CallbackType::SelectInstallDir { callback_name } => { #[cfg(windows)] - let result = - match nfd::open_pick_folder(None).expect("Unable to open folder dialog") { - Response::Okay(v) => v, - _ => return, - }; + let result = match nfd::open_pick_folder(None) + .log_expect("Unable to open folder dialog") + { + Response::Okay(v) => v, + _ => return, + }; #[cfg(not(windows))] let result = wv.dialog(Dialog::ChooseDirectory, "Select a install directory...", ""); if result.len() > 0 { - let result = - serde_json::to_string(&result).expect("Unable to serialize response"); + let result = serde_json::to_string(&result) + .log_expect("Unable to serialize response"); let command = format!("{}({});", callback_name, result); debug!("Injecting response: {}", command); wv.eval(&command); diff --git a/src/rest.rs b/src/rest.rs index 1ffadec..8a4459d 100644 --- a/src/rest.rs +++ b/src/rest.rs @@ -29,6 +29,8 @@ use assets; use installer::InstallMessage; use installer::InstallerFramework; +use logging::LoggingErrors; + #[derive(Serialize)] struct FileSelection { path: Option, @@ -38,22 +40,14 @@ struct FileSelection { /// application. pub struct WebServer { _handle: JoinHandle<()>, - addr: SocketAddr, } impl WebServer { - /// Returns the bound address that the server is running from. - pub fn get_addr(&self) -> SocketAddr { - self.addr.clone() - } - /// Creates a new web server with the specified address. pub fn with_addr( framework: Arc>, addr: SocketAddr, ) -> Result { - let (sender, receiver) = channel(); - let handle = thread::spawn(move || { let server = Http::new() .bind(&addr, move || { @@ -61,19 +55,12 @@ impl WebServer { framework: framework.clone(), }) }) - .unwrap(); + .log_expect("Failed to bind to port"); - sender.send(server.local_addr().unwrap()).unwrap(); - - server.run().unwrap(); + server.run().log_expect("Failed to run HTTP server"); }); - let addr = receiver.recv().unwrap(); - - Ok(WebServer { - _handle: handle, - addr, - }) + Ok(WebServer { _handle: handle }) } } @@ -94,10 +81,18 @@ impl Service for WebService { // This endpoint should be usable directly from a