Implement configuration handling, Vue

This commit is contained in:
James 2018-01-27 14:27:41 +11:00
parent 5b43f65ae5
commit f44b812a1a
9 changed files with 11169 additions and 94 deletions

96
Cargo.lock generated
View File

@ -32,6 +32,11 @@ name = "chunked_transfer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dtoa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "encoding"
version = "0.2.33"
@ -136,6 +141,11 @@ dependencies = [
"walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -251,6 +261,11 @@ name = "pkg-config"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.3.20"
@ -270,11 +285,64 @@ name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "siphasher"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.39"
@ -299,6 +367,19 @@ dependencies = [
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "0.2.38"
@ -373,7 +454,11 @@ dependencies = [
"includedir 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"includedir_codegen 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny_http 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"web-view 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -384,6 +469,7 @@ dependencies = [
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
"checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
"checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
"checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
@ -397,6 +483,7 @@ dependencies = [
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum includedir 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ed470a2a5c0afe4587796a886aa185fcef159feaefd8c4f40d85423aeeec4a3a"
"checksum includedir_codegen 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4cb2bb86e79496ab481fc7865ce8c2960cf1eb40cc1411524ce67fce54f3c95e"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
@ -412,12 +499,21 @@ dependencies = [
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0"
"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5"
"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
"checksum tiny_http 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "016f040cfc9b5be610de3619eaaa57017fa0b0b678187327bde75fc146e2a41f"
"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)" = "cbaa8377a162d88e7d15db0cf110c8523453edcbc5bc66d2b6fffccffa34a068"
"checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f"
"checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780"

View File

@ -10,5 +10,11 @@ tiny_http = "0.5.8"
includedir = "0.2.2"
phf = "0.7.21"
serde = "1.0.27"
serde_derive = "1.0.27"
serde_json = "1.0.9"
toml = "0.4"
[build-dependencies]
includedir_codegen = "0.2.0"

40
src/config.rs Normal file
View File

@ -0,0 +1,40 @@
/// config.rs
///
/// Contains Config structures, as well as means of serialising them.
use toml;
use toml::de::Error as TomlError;
use serde_json::{self, Error as SerdeError};
/// Describes a overview of a individual package.
#[derive(Deserialize, Serialize, Clone)]
pub struct PackageDescription {
name : String,
description : String,
default : Option<bool>
}
/// Describes the application itself.
#[derive(Deserialize, Serialize, Clone)]
pub struct GeneralConfig {
name : String
}
#[derive(Deserialize, Serialize, Clone)]
pub struct Config {
general : GeneralConfig,
packages : Vec<PackageDescription>
}
impl Config {
/// Serialises as a JSON string.
pub fn to_json_str(&self) -> Result<String, SerdeError> {
serde_json::to_string(self)
}
/// Builds a configuration from a specified TOML string.
pub fn from_toml_str(contents : &str) -> Result<Self, TomlError> {
toml::from_str(contents)
}
}

25
src/installer.rs Normal file
View File

@ -0,0 +1,25 @@
/// installer.rs
///
/// Contains the main installer structure, as well as high-level means of controlling it.
use config::Config;
/// The installer framework contains metadata about packages, what is installable, what isn't,
/// etc.
pub struct InstallerFramework {
config : Config
}
impl InstallerFramework {
/// Returns a copy of the configuration.
pub fn get_config(&self) -> Config {
self.config.clone()
}
/// Creates a new instance of the Installer Framework with a specified Config.
pub fn new(config : Config) -> Self {
InstallerFramework {
config
}
}
}

View File

@ -1,4 +1,4 @@
//#![windows_subsystem = "windows"]
#![windows_subsystem = "windows"]
extern crate web_view;
extern crate tiny_http;
@ -6,54 +6,40 @@ extern crate tiny_http;
extern crate includedir;
extern crate phf;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate toml;
mod assets;
mod rest;
mod config;
mod installer;
use web_view::*;
use tiny_http::Server;
use tiny_http::Response;
use tiny_http::Header;
use config::Config;
use std::thread;
use std::str::FromStr;
use installer::InstallerFramework;
use rest::WebServer;
// TODO: Fetch this over a HTTP request?
static RAW_CONFIG : &'static str = include_str!("../config.toml");
fn main() {
let config = Config::from_toml_str(RAW_CONFIG).unwrap();
let framework = InstallerFramework::new(config);
let server = WebServer::new(framework).unwrap();
// Startup HTTP server for handling the web view
let server = Server::http("127.0.0.1:0").unwrap();
let http_address = format!("http://{}", server.get_addr());
println!("{}", http_address);
let http_address = format!("http://{}", server.server_addr());
println!("{}", format!("{}", server.server_addr()));
let _ = thread::spawn(move || {
loop {
// blocks until the next request is received
let request = match server.recv() {
Ok(rq) => rq,
Err(e) => { println!("error: {}", e); break }
};
// Work out what they want
let mut url : String = request.url().into();
if url.ends_with("/") {
url += "index.html";
}
println!("Requesting: {}", url);
match assets::file_from_string(&url) {
Some((content_type, file)) => {
let mut response = Response::from_data(file);
if let Some(content_type) = content_type {
response.add_header(Header::from_str(
&format!("Content-Type:{}", content_type)).unwrap())
}
request.respond(response)
},
None => request.respond(Response::empty(404))
}.unwrap();
}
});
server.start();
// Init the web view
let size = (1024, 550);

119
src/rest.rs Normal file
View File

@ -0,0 +1,119 @@
/// rest.rs
///
/// Provides a HTTP/REST server for both frontend<->backend communication, as well
/// as talking to external applications.
use tiny_http::{Server, Request, Response, Header};
use std::error::Error;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::thread::{self, JoinHandle};
use std::str::FromStr;
use assets;
use installer::InstallerFramework;
/// Encapsulates tiny_http's state.
pub struct WebServer {
server : Server,
framework : InstallerFramework
}
impl WebServer {
/// Handles a Web Server request.
fn handle_request(&self, request : Request) {
// Work out what they want
let mut url : String = request.url().into();
println!("Serving request: {}", url);
// Capture API calls before they fall into phf
if url.starts_with("/api/") {
let api_url = &url[5 ..];
let call_response = self.rest_call(api_url);
match call_response {
Some(response) => request.respond(Response::from_string(response)),
None => request.respond(Response::empty(404))
}.unwrap();
return;
}
// At this point, we have a web browser client. Search for a index page
// if needed
if url.ends_with("/") {
url += "index.html";
}
match assets::file_from_string(&url) {
Some((content_type, file)) => {
let mut response = Response::from_data(file);
if let Some(content_type) = content_type {
response.add_header(Header::from_str(
&format!("Content-Type:{}", content_type)).unwrap())
}
request.respond(response)
},
None => request.respond(Response::empty(404))
}.unwrap();
}
/// Makes a call to a REST endpoint.
fn rest_call(&self, path : &str) -> Option<String> {
match path {
// This endpoint should be usable directly from a <script> tag during loading.
"config" => Some(enscapsulate_json("config",
&self.framework.get_config().to_json_str().unwrap())),
_ => None
}
}
/// Runs the main loop of the web server.
fn run(&mut self) {
loop {
// Take a single request from the client
let request = match self.server.recv() {
Ok(rq) => rq,
Err(e) => { println!("error: {}", e); break }
};
self.handle_request(request);
}
}
/// Starts the webserver. Consumes the entity.
pub fn start(mut self) -> JoinHandle<()> {
thread::spawn(move || {
self.run()
})
}
/// Returns the bound address that the server is running from.
pub fn get_addr(&self) -> SocketAddr {
self.server.server_addr()
}
/// Creates a new web server, bound to a random port on localhost.
pub fn new(framework : InstallerFramework) -> Result<Self, Box<Error + Send + Sync + 'static>> {
WebServer::with_addr(framework, SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0))
}
/// Creates a new web server with the specified address.
pub fn with_addr(framework : InstallerFramework, addr : SocketAddr)
-> Result<Self, Box<Error + Send + Sync + 'static>> {
let server = Server::http(addr)?;
Ok(WebServer {
server,
framework
})
}
}
/// Encapsulates JSON as a injectable Javascript script.
fn enscapsulate_json(field_name : &str, json : &str) -> String {
format!("var {} = {};", field_name, json)
}

View File

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport"
@ -11,72 +11,71 @@
<link rel="stylesheet" href="/css/main.css" type="text/css">
</head>
<body>
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<div id="app">
<nav class="navbar is-dark" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<span class="navbar-item">
<img src="/img/logo.png" alt="Logo">
<img src="/img/logo.png" v-bind:alt="config.general.name">
</span>
</div>
</nav>
</div>
</nav>
<!-- Main content -->
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-one-third">
<h1 class="title">
Welcome to the yuzu installer!
</h1>
<h2 class="subtitle">
We will have you up and running in just a few moments.
</h2>
</div>
<!-- Main content -->
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-one-third">
<h1 class="title">
Welcome to the {{ config.general.name }} installer!
</h1>
<h2 class="subtitle">
We will have you up and running in just a few moments.
</h2>
</div>
<div class="column">
<h4 class="subtitle">Select your preferred settings:</h4>
<div class="column">
<h4 class="subtitle">Select your preferred settings:</h4>
<!-- Build options -->
<div class="tile is-ancestor">
<div class="tile is-parent">
<div class="tile is-child">
<div class="box">
<label class="checkbox">
<input type="checkbox" />
yuzu Nightly
</label>
<p>
The nightly build of yuzu contains already reviewed and tested features.
</p>
<!-- Build options -->
<div class="tile is-ancestor">
<div class="tile is-parent" v-for="package in config.packages" :index="package.name">
<div class="tile is-child">
<div class="box">
<label class="checkbox">
<input type="checkbox" v-bind:checked="package.default" />
{{ package.name }}
</label>
<p>
{{ package.description }}
</p>
</div>
</div>
</div>
</div>
<div class="tile is-parent">
<div class="tile is-child">
<div class="box">
<label class="checkbox">
<input type="checkbox" />
yuzu Canary
</label>
<p>
The canary build of yuzu has additional features that are still waiting on
review.
</p>
</div>
<div class="field">
<label class="label subtitle is-6">Install Location</label>
<div class="control">
<input class="input" type="text" placeholder="%localappdata%\yuzu">
</div>
</div>
</div>
<div class="field">
<label class="label subtitle is-6">Install Location</label>
<div class="control">
<input class="input" type="text" placeholder="%localappdata%\yuzu">
</div>
<a class="button is-primary">Install!</a>
</div>
<a class="button is-primary">Install!</a>
</div>
</div>
</div>
</section>
</section>
</div>
<script src="/api/config"></script>
<script src="/js/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
config : config
}
});
</script>
</body>
</html>

10798
static/js/vue.js Normal file

File diff suppressed because it is too large Load Diff

6
static/js/vue.min.js vendored Normal file

File diff suppressed because one or more lines are too long