config.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use anyhow::Context;
  5. use json_patch::merge;
  6. use log::error;
  7. use once_cell::sync::Lazy;
  8. use serde_json::Value as JsonValue;
  9. pub use tauri_utils::config::*;
  10. use std::{
  11. collections::HashMap,
  12. env::{set_var, var_os},
  13. ffi::OsStr,
  14. process::exit,
  15. sync::{Arc, Mutex},
  16. };
  17. pub const MERGE_CONFIG_EXTENSION_NAME: &str = "--config";
  18. pub struct ConfigMetadata {
  19. /// The actual configuration, merged with any extension.
  20. inner: Config,
  21. /// The config extensions (platform-specific config files or the config CLI argument).
  22. /// Maps the extension name to its value.
  23. extensions: HashMap<String, JsonValue>,
  24. }
  25. impl std::ops::Deref for ConfigMetadata {
  26. type Target = Config;
  27. #[inline(always)]
  28. fn deref(&self) -> &Config {
  29. &self.inner
  30. }
  31. }
  32. impl ConfigMetadata {
  33. /// Checks which config is overwriting the bundle identifier.
  34. pub fn find_bundle_identifier_overwriter(&self) -> Option<String> {
  35. for (ext, config) in &self.extensions {
  36. if let Some(identifier) = config
  37. .as_object()
  38. .and_then(|config| config.get("tauri"))
  39. .and_then(|tauri_config| tauri_config.as_object())
  40. .and_then(|tauri_config| tauri_config.get("bundle"))
  41. .and_then(|bundle_config| bundle_config.as_object())
  42. .and_then(|bundle_config| bundle_config.get("identifier"))
  43. .and_then(|id| id.as_str())
  44. {
  45. if identifier == self.inner.tauri.bundle.identifier {
  46. return Some(ext.clone());
  47. }
  48. }
  49. }
  50. None
  51. }
  52. }
  53. pub type ConfigHandle = Arc<Mutex<Option<ConfigMetadata>>>;
  54. pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings {
  55. tauri_bundler::WixSettings {
  56. language: tauri_bundler::WixLanguage(match config.language {
  57. WixLanguage::One(lang) => vec![(lang, Default::default())],
  58. WixLanguage::List(languages) => languages
  59. .into_iter()
  60. .map(|lang| (lang, Default::default()))
  61. .collect(),
  62. WixLanguage::Localized(languages) => languages
  63. .into_iter()
  64. .map(|(lang, config)| {
  65. (
  66. lang,
  67. tauri_bundler::WixLanguageConfig {
  68. locale_path: config.locale_path.map(Into::into),
  69. },
  70. )
  71. })
  72. .collect(),
  73. }),
  74. template: config.template,
  75. fragment_paths: config.fragment_paths,
  76. component_group_refs: config.component_group_refs,
  77. component_refs: config.component_refs,
  78. feature_group_refs: config.feature_group_refs,
  79. feature_refs: config.feature_refs,
  80. merge_refs: config.merge_refs,
  81. skip_webview_install: config.skip_webview_install,
  82. license: config.license,
  83. enable_elevated_update_task: config.enable_elevated_update_task,
  84. banner_path: config.banner_path,
  85. dialog_image_path: config.dialog_image_path,
  86. fips_compliant: var_os("TAURI_FIPS_COMPLIANT").map_or(false, |v| v == "true"),
  87. }
  88. }
  89. pub fn nsis_settings(config: NsisConfig) -> tauri_bundler::NsisSettings {
  90. tauri_bundler::NsisSettings {
  91. template: config.template,
  92. license: config.license,
  93. header_image: config.header_image,
  94. sidebar_image: config.sidebar_image,
  95. installer_icon: config.installer_icon,
  96. install_mode: config.install_mode,
  97. languages: config.languages,
  98. custom_language_files: config.custom_language_files,
  99. display_language_selector: config.display_language_selector,
  100. compression: config.compression,
  101. }
  102. }
  103. fn config_handle() -> &'static ConfigHandle {
  104. static CONFING_HANDLE: Lazy<ConfigHandle> = Lazy::new(Default::default);
  105. &CONFING_HANDLE
  106. }
  107. /// Gets the static parsed config from `tauri.conf.json`.
  108. fn get_internal(merge_config: Option<&str>, reload: bool) -> crate::Result<ConfigHandle> {
  109. if !reload && config_handle().lock().unwrap().is_some() {
  110. return Ok(config_handle().clone());
  111. }
  112. let tauri_dir = super::app_paths::tauri_dir();
  113. let (mut config, config_path) =
  114. tauri_utils::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))?;
  115. let config_file_name = config_path.file_name().unwrap().to_string_lossy();
  116. let mut extensions = HashMap::new();
  117. if let Some((platform_config, config_path)) =
  118. tauri_utils::config::parse::read_platform(tauri_dir)?
  119. {
  120. merge(&mut config, &platform_config);
  121. extensions.insert(
  122. config_path.file_name().unwrap().to_str().unwrap().into(),
  123. platform_config,
  124. );
  125. }
  126. if let Some(merge_config) = merge_config {
  127. set_var("TAURI_CONFIG", merge_config);
  128. let merge_config: JsonValue =
  129. serde_json::from_str(merge_config).with_context(|| "failed to parse config to merge")?;
  130. merge(&mut config, &merge_config);
  131. extensions.insert(MERGE_CONFIG_EXTENSION_NAME.into(), merge_config);
  132. };
  133. if config_path.extension() == Some(OsStr::new("json"))
  134. || config_path.extension() == Some(OsStr::new("json5"))
  135. {
  136. let schema: JsonValue = serde_json::from_str(include_str!("../../schema.json"))?;
  137. let schema = jsonschema::JSONSchema::compile(&schema).unwrap();
  138. let result = schema.validate(&config);
  139. if let Err(errors) = result {
  140. for error in errors {
  141. let path = error.instance_path.clone().into_vec().join(" > ");
  142. if path.is_empty() {
  143. error!("`{}` error: {}", config_file_name, error);
  144. } else {
  145. error!("`{}` error on `{}`: {}", config_file_name, path, error);
  146. }
  147. }
  148. if !reload {
  149. exit(1);
  150. }
  151. }
  152. }
  153. let config: Config = serde_json::from_value(config)?;
  154. *config_handle().lock().unwrap() = Some(ConfigMetadata {
  155. inner: config,
  156. extensions,
  157. });
  158. Ok(config_handle().clone())
  159. }
  160. pub fn get(merge_config: Option<&str>) -> crate::Result<ConfigHandle> {
  161. get_internal(merge_config, false)
  162. }
  163. pub fn reload(merge_config: Option<&str>) -> crate::Result<ConfigHandle> {
  164. get_internal(merge_config, true)
  165. }