diff --git a/tauri-api/Cargo.toml b/tauri-api/Cargo.toml index 4c6de96af..6e23f1de1 100644 --- a/tauri-api/Cargo.toml +++ b/tauri-api/Cargo.toml @@ -33,6 +33,7 @@ tauri-utils = {version = "0.5", path = "../tauri-utils"} envmnt = "0.8.2" clap = { git = "https://github.com/clap-rs/clap", rev = "1a276f8", version = "3.0.0-beta.1", optional = true } notify-rust = { version = "4.0.0", optional = true } +once_cell = "1.4.0" [dev-dependencies] quickcheck = "0.9.2" diff --git a/tauri-api/src/cli.rs b/tauri-api/src/cli.rs index 6393c548f..aa20bc040 100644 --- a/tauri-api/src/cli.rs +++ b/tauri-api/src/cli.rs @@ -1,4 +1,4 @@ -use crate::config::{Cli, Config}; +use crate::config::{get as get_config, Cli}; use clap::{App, Arg, ArgMatches}; use serde::Serialize; @@ -36,16 +36,21 @@ impl Matches { } } -pub fn get_matches(config: Config) -> Matches { - let cli = config.tauri.cli.unwrap(); +pub fn get_matches() -> crate::Result { + let config = get_config()?; + let cli = config + .tauri + .cli + .as_ref() + .ok_or(anyhow::anyhow!("CLI configuration not defined"))?; let about = cli .description() .unwrap_or(&crate_description!().to_string()) .to_string(); - let app = get_app(crate_name!(), Some(&about), &cli); + let app = get_app(crate_name!(), Some(&about), cli); let matches = app.get_matches(); - get_matches_internal(&cli, &matches) + Ok(get_matches_internal(cli, &matches)) } fn get_matches_internal(config: &T, matches: &ArgMatches) -> Matches { diff --git a/tauri-api/src/config.rs b/tauri-api/src/config.rs index 85c03931f..cdfbcebeb 100644 --- a/tauri-api/src/config.rs +++ b/tauri-api/src/config.rs @@ -1,9 +1,12 @@ use serde::Deserialize; +use once_cell::sync::OnceCell; use std::collections::HashMap; use std::{fs, path}; -#[derive(PartialEq, Deserialize, Clone, Debug)] +static CONFIG: OnceCell = OnceCell::new(); + +#[derive(PartialEq, Deserialize, Debug)] #[serde(tag = "window", rename_all = "camelCase")] pub struct WindowConfig { #[serde(default = "default_width")] @@ -44,7 +47,7 @@ fn default_window() -> WindowConfig { } } -#[derive(PartialEq, Deserialize, Clone, Debug)] +#[derive(PartialEq, Deserialize, Debug)] #[serde(tag = "embeddedServer", rename_all = "camelCase")] pub struct EmbeddedServerConfig { #[serde(default = "default_host")] @@ -68,7 +71,7 @@ fn default_embedded_server() -> EmbeddedServerConfig { } } -#[derive(PartialEq, Deserialize, Clone, Debug, Default)] +#[derive(PartialEq, Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] pub struct CliArg { pub short: Option, @@ -95,7 +98,7 @@ pub struct CliArg { pub require_equals: Option, } -#[derive(PartialEq, Deserialize, Clone, Debug)] +#[derive(PartialEq, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct CliSubcommand { description: Option, @@ -106,7 +109,7 @@ pub struct CliSubcommand { subcommands: Option>, } -#[derive(PartialEq, Deserialize, Clone, Debug)] +#[derive(PartialEq, Deserialize, Debug)] #[serde(tag = "cli", rename_all = "camelCase")] pub struct CliConfig { description: Option, @@ -159,7 +162,7 @@ macro_rules! impl_cli { } } -#[derive(PartialEq, Deserialize, Clone, Debug)] +#[derive(PartialEq, Deserialize, Debug)] #[serde(tag = "bundle", rename_all = "camelCase")] pub struct BundleConfig { pub identifier: String, @@ -173,7 +176,7 @@ fn default_bundle() -> BundleConfig { impl_cli!(CliSubcommand, CliConfig); -#[derive(PartialEq, Deserialize, Clone, Debug)] +#[derive(PartialEq, Deserialize, Debug)] #[serde(tag = "tauri", rename_all = "camelCase")] pub struct TauriConfig { #[serde(default = "default_window")] @@ -186,7 +189,7 @@ pub struct TauriConfig { pub bundle: BundleConfig, } -#[derive(PartialEq, Deserialize, Clone, Debug)] +#[derive(PartialEq, Deserialize, Debug)] #[serde(tag = "build", rename_all = "camelCase")] pub struct BuildConfig { #[serde(default = "default_dev_path")] @@ -197,7 +200,7 @@ fn default_dev_path() -> String { "".to_string() } -#[derive(PartialEq, Deserialize, Clone, Debug)] +#[derive(PartialEq, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Config { #[serde(default = "default_tauri")] @@ -221,17 +224,27 @@ fn default_build() -> BuildConfig { } } -pub fn get() -> crate::Result { - match option_env!("TAURI_CONFIG") { - Some(config) => Ok(serde_json::from_str(config).expect("failed to parse TAURI_CONFIG env")), +pub fn get() -> crate::Result<&'static Config> { + if let Some(config) = CONFIG.get() { + return Ok(config); + } + let config: Config = match option_env!("TAURI_CONFIG") { + Some(config) => serde_json::from_str(config).expect("failed to parse TAURI_CONFIG env"), None => { let env_var = envmnt::get_or("TAURI_DIR", "../dist"); let path = path::Path::new(&env_var); let contents = fs::read_to_string(path.join("tauri.conf.json"))?; - Ok(serde_json::from_str(&contents).expect("failed to read tauri.conf.json")) + serde_json::from_str(&contents).expect("failed to read tauri.conf.json") } - } + }; + + CONFIG + .set(config) + .map_err(|_| anyhow::anyhow!("failed to set CONFIG"))?; + + let config = CONFIG.get().unwrap(); + Ok(config) } #[cfg(test)] @@ -328,7 +341,7 @@ mod test { // On Ok, check that the config is the same as the test config. Ok(c) => { println!("{:?}", c); - assert_eq!(c, test_config) + assert_eq!(c, &test_config) } Err(_) => assert!(false), } diff --git a/tauri-api/src/notification.rs b/tauri-api/src/notification.rs index 2200f8402..01df49184 100644 --- a/tauri-api/src/notification.rs +++ b/tauri-api/src/notification.rs @@ -1,4 +1,5 @@ -use crate::config::Config; +#[cfg(windows)] +use crate::config::get as get_config; #[cfg(windows)] use std::path::MAIN_SEPARATOR; @@ -7,16 +8,14 @@ pub struct Notification { body: Option, title: Option, icon: Option, - config: Config, } impl Notification { - pub fn new(config: Config) -> Self { + pub fn new() -> Self { Self { body: None, title: None, icon: None, - config, } } @@ -46,7 +45,7 @@ impl Notification { if let Some(icon) = self.icon { notification.icon(&icon); } - #[cfg(windowss)] + #[cfg(windows)] { let exe = std::env::current_exe()?; let exe_dir = exe.parent().expect("failed to get exe directory"); @@ -55,8 +54,9 @@ impl Notification { if !(curr_dir.ends_with(format!("{S}target{S}debug", S = MAIN_SEPARATOR).as_str()) || curr_dir.ends_with(format!("{S}target{S}release", S = MAIN_SEPARATOR).as_str())) { - let identifier = self.config.tauri.bundle.identifier.clone(); - notification.id(&identifier); + let config = get_config()?; + let identifier = config.tauri.bundle.identifier.clone(); + notification.app_id(&identifier); } } notification diff --git a/tauri/examples/communication/dist/index.tauri.html b/tauri/examples/communication/dist/index.tauri.html index ac210f47a..ba2c94f86 100644 --- a/tauri/examples/communication/dist/index.tauri.html +++ b/tauri/examples/communication/dist/index.tauri.html @@ -81,7 +81,7 @@ switch (navigator.platform) { var Dir = { Audio: 1, Cache: 2, - Config: 3, + Config: 3, Data: 4, LocalData: 5, Desktop: 6, diff --git a/tauri/examples/communication/src-tauri/tauri.js b/tauri/examples/communication/src-tauri/tauri.js index 705c7a0b6..7b6a5537c 100644 --- a/tauri/examples/communication/src-tauri/tauri.js +++ b/tauri/examples/communication/src-tauri/tauri.js @@ -81,7 +81,7 @@ switch (navigator.platform) { var Dir = { Audio: 1, Cache: 2, - Config: 3, + Config: 3, Data: 4, LocalData: 5, Desktop: 6, diff --git a/tauri/src/app/runner.rs b/tauri/src/app/runner.rs index 4e13ff667..d68588298 100644 --- a/tauri/src/app/runner.rs +++ b/tauri/src/app/runner.rs @@ -12,24 +12,12 @@ use web_view::{builder, Content, WebView}; use super::App; #[cfg(feature = "embedded-server")] use crate::api::tcp::{get_available_port, port_is_available}; -use tauri_api::config::{get, Config}; - -#[cfg(feature = "cli")] -use tauri_api::cli::get_matches; +use tauri_api::config::get; // Main entry point function for running the Webview pub(crate) fn run(application: &mut App) -> crate::Result<()> { - // get the tauri config struct - let config = get()?; - - #[cfg(feature = "cli")] - { - let matches = get_matches(config.clone()); - crate::cli::set_matches(matches)?; - } - // setup the content using the config struct depending on the compile target - let main_content = setup_content(config.clone())?; + let main_content = setup_content()?; // setup the server url for the embedded-server #[cfg(feature = "embedded-server")] @@ -44,7 +32,6 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> { // build the webview let webview = build_webview( application, - config, main_content, if application.splashscreen_html().is_some() { Some(Content::Html( @@ -74,12 +61,13 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> { // setup content for dev-server #[cfg(not(any(feature = "embedded-server", feature = "no-server")))] -fn setup_content(config: Config) -> crate::Result> { +fn setup_content() -> crate::Result> { + let config = get()?; if config.build.dev_path.starts_with("http") { - Ok(Content::Url(config.build.dev_path)) + Ok(Content::Url(config.build.dev_path.clone())) } else { - let dev_dir = config.build.dev_path; - let dev_path = Path::new(&dev_dir).join("index.tauri.html"); + let dev_dir = &config.build.dev_path; + let dev_path = Path::new(dev_dir).join("index.tauri.html"); if !dev_path.exists() { panic!( "Couldn't find 'index.tauri.html' inside {}; did you forget to run 'tauri dev'?", @@ -92,16 +80,21 @@ fn setup_content(config: Config) -> crate::Result> { // setup content for embedded server #[cfg(feature = "embedded-server")] -fn setup_content(config: Config) -> crate::Result> { - 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"); +fn setup_content() -> crate::Result> { + let (port, valid) = setup_port()?; + let url = (if valid { + setup_server_url(port) + } else { + Err(anyhow::anyhow!("invalid 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) -> crate::Result> { +fn setup_content() -> crate::Result> { let dist_dir = match option_env!("TAURI_DIST_DIR") { Some(d) => d.to_string(), None => env::current_dir()? @@ -116,35 +109,33 @@ fn setup_content(_: Config) -> crate::Result> { // get the port for the embedded server #[cfg(feature = "embedded-server")] -fn setup_port(config: Config) -> Option<(String, bool)> { +fn setup_port() -> crate::Result<(String, bool)> { + let config = get()?; 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)), + Some(available_port) => Ok((available_port.to_string(), true)), + None => Ok(("0".to_string(), false)), } } else { - let port = config.tauri.embedded_server.port; + let port = &config.tauri.embedded_server.port; let port_valid = port_is_available( port .parse::() .expect(&format!("Invalid port {}", port)), ); - Some((port, port_valid)) + Ok((port.to_string(), 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 +fn setup_server_url(port: String) -> crate::Result { + let config = get()?; + let mut url = format!("{}:{}", config.tauri.embedded_server.host, port); + if !url.starts_with("http") { + url = format!("http://{}", url); } + Ok(url) } // spawn the embedded server @@ -186,22 +177,21 @@ fn spawn_updater() -> crate::Result<()> { // build the webview struct fn build_webview( application: &mut App, - config: Config, content: Content, splashscreen_content: Option>, ) -> crate::Result> { + let config = get()?; let content_clone = match content { Content::Html(ref html) => Content::Html(html.clone()), Content::Url(ref url) => Content::Url(url.clone()), }; let debug = cfg!(debug_assertions); - let config_clone = config.clone(); // get properties from config struct let width = config.tauri.window.width; let height = config.tauri.window.height; let resizable = config.tauri.window.resizable; let fullscreen = config.tauri.window.fullscreen; - let title = config.tauri.window.title.into_boxed_str(); + let title = config.tauri.window.title.clone().into_boxed_str(); let has_splashscreen = splashscreen_content.is_some(); let mut initialized_splashscreen = false; @@ -229,9 +219,7 @@ fn build_webview( webview.eval(&format!(r#"window.location.href = "{}""#, content_href))?; } else { let handler_error; - if let Err(tauri_handle_error) = - crate::endpoints::handle(webview, arg, config_clone.clone()) - { + if let Err(tauri_handle_error) = crate::endpoints::handle(webview, arg) { let tauri_handle_error_str = tauri_handle_error.to_string(); if tauri_handle_error_str.contains("unknown variant") { let handled_by_app = application.run_invoke_handler(webview, arg); @@ -295,14 +283,13 @@ mod test { #[cfg(not(feature = "embedded-server"))] use std::{env, fs::read_to_string, path::Path}; - fn init_config() -> tauri_api::config::Config { + fn init_config() -> &'static tauri_api::config::Config { tauri_api::config::get().expect("unable to setup default config") } #[test] fn check_setup_content() { let config = init_config(); - let _c = config.clone(); let tauri_dir = match option_env!("TAURI_DIR") { Some(d) => d.to_string(), @@ -313,7 +300,7 @@ mod test { .expect("Unable to convert to normal String"), }; env::set_current_dir(tauri_dir).expect("failed to change cwd"); - let res = super::setup_content(config); + let res = super::setup_content(); #[cfg(feature = "embedded-server")] match res { @@ -342,10 +329,10 @@ mod test { #[cfg(not(any(feature = "embedded-server", feature = "no-server")))] match res { - Ok(Content::Url(dp)) => assert_eq!(dp, _c.build.dev_path), + Ok(Content::Url(dp)) => assert_eq!(dp, config.build.dev_path), Ok(Content::Html(s)) => { - let dev_dir = _c.build.dev_path; - let dev_path = Path::new(&dev_dir).join("index.tauri.html"); + let dev_dir = &config.build.dev_path; + let dev_path = Path::new(dev_dir).join("index.tauri.html"); assert_eq!( s, read_to_string(dev_path).expect("failed to read dev path") @@ -358,11 +345,9 @@ mod test { #[cfg(feature = "embedded-server")] #[test] fn check_setup_port() { - let config = init_config(); - - let res = super::setup_port(config); + let res = super::setup_port(); match res { - Some((_s, _b)) => assert!(true), + Ok((_s, _b)) => assert!(true), _ => assert!(false), } } @@ -372,16 +357,13 @@ mod test { #[cfg(feature = "embedded-server")] #[test] fn check_server_url(port in (any::().prop_map(|v| v.to_string()))) { - let config = init_config(); - let valid = true; - let p = port.clone(); - let res = super::setup_server_url(config, valid, port); + let res = super::setup_server_url(port); match res { - Some(url) => assert!(url.contains(&p)), - None => assert!(false) + Ok(url) => assert!(url.contains(&p)), + Err(_) => assert!(false) } } } diff --git a/tauri/src/cli.rs b/tauri/src/cli.rs index d81af4662..d29961bf7 100644 --- a/tauri/src/cli.rs +++ b/tauri/src/cli.rs @@ -1,14 +1,8 @@ -use once_cell::sync::OnceCell; +use once_cell::sync::Lazy; use tauri_api::cli::Matches; -static MATCHES: OnceCell = OnceCell::new(); +pub fn get_matches() -> &'static Option { + static MATCHES: Lazy> = Lazy::new(|| tauri_api::cli::get_matches().ok()); -pub(crate) fn set_matches(matches: Matches) -> crate::Result<()> { - MATCHES - .set(matches) - .map_err(|_| anyhow::anyhow!("failed to set once_cell matches")) -} - -pub fn get_matches() -> Option<&'static Matches> { - MATCHES.get() + &MATCHES } diff --git a/tauri/src/endpoints.rs b/tauri/src/endpoints.rs index 18e45aa2b..7a39b4878 100644 --- a/tauri/src/endpoints.rs +++ b/tauri/src/endpoints.rs @@ -6,15 +6,10 @@ mod salt; #[cfg(any(feature = "embedded-server", feature = "no-server"))] use std::path::PathBuf; -use tauri_api::config::Config; use web_view::WebView; #[allow(unused_variables)] -pub(crate) fn handle( - webview: &mut WebView<'_, T>, - arg: &str, - config: Config, -) -> crate::Result<()> { +pub(crate) fn handle(webview: &mut WebView<'_, T>, arg: &str) -> crate::Result<()> { use cmd::Cmd::*; match serde_json::from_str(arg) { Err(e) => Err(e.into()), @@ -199,7 +194,7 @@ pub(crate) fn handle( callback, error, } => { - notification(webview, options, callback, error, config)?; + notification(webview, options, callback, error)?; } #[cfg(any(feature = "all-api", feature = "notification"))] IsNotificationPermissionGranted { callback, error } => { @@ -413,12 +408,11 @@ fn notification( options: cmd::NotificationOptions, callback: String, error: String, - config: Config, ) -> crate::Result<()> { crate::execute_promise( webview, move || { - let mut notification = tauri_api::notification::Notification::new(config); + let mut notification = tauri_api::notification::Notification::new(); notification.body(options.body); if let Some(title) = options.title { notification.title(title);