Refactor(runner.rs) Clean up code to make it more composable and… (#259)

* update .gitignore

* remove cargo.lock

* add clone to config

* create functions

* finish merge and fix conflict.

* cleanup

* continue refactor cleanup

* fix logic for embedded server

* bringing back the polymorphisms!

* cleaning it all up...

* add imports

* add basic documentation.

* add a TauriResult type

* cleanup spawn_updater func

* cleanup imports and errors.

* fix error and proliferate it

* cleanup ?s
This commit is contained in:
Tensor-Programming 2020-01-01 05:30:17 -05:00 committed by Lucas Fernandes Nogueira
parent 7ae7c9dfc2
commit aa149370a4
5 changed files with 217 additions and 149 deletions

View File

@ -10,7 +10,7 @@ pub struct App {
impl App {
pub fn run(mut self) {
runner::run(&mut self);
runner::run(&mut self).expect("Failed to build webview");
}
pub(crate) fn run_invoke_handler(&mut self, webview: &mut WebView<'_, ()>, arg: &str) {

View File

@ -1,141 +1,202 @@
pub(crate) fn run(application: &mut crate::App) {
let debug = cfg!(debug_assertions);
let config = crate::config::get();
#[allow(unused_imports)]
use std::{fs::read_to_string, path::Path, process::Stdio, thread::spawn};
let content;
#[cfg(not(any(feature = "embedded-server", feature = "no-server")))]
{
content = if config.build.dev_path.starts_with("http") {
web_view::Content::Url(config.build.dev_path)
} else {
let dev_path = std::path::Path::new(&config.build.dev_path).join("index.tauri.html");
web_view::Content::Html(
std::fs::read_to_string(dev_path).expect("failed to read index.tauri.html"),
)
};
}
use web_view::{builder, Content, WebView};
#[cfg(feature = "embedded-server")]
let server_url;
use crate::config::{get, Config};
#[cfg(feature = "embedded-server")]
use crate::tcp::{get_available_port, port_is_available};
use crate::App;
use crate::TauriResult;
#[cfg(feature = "embedded-server")]
{
// define URL
let port;
let port_valid;
if config.tauri.embedded_server.port == "random" {
match crate::tcp::get_available_port() {
Some(available_port) => {
port = available_port.to_string();
port_valid = true;
}
None => {
port = "0".to_string();
port_valid = false;
}
}
} else {
port = config.tauri.embedded_server.port;
port_valid = crate::tcp::port_is_available(
port
.parse::<u16>()
.expect(&format!("Invalid port {}", port)),
);
}
if port_valid {
let mut url = format!("{}:{}", config.tauri.embedded_server.host, port);
if !url.starts_with("http") {
url = format!("http://{}", url);
}
server_url = url.clone();
content = web_view::Content::Url(url.to_string());
} else {
panic!(format!("Port {} is not valid or not open", port));
// JavaScript string literal
const JS_STRING: &'static str = r#"
if (window.onTauriInit !== void 0) {
window.onTauriInit()
window.onTauriInit = void 0
}
Object.defineProperty(window, 'onTauriInit', {
set: function(val) {
if (typeof(val) === 'function') {
val()
}
}
})
"#;
#[cfg(feature = "no-server")]
{
let index_path = std::path::Path::new(env!("TAURI_DIST_DIR")).join("index.tauri.html");
content =
web_view::Content::Html(std::fs::read_to_string(index_path).expect("failed to read string"));
}
// Main entry point function for running the Webview
pub(crate) fn run(application: &mut App) -> TauriResult<()> {
// get the tauri config struct
let config = get()?;
#[cfg(feature = "updater")]
{
std::thread::spawn(|| {
crate::command::spawn_relative_command(
"updater".to_string(),
Vec::new(),
std::process::Stdio::inherit(),
)
.expect("Failed to spawn updater thread");
});
}
// setup the content using the config struct depending on the compile target
let content = setup_content(config.clone())?;
let webview = web_view::builder()
.title(&config.tauri.window.title)
.size(config.tauri.window.width, config.tauri.window.height)
.resizable(config.tauri.window.resizable)
.debug(debug)
.user_data(())
.invoke_handler(|webview, arg| {
if arg == r#"{"cmd":"__initialized"}"# {
application.run_setup(webview);
webview.eval("
if (window.onTauriInit !== void 0) {
window.onTauriInit()
window.onTauriInit = void 0
}
Object.defineProperty(window, 'onTauriInit', {
set: function(val) {
if (typeof(val) === 'function') {
val()
}
}
})
").expect("failed to evaluate window.onTauriInit");
} else if !crate::endpoints::handle(webview, arg) {
application.run_invoke_handler(webview, arg);
}
// setup the server url for the embedded-server
#[cfg(feature = "embedded-server")]
let server_url = {
if let Content::Url(ref url) = &content {
String::from(url)
} else {
String::from("")
}
};
Ok(())
})
.content(content)
.build()
.expect("Failed to build webview builder");
// build the webview
let webview = build_webview(application, config, content)?;
// on dev-server grab a handler and execute the tauri.js API entry point.
#[cfg(feature = "dev-server")]
webview
.handle()
.dispatch(|_webview| _webview.eval(include_str!(concat!(env!("TAURI_DIR"), "/tauri.js"))))
.expect("Failed to grab webview handle");
.dispatch(|_webview| _webview.eval(include_str!(concat!(env!("TAURI_DIR"), "/tauri.js"))))?;
// spawn the embedded server on our server url
#[cfg(feature = "embedded-server")]
{
std::thread::spawn(move || {
let server = tiny_http::Server::http(
server_url
.clone()
.replace("http://", "")
.replace("https://", ""),
)
.expect(&format!(
"Could not start embedded server with the specified url: {}",
server_url
));
for request in server.incoming_requests() {
let url = match request.url() {
"/" => "/index.tauri.html",
url => url,
}
.to_string();
request
.respond(crate::server::asset_response(&url))
.expect("Failed to read asset type");
}
});
}
spawn_server(server_url.to_string())?;
webview.run().expect("Failed to run webview");
// spin up the updater process
#[cfg(feature = "updater")]
spawn_updater()?;
// run the webview
webview.run()?;
Ok(())
}
// setup content for dev-server
#[cfg(not(any(feature = "embedded-server", feature = "no-server")))]
fn setup_content(config: Config) -> TauriResult<Content<String>> {
if config.build.dev_path.starts_with("http") {
Ok(Content::Url(config.build.dev_path))
} else {
let dev_path = Path::new(env!("TAURI_DIST_DIR")).join("index.tauri.html");
Ok(Content::Html(read_to_string(dev_path)?))
}
}
// setup content for embedded server
#[cfg(feature = "embedded-server")]
fn setup_content(config: Config) -> TauriResult<Content<String>> {
let (port, valid) = setup_port(config.clone()).expect("Unable to setup Port");
let url = setup_server_url(config.clone(), valid, port).expect("Unable to setup URL");
Ok(Content::Url(url.to_string()))
}
// setup content for no-server
#[cfg(feature = "no-server")]
fn setup_content(_: Config) -> TauriResult<Content<String>> {
let index_path = Path::new(env!("TAURI_DIST_DIR")).join("index.tauri.html");
Ok(Content::Html(read_to_string(index_path)?))
}
// get the port for the embedded server
#[cfg(feature = "embedded-server")]
fn setup_port(config: Config) -> Option<(String, bool)> {
if config.tauri.embedded_server.port == "random" {
match get_available_port() {
Some(available_port) => Some((available_port.to_string(), true)),
None => Some(("0".to_string(), false)),
}
} else {
let port = config.tauri.embedded_server.port;
let port_valid = port_is_available(
port
.parse::<u16>()
.expect(&format!("Invalid port {}", port)),
);
Some((port, port_valid))
}
}
// setup the server url for embedded server
#[cfg(feature = "embedded-server")]
fn setup_server_url(config: Config, valid: bool, port: String) -> Option<String> {
if valid {
let mut url = format!("{}:{}", config.tauri.embedded_server.host, port);
if !url.starts_with("http") {
url = format!("http://{}", url);
}
Some(url)
} else {
None
}
}
// spawn the embedded server
#[cfg(feature = "embedded-server")]
fn spawn_server(server_url: String) -> TauriResult<()> {
spawn(move || {
let server = tiny_http::Server::http(
server_url
.clone()
.replace("http://", "")
.replace("https://", ""),
)
.expect("Unable to spawn server");
for request in server.incoming_requests() {
let url = match request.url() {
"/" => "/index.tauri.html",
url => url,
}
.to_string();
request
.respond(crate::server::asset_response(&url))
.expect("unable to setup response");
}
});
Ok(())
}
// spawn an updater process.
#[cfg(feature = "updater")]
fn spawn_updater() -> TauriResult<()> {
spawn(|| {
tauri_api::command::spawn_relative_command(
"updater".to_string(),
Vec::new(),
Stdio::inherit(),
)?;
});
Ok(())
}
// build the webview struct
fn build_webview(
application: &mut App,
config: Config,
content: Content<String>,
) -> TauriResult<WebView<'_, ()>> {
let debug = cfg!(debug_assertions);
// get properties from config struct
let width = config.tauri.window.width;
let height = config.tauri.window.height;
let resizable = config.tauri.window.resizable;
let title = config.tauri.window.title.into_boxed_str();
Ok(
builder()
.title(Box::leak(title))
.size(width, height)
.resizable(resizable)
.debug(debug)
.user_data(())
.invoke_handler(move |webview, arg| {
if arg == r#"{"cmd":"__initialized"}"# {
application.run_setup(webview);
webview.eval(JS_STRING)?;
} else if let Ok(b) = crate::endpoints::handle(webview, arg) {
if !b {
application.run_invoke_handler(webview, arg);
}
}
Ok(())
})
.content(content)
.build()?,
)
}

View File

@ -1,6 +1,8 @@
use std::env;
#[derive(Deserialize)]
use crate::TauriResult;
#[derive(Deserialize, Clone)]
#[serde(tag = "window", rename_all = "camelCase")]
pub struct WindowConfig {
#[serde(default = "default_width")]
@ -38,7 +40,7 @@ fn default_window() -> WindowConfig {
};
}
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
#[serde(tag = "embeddedServer", rename_all = "camelCase")]
pub struct EmbeddedServerConfig {
#[serde(default = "default_host")]
@ -62,53 +64,54 @@ fn default_embedded_server() -> EmbeddedServerConfig {
}
}
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
#[serde(tag = "tauri", rename_all = "camelCase")]
pub struct TauriConfig {
#[serde(default = "default_window")]
pub window: WindowConfig,
#[serde(default = "default_embedded_server")]
pub embedded_server: EmbeddedServerConfig
pub embedded_server: EmbeddedServerConfig,
}
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
#[serde(tag = "build", rename_all = "camelCase")]
pub struct BuildConfig {
#[serde(default = "default_dev_path")]
pub dev_path: String
pub dev_path: String,
}
fn default_dev_path() -> String {
"".to_string()
}
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Config {
#[serde(default = "default_tauri")]
pub tauri: TauriConfig,
#[serde(default = "default_build")]
pub build: BuildConfig
pub build: BuildConfig,
}
fn default_tauri() -> TauriConfig {
TauriConfig {
window: default_window(),
embedded_server: default_embedded_server()
embedded_server: default_embedded_server(),
}
}
fn default_build() -> BuildConfig {
BuildConfig {
dev_path: default_dev_path()
dev_path: default_dev_path(),
}
}
pub fn get() -> Config {
pub fn get() -> TauriResult<Config> {
match option_env!("TAURI_CONFIG") {
Some(config) => serde_json::from_str(config)
.expect("failed to parse TAURI_CONFIG env"),
None => serde_json::from_str(include_str!(concat!(env!("TAURI_DIR"), "/tauri.conf.json")))
.expect("failed to read tauri.conf.json")
Some(config) => Ok(serde_json::from_str(config).expect("failed to parse TAURI_CONFIG env")),
None => Ok(
serde_json::from_str(include_str!(concat!(env!("TAURI_DIR"), "/tauri.conf.json")))
.expect("failed to read tauri.conf.json"),
),
}
}

View File

@ -2,11 +2,13 @@ mod cmd;
use web_view::WebView;
use crate::TauriResult;
#[allow(unused_variables)]
pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> bool {
pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> TauriResult<bool> {
use cmd::Cmd::*;
match serde_json::from_str(arg) {
Err(_) => false,
Err(_) => Ok(false),
Ok(command) => {
match command {
Init {} => {
@ -44,13 +46,12 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> boo
listeners = crate::event::event_listeners_object_name(),
queue = crate::event::event_queue_object_name()
);
webview
.eval(&format!(
r#"{event_init}
webview.eval(&format!(
r#"{event_init}
window.external.invoke('{{"cmd":"__initialized"}}')
"#,
event_init = event_init
)).expect("Failed to call webview.eval from init");
event_init = event_init
))?;
}
#[cfg(any(feature = "all-api", feature = "readTextFile"))]
ReadTextFile {
@ -95,7 +96,7 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> boo
}
#[cfg(any(feature = "all-api", feature = "setTitle"))]
SetTitle { title } => {
webview.set_title(&title).expect("Failed to set title");
webview.set_title(&title)?;
}
#[cfg(any(feature = "all-api", feature = "execute"))]
Execute {
@ -211,7 +212,7 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> boo
);
}
}
true
Ok(true)
}
}
}

View File

@ -34,6 +34,9 @@ use web_view::*;
pub use tauri_api as api;
// Result alias
type TauriResult<T> = Result<T, Box<dyn std::error::Error>>;
thread_local!(static POOL: ThreadPool = ThreadPool::new(4));
pub fn spawn<F: FnOnce() -> () + Send + 'static>(task: F) {