From aa149370a400630b6af697f2ff116e992b64246b Mon Sep 17 00:00:00 2001 From: Tensor-Programming Date: Wed, 1 Jan 2020 05:30:17 -0500 Subject: [PATCH] =?UTF-8?q?Refactor(runner.rs)=20Clean=20up=20code=20to=20?= =?UTF-8?q?make=20it=20more=20composable=20and=E2=80=A6=20(#259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- tauri/src/app.rs | 2 +- tauri/src/app/runner.rs | 309 ++++++++++++++++++++++++---------------- tauri/src/config.rs | 33 +++-- tauri/src/endpoints.rs | 19 +-- tauri/src/lib.rs | 3 + 5 files changed, 217 insertions(+), 149 deletions(-) diff --git a/tauri/src/app.rs b/tauri/src/app.rs index af128d68f..a29b335f1 100644 --- a/tauri/src/app.rs +++ b/tauri/src/app.rs @@ -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) { diff --git a/tauri/src/app/runner.rs b/tauri/src/app/runner.rs index 6a3db9bfb..ee15152af 100644 --- a/tauri/src/app/runner.rs +++ b/tauri/src/app/runner.rs @@ -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::() - .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> { + 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> { + 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> { + 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::() + .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 { + 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, +) -> TauriResult> { + 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()?, + ) } diff --git a/tauri/src/config.rs b/tauri/src/config.rs index 980e245ef..e2d4e09b2 100644 --- a/tauri/src/config.rs +++ b/tauri/src/config.rs @@ -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 { 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"), + ), } } diff --git a/tauri/src/endpoints.rs b/tauri/src/endpoints.rs index c6748a84c..b0496498c 100644 --- a/tauri/src/endpoints.rs +++ b/tauri/src/endpoints.rs @@ -2,11 +2,13 @@ mod cmd; use web_view::WebView; +use crate::TauriResult; + #[allow(unused_variables)] -pub(crate) fn handle(webview: &mut WebView<'_, T>, arg: &str) -> bool { +pub(crate) fn handle(webview: &mut WebView<'_, T>, arg: &str) -> TauriResult { 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(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(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(webview: &mut WebView<'_, T>, arg: &str) -> boo ); } } - true + Ok(true) } } } diff --git a/tauri/src/lib.rs b/tauri/src/lib.rs index 542982da4..096835832 100644 --- a/tauri/src/lib.rs +++ b/tauri/src/lib.rs @@ -34,6 +34,9 @@ use web_view::*; pub use tauri_api as api; +// Result alias +type TauriResult = Result>; + thread_local!(static POOL: ThreadPool = ThreadPool::new(4)); pub fn spawn () + Send + 'static>(task: F) {