From cada46738a5986f91498b4cd2724736fe7cc54e3 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 7 Aug 2018 20:17:01 +1000 Subject: [PATCH] Do that routing magic --- Cargo.lock | 20 +++++ Cargo.toml | 3 + build.rs | 103 ++++++++++++++++++++++++- src/assets.rs | 2 +- src/logging.rs | 3 +- src/main.rs | 3 +- src/rest.rs | 3 +- src/sources/github/mod.rs | 7 +- static/index.html | 32 ++------ static/js/views.js | 153 +++++++++++++++++++++++++++++--------- 10 files changed, 256 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b04885a..532bf3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -369,6 +369,7 @@ dependencies = [ "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "web-view 0.2.1 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)", "winres 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "zip 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -732,6 +733,14 @@ name = "safemem" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "same-file" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "schannel" version = "0.1.10" @@ -1089,6 +1098,15 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "walkdir" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "want" version = "0.0.4" @@ -1263,6 +1281,7 @@ dependencies = [ "checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5" "checksum reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2abe46f8e00792693a2488e296c593d1f4ea39bb1178cfce081d6793657575e4" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" "checksum schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "acece75e0f987c48863a6c792ec8b7d6c4177d4a027f8ccc72f849794f437016" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" @@ -1309,6 +1328,7 @@ dependencies = [ "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum walkdir 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1b768ba943161a9226ccd59b26bcd901e5d60e6061f4fcad3034784e0c7372b" "checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" "checksum web-view 0.2.1 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)" = "" "checksum webview-sys 0.1.0 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)" = "" diff --git a/Cargo.toml b/Cargo.toml index d588f67..7650e4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,9 @@ chrono = "0.4.5" clap = "2.32.0" +[build-dependencies] +walkdir = "2" + [target.'cfg(windows)'.dependencies] # NFD is needed on Windows, as web-view doesn't work correctly here nfd = "0.0.4" diff --git a/build.rs b/build.rs index a0492ec..50fa30b 100644 --- a/build.rs +++ b/build.rs @@ -1,12 +1,111 @@ +extern crate walkdir; #[cfg(windows)] extern crate winres; +use walkdir::WalkDir; + +use std::env; +use std::path::PathBuf; + +use std::fs::copy; +use std::fs::create_dir_all; +use std::fs::File; + +use std::io::BufRead; +use std::io::BufReader; +use std::io::Write; + +const FILES_TO_PREPROCESS: &'static [&'static str] = &["helpers.js", "views.js"]; + #[cfg(windows)] -fn main() { +fn handle_binary() { let mut res = winres::WindowsResource::new(); res.set_icon("static/favicon.ico"); res.compile().expect("Failed to generate metadata"); } #[cfg(not(windows))] -fn main() {} +fn handle_binary() {} + +fn main() { + handle_binary(); + + let output_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + // Copy files from static/ to build dir + for entry in WalkDir::new("static") { + let entry = entry.expect("Unable to read output directory"); + + let output_file = output_dir.join(entry.path()); + + if entry.path().is_dir() { + create_dir_all(output_file).expect("Unable to create dir"); + } else { + let filename = entry + .path() + .file_name() + .expect("Unable to parse filename") + .to_str() + .expect("Unable to convert to string"); + + if FILES_TO_PREPROCESS.contains(&filename) { + // Do basic preprocessing - transcribe template string + let source = BufReader::new(File::open(entry.path()).expect("Unable to copy file")); + let mut target = File::create(output_file).expect("Unable to copy file"); + + let mut is_template_string = false; + + for line in source.lines() { + let line = line.expect("Unable to read line from JS file"); + + let mut is_break = false; + let mut is_quote = false; + + let mut output_line = String::new(); + + if is_template_string { + output_line += "\""; + } + + for c in line.chars() { + if c == '\\' { + is_break = true; + output_line.push('\\'); + continue; + } + + if (c == '\"' || c == '\'') && !is_break && !is_template_string { + is_quote = !is_quote; + } + + if c == '`' && !is_break && !is_quote { + output_line += "\""; + is_template_string = !is_template_string; + continue; + } + + if c == '"' && !is_break && is_template_string { + output_line += "\\\""; + continue; + } + + is_break = false; + output_line.push(c); + } + + if is_template_string { + output_line += "\" +"; + } + + output_line.push('\n'); + + target + .write(output_line.as_bytes()) + .expect("Unable to write line"); + } + } else { + copy(entry.path(), output_file).expect("Unable to copy file"); + } + } + } +} diff --git a/src/assets.rs b/src/assets.rs index 7384c61..8f311fb 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -8,7 +8,7 @@ macro_rules! include_files_as_assets { ( $target_match:expr, $( $file_name:expr ),* ) => { match $target_match { $( - $file_name => Some(include_bytes!(concat!("../static/", $file_name)).as_ref()), + $file_name => Some(include_bytes!(concat!(concat!(env!("OUT_DIR"), "/static/"), $file_name)).as_ref()), )* _ => None } diff --git a/src/logging.rs b/src/logging.rs index b80ded6..659a52a 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -17,8 +17,7 @@ pub fn setup_logger() -> Result<(), fern::InitError> { record.level(), message )) - }) - .level(log::LevelFilter::Info) + }).level(log::LevelFilter::Info) .chain(io::stdout()) .chain(fern::log_file("installer.log")?) .apply()?; diff --git a/src/main.rs b/src/main.rs index 53e5c9c..61b9f72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,8 +96,7 @@ fn main() { .value_name("TARGET") .help("Launches the specified executable after checking for updates") .takes_value(true), - ) - .get_matches(); + ).get_matches(); info!("{} installer", app_name); diff --git a/src/rest.rs b/src/rest.rs index d874ec9..c389e6b 100644 --- a/src/rest.rs +++ b/src/rest.rs @@ -59,8 +59,7 @@ impl WebServer { Ok(WebService { framework: framework.clone(), }) - }) - .log_expect("Failed to bind to port"); + }).log_expect("Failed to bind to port"); server.run().log_expect("Failed to run HTTP server"); }); diff --git a/src/sources/github/mod.rs b/src/sources/github/mod.rs index 1066a5d..a90dac0 100644 --- a/src/sources/github/mod.rs +++ b/src/sources/github/mod.rs @@ -40,8 +40,7 @@ impl ReleaseSource for GithubReleases { .get(&format!( "https://api.github.com/repos/{}/releases", config.repo - )) - .header(UserAgent::new("liftinstall (j-selby)")) + )).header(UserAgent::new("liftinstall (j-selby)")) .send() .map_err(|x| format!("Error while sending HTTP request: {:?}", x))?; @@ -53,8 +52,8 @@ impl ReleaseSource for GithubReleases { .text() .map_err(|x| format!("Failed to decode HTTP response body: {:?}", x))?; - let result: serde_json::Value = - serde_json::from_str(&body).map_err(|x| format!("Failed to parse response: {:?}", x))?; + let result: serde_json::Value = serde_json::from_str(&body) + .map_err(|x| format!("Failed to parse response: {:?}", x))?; let result: &Vec = result .as_array() diff --git a/static/index.html b/static/index.html index 7bd63c4..4b7243d 100644 --- a/static/index.html +++ b/static/index.html @@ -141,7 +141,7 @@ - @@ -196,32 +196,32 @@ } var app = new Vue({ - router, + router: router, data: { attrs: base_attributes, config : {}, install_location : "", // If the initial modify menu should be shown - modify_install : false, + /*modify_install : false, // If the package selection screen should be shown select_packages : true, // If an installation operation is happening is_installing : false, // If an installation has completed successfully - is_finished : false, + is_finished : false,*/ // If the application should act as an launcher, rather than as an installer is_launcher : false, launcher_path : undefined, // If a confirmation prompt should be shown - confirm_uninstall : false, + /*confirm_uninstall : false, // If the downloading config page should be shown is_downloading_config : false, progress : 0, progress_message : "", - has_error : false, + has_error : false,*/ // If the option to pick an install location should be provided show_install_location : true, - error : "", + //error : "", metadata : { database : [], install_path : "", @@ -229,22 +229,6 @@ } }, methods: { - "back_to_packages": function() { - app.select_packages = true; - app.has_error = false; - app.is_installing = false; - app.is_finished = false; - }, - "prepare_uninstall": function() { - app.confirm_uninstall = true; - }, - "cancel_uninstall": function() { - app.confirm_uninstall = false; - }, - "modify_packages": function() { - app.select_packages = true; - app.modify_install = false; - }, "exit": function() { ajax("/api/exit", function() {}); } diff --git a/static/js/views.js b/static/js/views.js index fd0f0d1..bf93c1f 100644 --- a/static/js/views.js +++ b/static/js/views.js @@ -1,6 +1,6 @@ const DownloadConfig = { template: ` -
+

Downloading config...


@@ -14,19 +14,23 @@ const DownloadConfig = { }, methods: { download_install_status: function() { - ajax("/api/installation-status", (e) => { + var that = this; // IE workaround + + ajax("/api/installation-status", function(e) { app.metadata = e; - this.download_config(); + that.download_config(); }); }, download_config: function() { - ajax("/api/config", (e) => { + var that = this; // IE workaround + + ajax("/api/config", function(e) { app.config = e; - this.choose_next_state(); + that.choose_next_state(); - }, (e) => { + }, function(e) { console.error("Got error while downloading config: " + e); @@ -71,7 +75,7 @@ const DownloadConfig = { // Need to do a bit more digging to get at the // install location. - ajax("/api/default-path", (e) => { + ajax("/api/default-path", function(e) { if (e.path != null) { app.install_location = e.path; } @@ -79,30 +83,13 @@ const DownloadConfig = { router.replace("/packages"); } - - /*app.is_downloading_config = false; - if (e.preexisting_install) { - app.modify_install = true; - app.select_packages = false; - app.show_install_location = false; - app.install_location = e.install_path; - - - - if (e.is_launcher) { - - app.is_launcher = true; - app.install(); - } - } else { - }*/ } } }; const SelectPackages = { template: ` -
+

Select your preferred settings:

@@ -130,13 +117,18 @@ const SelectPackages = { placeholder="Enter a install path here">
- Install! + Install! + Modify + Back
`, methods: { @@ -149,13 +141,16 @@ const SelectPackages = { }, install: function() { router.push("/install/regular"); + }, + go_back: function() { + router.go(-1); } } }; const InstallPackages = { template: ` -
+

Checking for updates...

Uninstalling...

Installing...

@@ -193,25 +188,27 @@ const InstallPackages = { results["path"] = app.install_location; + var that = this; // IE workaround + stream_ajax(this.is_uninstall ? "/api/uninstall" : - "/api/start-install", (line) => { + "/api/start-install", function(line) { if (line.hasOwnProperty("Status")) { - this.progress_message = line.Status[0]; - this.progress = line.Status[1] * 100; + that.progress_message = line.Status[0]; + that.progress = line.Status[1] * 100; } if (line.hasOwnProperty("Error")) { if (app.metadata.is_launcher) { app.exit(); } else { - this.failed_with_error = true; + that.failed_with_error = true; router.replace({name: 'showerr', params: {msg: line.Error}}); } } - }, (e) => { + }, function(e) { if (app.metadata.is_launcher) { app.exit(); - } else if (!this.failed_with_error) { + } else if (!that.failed_with_error) { router.push("/complete"); } }, undefined, results); @@ -221,7 +218,7 @@ const InstallPackages = { const ErrorView = { template: ` -
+

An error occurred:

{{ msg }} @@ -242,7 +239,81 @@ const ErrorView = { } }; -const +const CompleteView = { + template: ` +
+

Thanks for installing {{ $root.$data.attrs.name }}!

+ + Exit +
+ `, + methods: { + exit: function() { + app.exit(); + } + } +}; + +const ModifyView = { + template: ` +
+

Choose an option:

+ + + + +
+ `, + data: function() { + return { + show_uninstall: false + } + }, + methods: { + update: function() { + router.push("/install/regular"); + }, + modify_packages: function() { + router.push("/packages"); + }, + prepare_uninstall: function() { + this.show_uninstall = true; + }, + cancel_uninstall: function() { + this.show_uninstall = false; + }, + uninstall: function() { + router.push("/install/uninstall"); + }, + } +}; const router = new VueRouter({ routes: [ @@ -266,6 +337,16 @@ const router = new VueRouter({ name: 'showerr', component: ErrorView }, + { + path: '/complete', + name: 'complete', + component: CompleteView + }, + { + path: '/modify', + name: 'modify', + component: ModifyView + }, { path: '/', redirect: '/config'