diff --git a/config.toml b/config.toml
index 5d8c9fb..5f49e8d 100644
--- a/config.toml
+++ b/config.toml
@@ -5,7 +5,6 @@ installing_message = "Reminder: yuzu is an experimental emulator. Stuff w
[[packages]]
name = "yuzu Nightly"
description = "The nightly build of yuzu contains already reviewed and tested features."
-default = true
[packages.source]
name = "github"
match = "^yuzu-#PLATFORM#(-mingw)?-[0-9]*-[0-9a-f]*.zip$"
@@ -20,3 +19,13 @@ description = "The canary build of yuzu has additional features that are still w
match = "^yuzu-#PLATFORM#(-mingw)?-[0-9]*-[0-9a-f]*.zip$"
[packages.source.config]
repo = "yuzu-emu/yuzu-canary"
+
+[[packages]]
+name = "Test package"
+description = "Just a testing package"
+default = true
+ [packages.source]
+ name = "github"
+ match = "^TestPackage.zip$"
+ [packages.source.config]
+ repo = "j-selby/test-installer"
diff --git a/src/installer.rs b/src/installer.rs
index a92dcff..b006b62 100644
--- a/src/installer.rs
+++ b/src/installer.rs
@@ -19,11 +19,11 @@ use std::env::var;
use std::env::current_exe;
use std::env::consts::OS;
+use std::path::Path;
use std::path::PathBuf;
use std::io::Cursor;
use std::io::copy;
-use std::io::Write;
use std::sync::Arc;
use std::sync::Mutex;
@@ -47,6 +47,9 @@ pub enum InstallMessage {
/// etc.
pub struct InstallerFramework {
config: Config,
+ database: Vec,
+ install_path: Option,
+ preexisting_install: bool
}
/// Used to track the amount of data that has been downloaded during a HTTP request.
@@ -83,13 +86,21 @@ impl InstallerFramework {
}
/// Sends a request for something to be installed.
+ /// items: Array of named packages to be installed
+ /// messages: Channel used to send progress messages
+ /// fresh_install: If the install directory must be empty
pub fn install(
- &self,
+ &mut self,
items: Vec,
- path: &str,
messages: &Sender,
+ fresh_install: bool
) -> Result<(), String> {
- // TODO: Error handling
+ // We have to have a install path for us to be able to do anything
+ let path = match self.install_path.clone() {
+ Some(v) => v,
+ None => return Err(format!("No install directory for installer"))
+ };
+
println!("Framework: Installing {:?} to {}", items, path);
// Create our install directory
@@ -106,13 +117,15 @@ impl InstallerFramework {
}
// Make sure it is empty
- let paths = match read_dir(&path) {
- Ok(v) => v,
- Err(v) => return Err(format!("Failed to read install destination: {:?}", v)),
- };
+ if fresh_install {
+ let paths = match read_dir(&path) {
+ Ok(v) => v,
+ Err(v) => return Err(format!("Failed to read install destination: {:?}", v)),
+ };
- if paths.count() != 0 {
- return Err(format!("Install destination is not empty."));
+ if paths.count() != 0 {
+ return Err(format!("Install destination is not empty."));
+ }
}
// Resolve items in config
@@ -126,12 +139,12 @@ impl InstallerFramework {
println!("Resolved to {:?}", to_install);
+ // TODO: Uninstall pre-existing packages
+
// Install packages
let mut count = 0.0 as f64;
let max = to_install.len() as f64;
- let mut installed_packages = Vec::new();
-
for package in to_install.iter() {
let base_package_percentage = count / max;
let base_package_range = ((count + 1.0) / max) - base_package_percentage;
@@ -165,6 +178,8 @@ impl InstallerFramework {
Err(v) => return Err(format!("An error occured while compiling regex: {:?}", v)),
};
+ println!("Releases: {:?}", results);
+
// Find the latest release in here
let latest_result = results
.into_iter()
@@ -304,7 +319,8 @@ impl InstallerFramework {
}
// Save metadata about this package
- installed_packages.push(LocalInstallation {
+ // TODO: Check if this package already exists
+ self.database.push(LocalInstallation {
name: package.name.to_owned(),
version: latest_version,
files: installed_files,
@@ -313,22 +329,7 @@ impl InstallerFramework {
count += 1.0;
}
- // Make copy of metadata
- // TODO: Combine with already installed database, if needed
- let metadata_compiled = serde_json::to_string(&installed_packages).unwrap();
-
- {
- let metadata_path = path.join("metadata.json");
- let mut metadata_file = match File::create(metadata_path) {
- Ok(v) => v,
- Err(v) => return Err(format!("Unable to open file handle: {:?}", v)),
- };
-
- match metadata_file.write_all(metadata_compiled.as_bytes()) {
- Ok(v) => v,
- Err(v) => return Err(format!("Unable to write to file: {:?}", v)),
- };
- }
+ self.save_database()?;
// Copy installer binary to target directory
messages
@@ -369,8 +370,63 @@ impl InstallerFramework {
Ok(())
}
+ /// Saves the applications database.
+ pub fn save_database(&self) -> Result<(), String> {
+ // We have to have a install path for us to be able to do anything
+ let path = match self.install_path.clone() {
+ Some(v) => v,
+ None => return Err(format!("No install directory for installer"))
+ };
+
+ let metadata_path = Path::new(&path).join("metadata.json");
+ let metadata_file = match File::create(metadata_path) {
+ Ok(v) => v,
+ Err(v) => return Err(format!("Unable to open file handle: {:?}", v)),
+ };
+
+ match serde_json::to_writer(metadata_file, &self.database) {
+ Ok(v) => v,
+ Err(v) => return Err(format!("Unable to write to file: {:?}", v)),
+ };
+
+ Ok(())
+ }
+
+ /// Configures this installer to install to the specified location.
+ /// If there was a currently configured install path, this will be left as-is.
+ pub fn set_install_dir(&mut self, dir : &str) {
+ self.install_path = Some(dir.to_owned());
+ }
+
/// Creates a new instance of the Installer Framework with a specified Config.
pub fn new(config: Config) -> Self {
- InstallerFramework { config }
+ InstallerFramework {
+ config,
+ database : Vec::new(),
+ install_path : None,
+ preexisting_install : false
+ }
+ }
+
+ /// Creates a new instance of the Installer Framework with a specified Config, managing
+ /// a pre-existing installation.
+ pub fn new_with_db(config: Config, install_path : String) -> Result {
+ let metadata_path = Path::new(&install_path).join("metadata.json");
+ let metadata_file = match File::open(metadata_path) {
+ Ok(v) => v,
+ Err(v) => return Err(format!("Unable to open file handle: {:?}", v)),
+ };
+
+ let database : Vec = match serde_json::from_reader(metadata_file) {
+ Ok(v) => v,
+ Err(v) => return Err(format!("Unable to read metadata file: {:?}", v)),
+ };
+
+ Ok(InstallerFramework {
+ config,
+ database,
+ install_path : Some(install_path),
+ preexisting_install : true
+ })
}
}
diff --git a/src/main.rs b/src/main.rs
index 41607ba..e95f601 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -38,6 +38,7 @@ use config::Config;
use installer::InstallerFramework;
use rest::WebServer;
+use std::path::Path;
// TODO: Fetch this over a HTTP request?
static RAW_CONFIG: &'static str = include_str!("../config.toml");
@@ -47,7 +48,12 @@ fn main() {
let app_name = config.general.name.clone();
- let framework = InstallerFramework::new(config);
+ let metadata_file = Path::new("metadata.json");
+ let framework = if metadata_file.exists() {
+ InstallerFramework::new_with_db(config, format!("./")).unwrap()
+ } else {
+ InstallerFramework::new(config)
+ };
let server = WebServer::new(framework).unwrap();
diff --git a/src/rest.rs b/src/rest.rs
index b1c622b..6e7b9b8 100644
--- a/src/rest.rs
+++ b/src/rest.rs
@@ -31,6 +31,7 @@ use assets;
use installer::InstallerFramework;
use installer::InstallMessage;
+use std::sync::RwLock;
#[derive(Serialize)]
struct FileSelection {
@@ -63,7 +64,7 @@ impl WebServer {
let (sender, receiver) = channel();
let handle = thread::spawn(move || {
- let shared_framework = Arc::new(framework);
+ let shared_framework = Arc::new(RwLock::new(framework));
let server = Http::new()
.bind(&addr, move || {
@@ -89,7 +90,7 @@ impl WebServer {
/// Holds internal state for Hyper
struct WebService {
- framework: Arc,
+ framework: Arc>,
}
impl Service for WebService {
@@ -104,9 +105,11 @@ impl Service for WebService {
// This endpoint should be usable directly from a