liftinstall/src/tasks/install_pkg.rs
James 68109894f1 Update config files for v7 (#12)
* platform: fix build on Linux and update web-view

* deps: replace xz-decom with xz2 and update deps

* platform: fix regression...

... that prevents the build on Windows

* linux: implement platform-dependent functions

* travis: add macos and windows CI

* travis: use official Rust Docker image

* Update Cargo.lock for new version

* Break apart REST into separate services

This cleans up locking, ensures consistent futures for all endpoints
and enhances code re-use.

* Clean up codebase, fixing minor errors

* Update packages, use async client for downloading config

While this has a hell of a lot more boilerplate, this is quite
a bit cleaner.

* Add explicit 'dyn's as per Rust nightly requirements

* Migrate self updating functions to own module

* Migrate assets to server module

* Use patched web-view to fix dialogs, remove nfd

* Implement basic dark mode

* Revert window.close usage

* ui: split files and use Webpack

* frontend: ui: include prebuilt assets...

... and update rust side stuff

* build: integrate webpack building into build.rs

* Polish Vue UI split

* Add instructions for node + yarn

* native: fix uninstall self-destruction behavior...... by not showing the command prompt window and fork-spawning the cmd

* native: deal with Unicode issues in native APIs

* native: further improve Unicode support on Windows

* travis: add cache and fix issues

* ui: use Buefy components to...

... beautify the UI

* ui: makes error message selectable

* Make launcher mode behaviour more robust

* Fix error display on launcher pages

* Correctly handle exit on error

* Bump installer version
2019-07-04 21:23:16 -04:00

211 lines
6.5 KiB
Rust

//! Installs a specific package.
use installer::InstallerFramework;
use tasks::download_pkg::DownloadPackageTask;
use tasks::install_shortcuts::InstallShortcutsTask;
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;
use config::PackageDescription;
use installer::LocalInstallation;
use std::fs::create_dir_all;
use std::io::copy;
use logging::LoggingErrors;
use archives;
use std::fs::OpenOptions;
use std::path::Path;
pub struct InstallPackageTask {
pub name: String,
}
impl Task for InstallPackageTask {
fn execute(
&mut self,
mut input: Vec<TaskParamType>,
context: &mut InstallerFramework,
messenger: &dyn Fn(&TaskMessage),
) -> Result<TaskParamType, String> {
messenger(&TaskMessage::DisplayMessage(
&format!("Installing package {:?}...", self.name),
0.0,
));
let path = context
.install_path
.as_ref()
.log_expect("No install path specified");
let mut installed_files = Vec::new();
let mut metadata: Option<PackageDescription> = None;
for description in &context
.config
.as_ref()
.log_expect("Should have packages by now")
.packages
{
if self.name == description.name {
metadata = Some(description.clone());
break;
}
}
let package = match metadata {
Some(v) => v,
None => return Err(format!("Package {:?} could not be found.", self.name)),
};
// Grab data from the shortcut generator
let shortcuts = input.pop().log_expect("Should have input from resolver!");
let shortcuts = match shortcuts {
TaskParamType::GeneratedShortcuts(files) => files,
// If the resolver returned early, we need to unwind
TaskParamType::Break => return Ok(TaskParamType::None),
_ => return Err("Unexpected shortcuts param type to install package".to_string()),
};
// Ignore input from the uninstaller - no useful information passed
input.pop();
// Grab data from the resolver
let data = input.pop().log_expect("Should have input from resolver!");
let (version, file, data) = match data {
TaskParamType::FileContents(version, file, data) => (version, file, data),
_ => return Err("Unexpected file contents param type to install package".to_string()),
};
let mut archive = archives::read_archive(&file.name, data.as_slice())?;
archive.for_each(&mut |i, archive_size, filename, mut file| {
let string_name = filename
.to_str()
.ok_or("Unable to get str from file name")?
.to_string();
match &archive_size {
Some(size) => {
messenger(&TaskMessage::DisplayMessage(
&format!("Extracting {} ({} of {})", string_name, i + 1, size),
(i as f64) / (*size as f64),
));
}
_ => {
messenger(&TaskMessage::DisplayMessage(
&format!("Extracting {} ({} of ??)", string_name, i + 1),
0.0,
));
}
}
// Ensure that parent directories exist
let mut parent_dir: &Path = &filename;
while let Some(v) = parent_dir.parent() {
parent_dir = v;
let string_name = parent_dir
.to_str()
.ok_or("Unable to get str from file name")?
.to_string();
if string_name.is_empty() {
continue;
}
if !installed_files.contains(&string_name) {
info!("Creating dir: {:?}", string_name);
installed_files.push(string_name);
}
match create_dir_all(path.join(&parent_dir)) {
Ok(v) => v,
Err(v) => return Err(format!("Unable to create dir: {:?}", v)),
}
}
// Create target file
let target_path = path.join(&filename);
info!("Creating file: {:?}", string_name);
if !installed_files.contains(&string_name) {
installed_files.push(string_name.to_string());
}
let mut file_metadata = OpenOptions::new();
file_metadata.write(true).create_new(true);
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
file_metadata.mode(0o770);
}
let mut target_file = match file_metadata.open(target_path) {
Ok(v) => v,
Err(v) => return Err(format!("Unable to open file handle: {:?}", v)),
};
// Cross the streams
match copy(&mut file, &mut target_file) {
Ok(v) => v,
Err(v) => return Err(format!("Unable to write to file: {:?}", v)),
};
Ok(())
})?;
// Save metadata about this package
context.database.packages.push(LocalInstallation {
name: package.name.to_owned(),
version,
shortcuts,
files: installed_files,
});
messenger(&TaskMessage::PackageInstalled);
Ok(TaskParamType::None)
}
fn dependencies(&self) -> Vec<TaskDependency> {
vec![
TaskDependency::build(
TaskOrdering::Pre,
Box::new(DownloadPackageTask {
name: self.name.clone(),
}),
),
TaskDependency::build(
TaskOrdering::Pre,
Box::new(UninstallPackageTask {
name: self.name.clone(),
optional: true,
}),
),
TaskDependency::build(
TaskOrdering::Pre,
Box::new(InstallShortcutsTask {
name: self.name.clone(),
}),
),
TaskDependency::build(TaskOrdering::Post, Box::new(SaveDatabaseTask {})),
]
}
fn name(&self) -> String {
format!("InstallPackageTask (for {:?})", self.name)
}
}