From 19bec5d80c90aa1c0e8586b597e250909f1253c4 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 8 Aug 2018 19:58:30 +1000 Subject: [PATCH] Add a global shortcut for the maintenance tool --- src/installer.rs | 46 +++++++++++++--- src/tasks/download_pkg.rs | 2 +- src/tasks/install.rs | 6 +++ src/tasks/install_global_shortcut.rs | 72 ++++++++++++++++++++++++++ src/tasks/install_pkg.rs | 2 +- src/tasks/mod.rs | 2 + src/tasks/uninstall_global_shortcut.rs | 44 ++++++++++++++++ src/tasks/uninstall_pkg.rs | 6 +-- src/tasks/uninstall_shortcuts.rs | 12 ++--- static/js/views.js | 4 +- 10 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 src/tasks/install_global_shortcut.rs create mode 100644 src/tasks/uninstall_global_shortcut.rs diff --git a/src/installer.rs b/src/installer.rs index 3e4e470..c94c857 100644 --- a/src/installer.rs +++ b/src/installer.rs @@ -27,6 +27,7 @@ use logging::LoggingErrors; use dirs::home_dir; use std::fs::remove_file; +use tasks::uninstall_global_shortcut::UninstallGlobalShortcutsTask; /// A message thrown during the installation of packages. #[derive(Serialize)] @@ -36,12 +37,29 @@ pub enum InstallMessage { EOF, } +/// Metadata about the current installation itself. +#[derive(Serialize, Deserialize, Clone)] +pub struct InstallationDatabase { + pub packages: Vec, + pub shortcuts: Vec, +} + +impl InstallationDatabase { + /// Creates a new, empty installation database. + pub fn new() -> InstallationDatabase { + InstallationDatabase { + packages: Vec::new(), + shortcuts: Vec::new(), + } + } +} + /// The installer framework contains metadata about packages, what is installable, what isn't, /// etc. pub struct InstallerFramework { pub base_attributes: BaseAttributes, pub config: Option, - pub database: Vec, + pub database: InstallationDatabase, pub install_path: Option, pub preexisting_install: bool, pub is_launcher: bool, @@ -51,7 +69,7 @@ pub struct InstallerFramework { /// Contains basic properties on the status of the session. Subset of InstallationFramework. #[derive(Serialize)] pub struct InstallationStatus { - pub database: Vec, + pub database: InstallationDatabase, pub install_path: Option, pub preexisting_install: bool, pub is_launcher: bool, @@ -110,7 +128,7 @@ impl InstallerFramework { // Calculate packages to *uninstall* let mut uninstall_items = Vec::new(); if !fresh_install { - for package in &self.database { + for package in &self.database.packages { if !items.contains(&package.name) { uninstall_items.push(package.name.clone()); } @@ -141,7 +159,12 @@ impl InstallerFramework { /// Sends a request for everything to be uninstalled. pub fn uninstall(&mut self, messages: &Sender) -> Result<(), String> { - let items: Vec = self.database.iter().map(|x| x.name.clone()).collect(); + let items: Vec = self + .database + .packages + .iter() + .map(|x| x.name.clone()) + .collect(); let task = Box::new(UninstallTask { items }); @@ -155,6 +178,17 @@ impl InstallerFramework { } }).map(|_x| ())?; + // Uninstall shortcuts + let task = Box::new(UninstallGlobalShortcutsTask {}); + + let mut tree = DependencyTree::build(task); + + tree.execute(self, &|msg: &str, progress: f64| { + if let Err(v) = messages.send(InstallMessage::Status(msg.to_string(), progress as _)) { + error!("Failed to submit queue message: {:?}", v); + } + }).map(|_x| ())?; + // Delete the metadata file let path = self .install_path @@ -216,7 +250,7 @@ impl InstallerFramework { InstallerFramework { base_attributes: attrs, config: None, - database: Vec::new(), + database: InstallationDatabase::new(), install_path: None, preexisting_install: false, is_launcher: false, @@ -234,7 +268,7 @@ impl InstallerFramework { Err(v) => return Err(format!("Unable to open file handle: {:?}", v)), }; - let database: Vec = match serde_json::from_reader(metadata_file) { + let database: InstallationDatabase = match serde_json::from_reader(metadata_file) { Ok(v) => v, Err(v) => return Err(format!("Unable to read metadata file: {:?}", v)), }; diff --git a/src/tasks/download_pkg.rs b/src/tasks/download_pkg.rs index 9412b6a..3470184 100644 --- a/src/tasks/download_pkg.rs +++ b/src/tasks/download_pkg.rs @@ -35,7 +35,7 @@ impl Task for DownloadPackageTask { }; // Check to see if this is the newest file available already - for element in &context.database { + for element in &context.database.packages { if element.name == self.name { if element.version == version { info!("{:?} is already up to date.", self.name); diff --git a/src/tasks/install.rs b/src/tasks/install.rs index 4c6bc82..e70adba 100644 --- a/src/tasks/install.rs +++ b/src/tasks/install.rs @@ -7,6 +7,7 @@ use tasks::install_pkg::InstallPackageTask; use tasks::save_executable::SaveExecutableTask; use tasks::uninstall_pkg::UninstallPackageTask; +use tasks::install_global_shortcut::InstallGlobalShortcutsTask; use tasks::Task; use tasks::TaskDependency; use tasks::TaskOrdering; @@ -61,6 +62,11 @@ impl Task for InstallTask { TaskOrdering::Pre, Box::new(SaveExecutableTask {}), )); + + elements.push(TaskDependency::build( + TaskOrdering::Pre, + Box::new(InstallGlobalShortcutsTask {}), + )); } elements diff --git a/src/tasks/install_global_shortcut.rs b/src/tasks/install_global_shortcut.rs new file mode 100644 index 0000000..8bce2a4 --- /dev/null +++ b/src/tasks/install_global_shortcut.rs @@ -0,0 +1,72 @@ +//! Generates the global shortcut for this application. + +use installer::InstallerFramework; + +use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskParamType; + +use logging::LoggingErrors; + +use native::create_shortcut; +use tasks::save_database::SaveDatabaseTask; +use tasks::TaskOrdering; + +pub struct InstallGlobalShortcutsTask {} + +impl Task for InstallGlobalShortcutsTask { + fn execute( + &mut self, + _: Vec, + context: &mut InstallerFramework, + messenger: &Fn(&str, f64), + ) -> Result { + messenger(&format!("Generating global shortcut..."), 0.0); + + let path = context + .install_path + .as_ref() + .log_expect("No install path specified"); + + let starting_dir = path + .to_str() + .log_expect("Unable to build shortcut metadata (startingdir)"); + + // Generate installer path + let platform_extension = if cfg!(windows) { + "maintenancetool.exe" + } else { + "maintenancetool" + }; + + let tool_path = path.join(platform_extension); + let tool_path = tool_path + .to_str() + .log_expect("Unable to build shortcut metadata (tool)"); + + context.database.shortcuts.push(create_shortcut( + &format!("{} maintenance tool", context.base_attributes.name), + &format!( + "Launch the {} maintenance tool to update, modify and uninstall the application.", + context.base_attributes.name + ), + tool_path, + // TODO: Send by list + "", + &starting_dir, + )?); + + Ok(TaskParamType::None) + } + + fn dependencies(&self) -> Vec { + vec![TaskDependency::build( + TaskOrdering::Post, + Box::new(SaveDatabaseTask {}), + )] + } + + fn name(&self) -> String { + "InstallGlobalShortcutsTask".to_string() + } +} diff --git a/src/tasks/install_pkg.rs b/src/tasks/install_pkg.rs index 4242559..f2ce893 100644 --- a/src/tasks/install_pkg.rs +++ b/src/tasks/install_pkg.rs @@ -163,7 +163,7 @@ impl Task for InstallPackageTask { })?; // Save metadata about this package - context.database.push(LocalInstallation { + context.database.packages.push(LocalInstallation { name: package.name.to_owned(), version, shortcuts, diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index 1aea805..c587202 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -12,12 +12,14 @@ use sources::types::Version; pub mod download_pkg; pub mod install; pub mod install_dir; +pub mod install_global_shortcut; pub mod install_pkg; pub mod install_shortcuts; pub mod resolver; pub mod save_database; pub mod save_executable; pub mod uninstall; +pub mod uninstall_global_shortcut; pub mod uninstall_pkg; pub mod uninstall_shortcuts; diff --git a/src/tasks/uninstall_global_shortcut.rs b/src/tasks/uninstall_global_shortcut.rs new file mode 100644 index 0000000..828dff0 --- /dev/null +++ b/src/tasks/uninstall_global_shortcut.rs @@ -0,0 +1,44 @@ +//! Uninstalls a specific package. + +use installer::InstallerFramework; + +use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskParamType; + +use std::fs::remove_file; +use tasks::save_database::SaveDatabaseTask; +use tasks::TaskOrdering; + +pub struct UninstallGlobalShortcutsTask {} + +impl Task for UninstallGlobalShortcutsTask { + fn execute( + &mut self, + input: Vec, + context: &mut InstallerFramework, + messenger: &Fn(&str, f64), + ) -> Result { + assert_eq!(input.len(), 0); + + messenger(&format!("Uninstalling global shortcut..."), 0.0); + + while let Some(file) = context.database.shortcuts.pop() { + info!("Deleting shortcut {:?}", file); + remove_file(file).map_err(|x| format!("Unable to delete global shortcut: {:?}", x))?; + } + + Ok(TaskParamType::None) + } + + fn dependencies(&self) -> Vec { + vec![TaskDependency::build( + TaskOrdering::Post, + Box::new(SaveDatabaseTask {}), + )] + } + + fn name(&self) -> String { + "UninstallGlobalShortcutsTask".to_string() + } +} diff --git a/src/tasks/uninstall_pkg.rs b/src/tasks/uninstall_pkg.rs index d33018a..bf16ac7 100644 --- a/src/tasks/uninstall_pkg.rs +++ b/src/tasks/uninstall_pkg.rs @@ -36,9 +36,9 @@ impl Task for UninstallPackageTask { .log_expect("No install path specified"); let mut metadata: Option = None; - for i in 0..context.database.len() { - if self.name == context.database[i].name { - metadata = Some(context.database.remove(i)); + for i in 0..context.database.packages.len() { + if self.name == context.database.packages[i].name { + metadata = Some(context.database.packages.remove(i)); break; } } diff --git a/src/tasks/uninstall_shortcuts.rs b/src/tasks/uninstall_shortcuts.rs index 8fb4c90..a7f378f 100644 --- a/src/tasks/uninstall_shortcuts.rs +++ b/src/tasks/uninstall_shortcuts.rs @@ -33,9 +33,9 @@ impl Task for UninstallShortcutsTask { .log_expect("No install path specified"); let mut metadata: Option = None; - for i in 0..context.database.len() { - if self.name == context.database[i].name { - metadata = Some(context.database[i].clone()); + for i in 0..context.database.packages.len() { + if self.name == context.database.packages[i].name { + metadata = Some(context.database.packages[i].clone()); break; } } @@ -66,10 +66,10 @@ impl Task for UninstallShortcutsTask { for (i, file) in package.shortcuts.iter().enumerate() { let name = file.clone(); let file = path.join(file); - info!("Deleting {:?}", file); + info!("Deleting shortcut {:?}", file); messenger( - &format!("Deleting {} ({} of {})", name, i + 1, max), + &format!("Deleting shortcut {} ({} of {})", name, i + 1, max), (i as f64) / (max as f64), ); @@ -80,7 +80,7 @@ impl Task for UninstallShortcutsTask { }; if let Err(v) = result { - error!("Failed to delete file: {:?}", v); + error!("Failed to delete shortcut: {:?}", v); } } diff --git a/static/js/views.js b/static/js/views.js index d419bd6..6b54908 100644 --- a/static/js/views.js +++ b/static/js/views.js @@ -53,10 +53,10 @@ const DownloadConfig = { app.config.packages[x].installed = false; } - for (var i = 0; i < app.metadata.database.length; i++) { + for (var i = 0; i < app.metadata.database.packages.length; i++) { // Find this config package for (var x = 0; x < app.config.packages.length; x++) { - if (app.config.packages[x].name === app.metadata.database[i].name) { + if (app.config.packages[x].name === app.metadata.database.packages[i].name) { app.config.packages[x].default = true; app.config.packages[x].installed = true; }