Quality of life improvements to messenging

This commit is contained in:
James 2018-10-01 13:17:59 +10:00
parent 90f8792b15
commit 6daeead585
20 changed files with 185 additions and 90 deletions

View file

@ -30,6 +30,7 @@ use tasks::install::InstallTask;
use tasks::uninstall::UninstallTask;
use tasks::uninstall_global_shortcut::UninstallGlobalShortcutsTask;
use tasks::DependencyTree;
use tasks::TaskMessage;
use logging::LoggingErrors;
@ -45,6 +46,7 @@ use number_prefix::{decimal_prefix, Prefixed, Standalone};
#[derive(Serialize)]
pub enum InstallMessage {
Status(String, f64),
PackageInstalled,
Error(String),
EOF,
}
@ -101,6 +103,24 @@ pub struct LocalInstallation {
pub shortcuts: Vec<String>,
}
macro_rules! declare_messenger_callback {
($target:expr) => {
&|msg: &TaskMessage| match msg {
&TaskMessage::DisplayMessage(msg, progress) => {
if let Err(v) = $target.send(InstallMessage::Status(msg.to_string(), progress as _))
{
error!("Failed to submit queue message: {:?}", v);
}
}
&TaskMessage::PackageInstalled => {
if let Err(v) = $target.send(InstallMessage::PackageInstalled) {
error!("Failed to submit queue message: {:?}", v);
}
}
}
};
}
impl InstallerFramework {
/// Returns a copy of the configuration.
pub fn get_config(&self) -> Option<Config> {
@ -170,11 +190,8 @@ impl InstallerFramework {
info!("Dependency tree:\n{}", tree);
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| ())
tree.execute(self, declare_messenger_callback!(messages))
.map(|_x| ())
}
/// Sends a request for everything to be uninstalled.
@ -192,22 +209,16 @@ impl InstallerFramework {
info!("Dependency tree:\n{}", tree);
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| ())?;
tree.execute(self, declare_messenger_callback!(messages))
.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| ())?;
tree.execute(self, declare_messenger_callback!(messages))
.map(|_x| ())?;
// Delete the metadata file
let path = self

View file

@ -7,7 +7,7 @@ use log;
use std::fmt::Debug;
use std::io;
pub fn setup_logger(file_name : String) -> Result<(), fern::InitError> {
pub fn setup_logger(file_name: String) -> Result<(), fern::InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(

View file

@ -93,10 +93,10 @@ enum CallbackType {
}
fn main() {
let config =
BaseAttributes::from_toml_str(RAW_CONFIG).expect("Config file could not be read");
let config = BaseAttributes::from_toml_str(RAW_CONFIG).expect("Config file could not be read");
logging::setup_logger(format!("{}_installer.log", config.name)).expect("Unable to setup logging!");
logging::setup_logger(format!("{}_installer.log", config.name))
.expect("Unable to setup logging!");
let app_name = config.name.clone();

View file

@ -70,7 +70,7 @@ mod natives {
}
/// Cleans up the installer
pub fn burn_on_exit(app_name : &str) {
pub fn burn_on_exit(app_name: &str) {
let current_exe = env::current_exe().log_expect("Current executable could not be found");
let path = current_exe
.parent()
@ -123,7 +123,7 @@ mod natives {
}
/// Cleans up the installer
pub fn burn_on_exit(app_name : &str) {
pub fn burn_on_exit(app_name: &str) {
let current_exe = env::current_exe().log_expect("Current executable could not be found");
// Thank god for *nix platforms

View file

@ -45,8 +45,17 @@ impl ReleaseSource for GithubReleases {
.send()
.map_err(|x| format!("Error while sending HTTP request: {:?}", x))?;
if response.status() != StatusCode::OK {
return Err(format!("Bad status code: {:?}", response.status()));
match response.status() {
StatusCode::OK => {}
StatusCode::FORBIDDEN => {
return Err(format!(
"GitHub is rate limiting you. Try moving to a internet connection \
that isn't shared, and/or disabling VPNs."
));
}
_ => {
return Err(format!("Bad status code: {:?}.", response.status()));
}
}
let body = response

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskOrdering;
use tasks::TaskParamType;
@ -24,7 +25,7 @@ impl Task for DownloadPackageTask {
&mut self,
mut input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 1);
@ -45,7 +46,10 @@ impl Task for DownloadPackageTask {
}
}
messenger(&format!("Downloading package {:?}...", self.name), 0.0);
messenger(&TaskMessage::DisplayMessage(
&format!("Downloading package {:?}...", self.name),
0.0,
));
let mut downloaded = 0;
let mut data_storage: Vec<u8> = Vec::new();
@ -73,13 +77,13 @@ impl Task for DownloadPackageTask {
Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix),
};
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!(
"Downloading {} ({} of {})...",
self.name, pretty_current, pretty_total
),
percentage,
);
));
})?;
Ok(TaskParamType::FileContents(version, file, data_storage))

View file

@ -10,6 +10,7 @@ use tasks::uninstall_pkg::UninstallPackageTask;
use tasks::install_global_shortcut::InstallGlobalShortcutsTask;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskOrdering;
use tasks::TaskParamType;
@ -24,9 +25,9 @@ impl Task for InstallTask {
&mut self,
_: Vec<TaskParamType>,
_: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
messenger("Wrapping up...", 0.0);
messenger(&TaskMessage::DisplayMessage("Wrapping up...", 0.0));
Ok(TaskParamType::None)
}

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
use std::fs::create_dir_all;
@ -20,10 +21,13 @@ impl Task for VerifyInstallDirTask {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 0);
messenger("Polling installation directory...", 0.0);
messenger(&TaskMessage::DisplayMessage(
"Polling installation directory...",
0.0,
));
let path = context
.install_path

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
use logging::LoggingErrors;
@ -19,9 +20,12 @@ impl Task for InstallGlobalShortcutsTask {
&mut self,
_: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
messenger("Generating global shortcut...", 0.0);
messenger(&TaskMessage::DisplayMessage(
"Generating global shortcut...",
0.0,
));
let path = context
.install_path
@ -45,7 +49,7 @@ impl Task for InstallGlobalShortcutsTask {
.log_expect("Unable to build shortcut metadata (tool)");
let shortcut_file = create_shortcut(
&format!("{} maintenance tool", context.base_attributes.name),
&format!("{} Maintenance Tool", context.base_attributes.name),
&format!(
"Launch the {} Maintenance Tool to update, modify and uninstall the application.",
context.base_attributes.name

View file

@ -8,6 +8,7 @@ use tasks::save_database::SaveDatabaseTask;
use tasks::uninstall_pkg::UninstallPackageTask;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskOrdering;
use tasks::TaskParamType;
@ -33,9 +34,12 @@ impl Task for InstallPackageTask {
&mut self,
mut input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
messenger(&format!("Installing package {:?}...", self.name), 0.0);
messenger(&TaskMessage::DisplayMessage(
&format!("Installing package {:?}...", self.name),
0.0,
));
let path = context
.install_path
@ -91,16 +95,16 @@ impl Task for InstallPackageTask {
match &archive_size {
Some(size) => {
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!("Extracting {} ({} of {})", string_name, i + 1, size),
(i as f64) / (*size as f64),
);
));
}
_ => {
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!("Extracting {} ({} of ??)", string_name, i + 1),
0.0,
);
));
}
}
@ -170,6 +174,8 @@ impl Task for InstallPackageTask {
files: installed_files,
});
messenger(&TaskMessage::PackageInstalled);
Ok(TaskParamType::None)
}

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
use config::PackageDescription;
@ -21,12 +22,12 @@ impl Task for InstallShortcutsTask {
&mut self,
_: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!("Generating shortcuts for package {:?}...", self.name),
0.0,
);
));
let path = context
.install_path

View file

@ -58,6 +58,12 @@ impl TaskDependency {
}
}
/// A message from a task.
pub enum TaskMessage<'a> {
DisplayMessage(&'a str, f64),
PackageInstalled,
}
/// A Task is a small, async task conforming to a fixed set of inputs/outputs.
pub trait Task {
/// Executes this individual task, evaluating to the given Output result.
@ -67,7 +73,7 @@ pub trait Task {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String>;
/// Returns a vector containing all dependencies that need to be executed
@ -113,7 +119,7 @@ impl DependencyTree {
pub fn execute(
&mut self,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
let total_tasks = (self.dependencies.len() + 1) as f64;
@ -126,11 +132,14 @@ impl DependencyTree {
continue;
}
let result = i.execute(context, &|msg: &str, progress: f64| {
messenger(
msg,
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
)
let result = i.execute(context, &|msg: &TaskMessage| match msg {
&TaskMessage::DisplayMessage(msg, progress) => {
messenger(&TaskMessage::DisplayMessage(
msg,
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
))
}
_ => messenger(msg),
})?;
// Check to see if we skip matching other dependencies
@ -149,11 +158,14 @@ impl DependencyTree {
let task_result = self
.task
.execute(inputs, context, &|msg: &str, progress: f64| {
messenger(
msg,
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
)
.execute(inputs, context, &|msg: &TaskMessage| match msg {
&TaskMessage::DisplayMessage(msg, progress) => {
messenger(&TaskMessage::DisplayMessage(
msg,
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
))
}
_ => messenger(msg),
})?;
if let TaskParamType::Break = task_result {
@ -166,11 +178,14 @@ impl DependencyTree {
continue;
}
let result = i.execute(context, &|msg: &str, progress: f64| {
messenger(
msg,
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
)
let result = i.execute(context, &|msg: &TaskMessage| match msg {
&TaskMessage::DisplayMessage(msg, progress) => {
messenger(&TaskMessage::DisplayMessage(
msg,
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
))
}
_ => messenger(msg),
})?;
// Check to see if we skip matching other dependencies

View file

@ -6,6 +6,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
use config::PackageDescription;
@ -23,7 +24,7 @@ impl Task for ResolvePackageTask {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 0);
let mut metadata: Option<PackageDescription> = None;
@ -44,20 +45,20 @@ impl Task for ResolvePackageTask {
None => return Err(format!("Package {:?} could not be found.", self.name)),
};
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!(
"Polling {} for latest version of {:?}...",
package.source.name, package.name
),
0.0,
);
));
let results = package.source.get_current_releases()?;
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!("Resolving dependency for {:?}...", package.name),
0.5,
);
));
let filtered_regex = package.source.match_regex.replace("#PLATFORM#", OS);
let regex = match Regex::new(&filtered_regex) {

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
pub struct SaveDatabaseTask {}
@ -13,10 +14,13 @@ impl Task for SaveDatabaseTask {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 0);
messenger("Saving application database...", 0.0);
messenger(&TaskMessage::DisplayMessage(
"Saving application database...",
0.0,
));
context.save_database()?;

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
use std::fs::File;
@ -22,10 +23,13 @@ impl Task for SaveExecutableTask {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 0);
messenger("Copying installer binary...", 0.0);
messenger(&TaskMessage::DisplayMessage(
"Copying installer binary...",
0.0,
));
let path = context
.install_path

View file

@ -7,6 +7,7 @@ use tasks::TaskParamType;
use tasks::uninstall_pkg::UninstallPackageTask;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskOrdering;
pub struct UninstallTask {
@ -18,9 +19,9 @@ impl Task for UninstallTask {
&mut self,
_: Vec<TaskParamType>,
_: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
messenger("Wrapping up...", 0.0);
messenger(&TaskMessage::DisplayMessage("Wrapping up...", 0.0));
Ok(TaskParamType::None)
}

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
use std::fs::remove_file;
@ -17,11 +18,14 @@ impl Task for UninstallGlobalShortcutsTask {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 0);
messenger("Uninstalling global shortcut...", 0.0);
messenger(&TaskMessage::DisplayMessage(
"Uninstalling global shortcut...",
0.0,
));
while let Some(file) = context.database.shortcuts.pop() {
info!("Deleting shortcut {:?}", file);

View file

@ -5,6 +5,7 @@ use installer::InstallerFramework;
use tasks::save_database::SaveDatabaseTask;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskOrdering;
use tasks::TaskParamType;
@ -26,7 +27,7 @@ impl Task for UninstallPackageTask {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 1);
@ -57,7 +58,10 @@ impl Task for UninstallPackageTask {
}
};
messenger(&format!("Uninstalling package {:?}...", self.name), 0.0);
messenger(&TaskMessage::DisplayMessage(
&format!("Uninstalling package {:?}...", self.name),
0.0,
));
// Reverse, as to delete directories last
package.files.reverse();
@ -68,10 +72,10 @@ impl Task for UninstallPackageTask {
let file = path.join(file);
info!("Deleting {:?}", file);
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!("Deleting {} ({} of {})", name, i + 1, max),
(i as f64) / (max as f64),
);
));
let result = if file.is_dir() {
remove_dir(file)

View file

@ -4,6 +4,7 @@ use installer::InstallerFramework;
use tasks::Task;
use tasks::TaskDependency;
use tasks::TaskMessage;
use tasks::TaskParamType;
use installer::LocalInstallation;
@ -23,7 +24,7 @@ impl Task for UninstallShortcutsTask {
&mut self,
input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &Fn(&str, f64),
messenger: &Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
assert_eq!(input.len(), 0);
@ -54,10 +55,10 @@ impl Task for UninstallShortcutsTask {
}
};
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!("Uninstalling shortcuts for package {:?}...", self.name),
0.0,
);
));
// Reverse, as to delete directories last
package.files.reverse();
@ -68,10 +69,10 @@ impl Task for UninstallShortcutsTask {
let file = path.join(file);
info!("Deleting shortcut {:?}", file);
messenger(
messenger(&TaskMessage::DisplayMessage(
&format!("Deleting shortcut {} ({} of {})", name, i + 1, max),
(i as f64) / (max as f64),
);
));
let result = if file.is_dir() {
remove_dir(file)

View file

@ -199,7 +199,8 @@ const InstallPackages = {
is_uninstall: false,
is_updater_update: false,
is_update: false,
failed_with_error: false
failed_with_error: false,
packages_installed: 0
}
},
created: function() {
@ -238,6 +239,10 @@ const InstallPackages = {
that.progress = line.Status[1] * 100;
}
if (line.hasOwnProperty("PackageInstalled")) {
that.packages_installed += 1;
}
if (line.hasOwnProperty("Error")) {
if (app.metadata.is_launcher) {
app.exit();
@ -263,9 +268,17 @@ const InstallPackages = {
app.exit();
} else if (!that.failed_with_error) {
if (that.is_uninstall) {
router.replace({name: 'complete', params: {uninstall: true, update: that.is_update}});
router.replace({name: 'complete', params: {
uninstall: true,
update: that.is_update,
installed: that.packages_installed
}});
} else {
router.replace({name: 'complete', params: {uninstall: false, update: that.is_update}});
router.replace({name: 'complete', params: {
uninstall: false,
update: that.is_update,
installed: that.packages_installed
}});
}
}
}
@ -305,9 +318,16 @@ const CompleteView = {
template: `
<div class="column has-padding">
<div v-if="was_update">
<h4 class="subtitle">{{ $root.$data.attrs.name }} has been updated.</h4>
<p>You can find your installed applications in your start menu.</p>
<div v-if="has_installed">
<h4 class="subtitle">{{ $root.$data.attrs.name }} has been updated.</h4>
<p>You can find your installed applications in your start menu.</p>
</div>
<div v-else>
<h4 class="subtitle">{{ $root.$data.attrs.name }} is already up to date!</h4>
<p>You can find your installed applications in your start menu.</p>
</div>
</div>
<div v-else-if="was_install">
<h4 class="subtitle">Thanks for installing {{ $root.$data.attrs.name }}!</h4>
@ -328,7 +348,8 @@ const CompleteView = {
data: function() {
return {
was_install: !this.$route.params.uninstall,
was_update: this.$route.params.update
was_update: this.$route.params.update,
has_installed: this.$route.params.packages_installed > 0
}
},
methods: {
@ -420,7 +441,7 @@ const router = new VueRouter({
component: ErrorView
},
{
path: '/complete/:uninstall/:update',
path: '/complete/:uninstall/:update/:packages_installed',
name: 'complete',
component: CompleteView
},