tauri_utils/
config.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! The Tauri configuration used at runtime.
6//!
7//! It is pulled from a `tauri.conf.json` file and the [`Config`] struct is generated at compile time.
8//!
9//! # Stability
10//!
11//! This is a core functionality that is not considered part of the stable API.
12//! If you use it, note that it may include breaking changes in the future.
13//!
14//! These items are intended to be non-breaking from a de/serialization standpoint only.
15//! Using and modifying existing config values will try to avoid breaking changes, but they are
16//! free to add fields in the future - causing breaking changes for creating and full destructuring.
17//!
18//! To avoid this, [ignore unknown fields when destructuring] with the `{my, config, ..}` pattern.
19//! If you need to create the Rust config directly without deserializing, then create the struct
20//! the [Struct Update Syntax] with `..Default::default()`, which may need a
21//! `#[allow(clippy::needless_update)]` attribute if you are declaring all fields.
22//!
23//! [ignore unknown fields when destructuring]: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#ignoring-remaining-parts-of-a-value-with-
24//! [Struct Update Syntax]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax
25
26use http::response::Builder;
27#[cfg(feature = "schema")]
28use schemars::JsonSchema;
29use semver::Version;
30use serde::{
31  de::{Deserializer, Error as DeError, Visitor},
32  Deserialize, Serialize, Serializer,
33};
34use serde_json::Value as JsonValue;
35use serde_untagged::UntaggedEnumVisitor;
36use serde_with::skip_serializing_none;
37use url::Url;
38
39use std::{
40  collections::HashMap,
41  fmt::{self, Display},
42  fs::read_to_string,
43  path::PathBuf,
44  str::FromStr,
45};
46
47/// Items to help with parsing content into a [`Config`].
48pub mod parse;
49
50use crate::{acl::capability::Capability, TitleBarStyle, WindowEffect, WindowEffectState};
51
52pub use self::parse::parse;
53
54fn default_true() -> bool {
55  true
56}
57
58/// An URL to open on a Tauri webview window.
59#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
60#[cfg_attr(feature = "schema", derive(JsonSchema))]
61#[serde(untagged)]
62#[non_exhaustive]
63pub enum WebviewUrl {
64  /// An external URL. Must use either the `http` or `https` schemes.
65  External(Url),
66  /// The path portion of an app URL.
67  /// For instance, to load `tauri://localhost/users/john`,
68  /// you can simply provide `users/john` in this configuration.
69  App(PathBuf),
70  /// A custom protocol url, for example, `doom://index.html`
71  CustomProtocol(Url),
72}
73
74impl<'de> Deserialize<'de> for WebviewUrl {
75  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
76  where
77    D: Deserializer<'de>,
78  {
79    #[derive(Deserialize)]
80    #[serde(untagged)]
81    enum WebviewUrlDeserializer {
82      Url(Url),
83      Path(PathBuf),
84    }
85
86    match WebviewUrlDeserializer::deserialize(deserializer)? {
87      WebviewUrlDeserializer::Url(u) => {
88        if u.scheme() == "https" || u.scheme() == "http" {
89          Ok(Self::External(u))
90        } else {
91          Ok(Self::CustomProtocol(u))
92        }
93      }
94      WebviewUrlDeserializer::Path(p) => Ok(Self::App(p)),
95    }
96  }
97}
98
99impl fmt::Display for WebviewUrl {
100  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101    match self {
102      Self::External(url) | Self::CustomProtocol(url) => write!(f, "{url}"),
103      Self::App(path) => write!(f, "{}", path.display()),
104    }
105  }
106}
107
108impl Default for WebviewUrl {
109  fn default() -> Self {
110    Self::App("index.html".into())
111  }
112}
113
114/// A bundle referenced by tauri-bundler.
115#[derive(Debug, PartialEq, Eq, Clone)]
116#[cfg_attr(feature = "schema", derive(JsonSchema))]
117#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))]
118pub enum BundleType {
119  /// The debian bundle (.deb).
120  Deb,
121  /// The RPM bundle (.rpm).
122  Rpm,
123  /// The AppImage bundle (.appimage).
124  AppImage,
125  /// The Microsoft Installer bundle (.msi).
126  Msi,
127  /// The NSIS bundle (.exe).
128  Nsis,
129  /// The macOS application bundle (.app).
130  App,
131  /// The Apple Disk Image bundle (.dmg).
132  Dmg,
133}
134
135impl BundleType {
136  /// All bundle types.
137  fn all() -> &'static [Self] {
138    &[
139      BundleType::Deb,
140      BundleType::Rpm,
141      BundleType::AppImage,
142      BundleType::Msi,
143      BundleType::Nsis,
144      BundleType::App,
145      BundleType::Dmg,
146    ]
147  }
148}
149
150impl Display for BundleType {
151  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152    write!(
153      f,
154      "{}",
155      match self {
156        Self::Deb => "deb",
157        Self::Rpm => "rpm",
158        Self::AppImage => "appimage",
159        Self::Msi => "msi",
160        Self::Nsis => "nsis",
161        Self::App => "app",
162        Self::Dmg => "dmg",
163      }
164    )
165  }
166}
167
168impl Serialize for BundleType {
169  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
170  where
171    S: Serializer,
172  {
173    serializer.serialize_str(self.to_string().as_ref())
174  }
175}
176
177impl<'de> Deserialize<'de> for BundleType {
178  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
179  where
180    D: Deserializer<'de>,
181  {
182    let s = String::deserialize(deserializer)?;
183    match s.to_lowercase().as_str() {
184      "deb" => Ok(Self::Deb),
185      "rpm" => Ok(Self::Rpm),
186      "appimage" => Ok(Self::AppImage),
187      "msi" => Ok(Self::Msi),
188      "nsis" => Ok(Self::Nsis),
189      "app" => Ok(Self::App),
190      "dmg" => Ok(Self::Dmg),
191      _ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
192    }
193  }
194}
195
196/// Targets to bundle. Each value is case insensitive.
197#[derive(Debug, PartialEq, Eq, Clone)]
198pub enum BundleTarget {
199  /// Bundle all targets.
200  All,
201  /// A list of bundle targets.
202  List(Vec<BundleType>),
203  /// A single bundle target.
204  One(BundleType),
205}
206
207#[cfg(feature = "schema")]
208impl schemars::JsonSchema for BundleTarget {
209  fn schema_name() -> std::string::String {
210    "BundleTarget".to_owned()
211  }
212
213  fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
214    let any_of = vec![
215      schemars::schema::SchemaObject {
216        const_value: Some("all".into()),
217        metadata: Some(Box::new(schemars::schema::Metadata {
218          description: Some("Bundle all targets.".to_owned()),
219          ..Default::default()
220        })),
221        ..Default::default()
222      }
223      .into(),
224      schemars::_private::metadata::add_description(
225        gen.subschema_for::<Vec<BundleType>>(),
226        "A list of bundle targets.",
227      ),
228      schemars::_private::metadata::add_description(
229        gen.subschema_for::<BundleType>(),
230        "A single bundle target.",
231      ),
232    ];
233
234    schemars::schema::SchemaObject {
235      subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
236        any_of: Some(any_of),
237        ..Default::default()
238      })),
239      metadata: Some(Box::new(schemars::schema::Metadata {
240        description: Some("Targets to bundle. Each value is case insensitive.".to_owned()),
241        ..Default::default()
242      })),
243      ..Default::default()
244    }
245    .into()
246  }
247}
248
249impl Default for BundleTarget {
250  fn default() -> Self {
251    Self::All
252  }
253}
254
255impl Serialize for BundleTarget {
256  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
257  where
258    S: Serializer,
259  {
260    match self {
261      Self::All => serializer.serialize_str("all"),
262      Self::List(l) => l.serialize(serializer),
263      Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
264    }
265  }
266}
267
268impl<'de> Deserialize<'de> for BundleTarget {
269  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
270  where
271    D: Deserializer<'de>,
272  {
273    #[derive(Deserialize, Serialize)]
274    #[serde(untagged)]
275    pub enum BundleTargetInner {
276      List(Vec<BundleType>),
277      One(BundleType),
278      All(String),
279    }
280
281    match BundleTargetInner::deserialize(deserializer)? {
282      BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
283      BundleTargetInner::All(t) => Err(DeError::custom(format!(
284        "invalid bundle type {t}, expected one of `all`, {}",
285        BundleType::all()
286          .iter()
287          .map(|b| format!("`{b}`"))
288          .collect::<Vec<_>>()
289          .join(", ")
290      ))),
291      BundleTargetInner::List(l) => Ok(Self::List(l)),
292      BundleTargetInner::One(t) => Ok(Self::One(t)),
293    }
294  }
295}
296
297impl BundleTarget {
298  /// Gets the bundle targets as a [`Vec`]. The vector is empty when set to [`BundleTarget::All`].
299  #[allow(dead_code)]
300  pub fn to_vec(&self) -> Vec<BundleType> {
301    match self {
302      Self::All => BundleType::all().to_vec(),
303      Self::List(list) => list.clone(),
304      Self::One(i) => vec![i.clone()],
305    }
306  }
307}
308
309/// Configuration for AppImage bundles.
310///
311/// See more: <https://v2.tauri.app/reference/config/#appimageconfig>
312#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
313#[cfg_attr(feature = "schema", derive(JsonSchema))]
314#[serde(rename_all = "camelCase", deny_unknown_fields)]
315pub struct AppImageConfig {
316  /// Include additional gstreamer dependencies needed for audio and video playback.
317  /// This increases the bundle size by ~15-35MB depending on your build system.
318  #[serde(default, alias = "bundle-media-framework")]
319  pub bundle_media_framework: bool,
320  /// The files to include in the Appimage Binary.
321  #[serde(default)]
322  pub files: HashMap<PathBuf, PathBuf>,
323}
324
325/// Configuration for Debian (.deb) bundles.
326///
327/// See more: <https://v2.tauri.app/reference/config/#debconfig>
328#[skip_serializing_none]
329#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
330#[cfg_attr(feature = "schema", derive(JsonSchema))]
331#[serde(rename_all = "camelCase", deny_unknown_fields)]
332pub struct DebConfig {
333  /// The list of deb dependencies your application relies on.
334  pub depends: Option<Vec<String>>,
335  /// The list of deb dependencies your application recommends.
336  pub recommends: Option<Vec<String>>,
337  /// The list of dependencies the package provides.
338  pub provides: Option<Vec<String>>,
339  /// The list of package conflicts.
340  pub conflicts: Option<Vec<String>>,
341  /// The list of package replaces.
342  pub replaces: Option<Vec<String>>,
343  /// The files to include on the package.
344  #[serde(default)]
345  pub files: HashMap<PathBuf, PathBuf>,
346  /// Define the section in Debian Control file. See : https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections
347  pub section: Option<String>,
348  /// Change the priority of the Debian Package. By default, it is set to `optional`.
349  /// Recognized Priorities as of now are :  `required`, `important`, `standard`, `optional`, `extra`
350  pub priority: Option<String>,
351  /// Path of the uncompressed Changelog file, to be stored at /usr/share/doc/package-name/changelog.gz. See
352  /// <https://www.debian.org/doc/debian-policy/ch-docs.html#changelog-files-and-release-notes>
353  pub changelog: Option<PathBuf>,
354  /// Path to a custom desktop file Handlebars template.
355  ///
356  /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.
357  #[serde(alias = "desktop-template")]
358  pub desktop_template: Option<PathBuf>,
359  /// Path to script that will be executed before the package is unpacked. See
360  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
361  #[serde(alias = "pre-install-script")]
362  pub pre_install_script: Option<PathBuf>,
363  /// Path to script that will be executed after the package is unpacked. See
364  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
365  #[serde(alias = "post-install-script")]
366  pub post_install_script: Option<PathBuf>,
367  /// Path to script that will be executed before the package is removed. See
368  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
369  #[serde(alias = "pre-remove-script")]
370  pub pre_remove_script: Option<PathBuf>,
371  /// Path to script that will be executed after the package is removed. See
372  /// <https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html>
373  #[serde(alias = "post-remove-script")]
374  pub post_remove_script: Option<PathBuf>,
375}
376
377/// Configuration for Linux bundles.
378///
379/// See more: <https://v2.tauri.app/reference/config/#linuxconfig>
380#[skip_serializing_none]
381#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
382#[cfg_attr(feature = "schema", derive(JsonSchema))]
383#[serde(rename_all = "camelCase", deny_unknown_fields)]
384pub struct LinuxConfig {
385  /// Configuration for the AppImage bundle.
386  #[serde(default)]
387  pub appimage: AppImageConfig,
388  /// Configuration for the Debian bundle.
389  #[serde(default)]
390  pub deb: DebConfig,
391  /// Configuration for the RPM bundle.
392  #[serde(default)]
393  pub rpm: RpmConfig,
394}
395
396/// Compression algorithms used when bundling RPM packages.
397#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
398#[cfg_attr(feature = "schema", derive(JsonSchema))]
399#[serde(rename_all = "camelCase", deny_unknown_fields, tag = "type")]
400#[non_exhaustive]
401pub enum RpmCompression {
402  /// Gzip compression
403  Gzip {
404    /// Gzip compression level
405    level: u32,
406  },
407  /// Zstd compression
408  Zstd {
409    /// Zstd compression level
410    level: i32,
411  },
412  /// Xz compression
413  Xz {
414    /// Xz compression level
415    level: u32,
416  },
417  /// Bzip2 compression
418  Bzip2 {
419    /// Bzip2 compression level
420    level: u32,
421  },
422  /// Disable compression
423  None,
424}
425
426/// Configuration for RPM bundles.
427#[skip_serializing_none]
428#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
429#[cfg_attr(feature = "schema", derive(JsonSchema))]
430#[serde(rename_all = "camelCase", deny_unknown_fields)]
431pub struct RpmConfig {
432  /// The list of RPM dependencies your application relies on.
433  pub depends: Option<Vec<String>>,
434  /// The list of RPM dependencies your application recommends.
435  pub recommends: Option<Vec<String>>,
436  /// The list of RPM dependencies your application provides.
437  pub provides: Option<Vec<String>>,
438  /// The list of RPM dependencies your application conflicts with. They must not be present
439  /// in order for the package to be installed.
440  pub conflicts: Option<Vec<String>>,
441  /// The list of RPM dependencies your application supersedes - if this package is installed,
442  /// packages listed as "obsoletes" will be automatically removed (if they are present).
443  pub obsoletes: Option<Vec<String>>,
444  /// The RPM release tag.
445  #[serde(default = "default_release")]
446  pub release: String,
447  /// The RPM epoch.
448  #[serde(default)]
449  pub epoch: u32,
450  /// The files to include on the package.
451  #[serde(default)]
452  pub files: HashMap<PathBuf, PathBuf>,
453  /// Path to a custom desktop file Handlebars template.
454  ///
455  /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.
456  #[serde(alias = "desktop-template")]
457  pub desktop_template: Option<PathBuf>,
458  /// Path to script that will be executed before the package is unpacked. See
459  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
460  #[serde(alias = "pre-install-script")]
461  pub pre_install_script: Option<PathBuf>,
462  /// Path to script that will be executed after the package is unpacked. See
463  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
464  #[serde(alias = "post-install-script")]
465  pub post_install_script: Option<PathBuf>,
466  /// Path to script that will be executed before the package is removed. See
467  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
468  #[serde(alias = "pre-remove-script")]
469  pub pre_remove_script: Option<PathBuf>,
470  /// Path to script that will be executed after the package is removed. See
471  /// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
472  #[serde(alias = "post-remove-script")]
473  pub post_remove_script: Option<PathBuf>,
474  /// Compression algorithm and level. Defaults to `Gzip` with level 6.
475  pub compression: Option<RpmCompression>,
476}
477
478impl Default for RpmConfig {
479  fn default() -> Self {
480    Self {
481      depends: None,
482      recommends: None,
483      provides: None,
484      conflicts: None,
485      obsoletes: None,
486      release: default_release(),
487      epoch: 0,
488      files: Default::default(),
489      desktop_template: None,
490      pre_install_script: None,
491      post_install_script: None,
492      pre_remove_script: None,
493      post_remove_script: None,
494      compression: None,
495    }
496  }
497}
498
499fn default_release() -> String {
500  "1".into()
501}
502
503/// Position coordinates struct.
504#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
505#[cfg_attr(feature = "schema", derive(JsonSchema))]
506#[serde(rename_all = "camelCase", deny_unknown_fields)]
507pub struct Position {
508  /// X coordinate.
509  pub x: u32,
510  /// Y coordinate.
511  pub y: u32,
512}
513
514/// Size of the window.
515#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
516#[cfg_attr(feature = "schema", derive(JsonSchema))]
517#[serde(rename_all = "camelCase", deny_unknown_fields)]
518pub struct Size {
519  /// Width of the window.
520  pub width: u32,
521  /// Height of the window.
522  pub height: u32,
523}
524
525/// Configuration for Apple Disk Image (.dmg) bundles.
526///
527/// See more: <https://v2.tauri.app/reference/config/#dmgconfig>
528#[skip_serializing_none]
529#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
530#[cfg_attr(feature = "schema", derive(JsonSchema))]
531#[serde(rename_all = "camelCase", deny_unknown_fields)]
532pub struct DmgConfig {
533  /// Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`.
534  pub background: Option<PathBuf>,
535  /// Position of volume window on screen.
536  pub window_position: Option<Position>,
537  /// Size of volume window.
538  #[serde(default = "dmg_window_size", alias = "window-size")]
539  pub window_size: Size,
540  /// Position of app file on window.
541  #[serde(default = "dmg_app_position", alias = "app-position")]
542  pub app_position: Position,
543  /// Position of application folder on window.
544  #[serde(
545    default = "dmg_application_folder_position",
546    alias = "application-folder-position"
547  )]
548  pub application_folder_position: Position,
549}
550
551impl Default for DmgConfig {
552  fn default() -> Self {
553    Self {
554      background: None,
555      window_position: None,
556      window_size: dmg_window_size(),
557      app_position: dmg_app_position(),
558      application_folder_position: dmg_application_folder_position(),
559    }
560  }
561}
562
563fn dmg_window_size() -> Size {
564  Size {
565    width: 660,
566    height: 400,
567  }
568}
569
570fn dmg_app_position() -> Position {
571  Position { x: 180, y: 170 }
572}
573
574fn dmg_application_folder_position() -> Position {
575  Position { x: 480, y: 170 }
576}
577
578fn de_macos_minimum_system_version<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
579where
580  D: Deserializer<'de>,
581{
582  let version = Option::<String>::deserialize(deserializer)?;
583  match version {
584    Some(v) if v.is_empty() => Ok(macos_minimum_system_version()),
585    e => Ok(e),
586  }
587}
588
589/// Configuration for the macOS bundles.
590///
591/// See more: <https://v2.tauri.app/reference/config/#macconfig>
592#[skip_serializing_none]
593#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
594#[cfg_attr(feature = "schema", derive(JsonSchema))]
595#[serde(rename_all = "camelCase", deny_unknown_fields)]
596pub struct MacConfig {
597  /// A list of strings indicating any macOS X frameworks that need to be bundled with the application.
598  ///
599  /// If a name is used, ".framework" must be omitted and it will look for standard install locations. You may also use a path to a specific framework.
600  pub frameworks: Option<Vec<String>>,
601  /// The files to include in the application relative to the Contents directory.
602  #[serde(default)]
603  pub files: HashMap<PathBuf, PathBuf>,
604  /// A version string indicating the minimum macOS X version that the bundled application supports. Defaults to `10.13`.
605  ///
606  /// Setting it to `null` completely removes the `LSMinimumSystemVersion` field on the bundle's `Info.plist`
607  /// and the `MACOSX_DEPLOYMENT_TARGET` environment variable.
608  ///
609  /// An empty string is considered an invalid value so the default value is used.
610  #[serde(
611    deserialize_with = "de_macos_minimum_system_version",
612    default = "macos_minimum_system_version",
613    alias = "minimum-system-version"
614  )]
615  pub minimum_system_version: Option<String>,
616  /// Allows your application to communicate with the outside world.
617  /// It should be a lowercase, without port and protocol domain name.
618  #[serde(alias = "exception-domain")]
619  pub exception_domain: Option<String>,
620  /// Identity to use for code signing.
621  #[serde(alias = "signing-identity")]
622  pub signing_identity: Option<String>,
623  /// Whether the codesign should enable [hardened runtime] (for executables) or not.
624  ///
625  /// [hardened runtime]: <https://developer.apple.com/documentation/security/hardened_runtime>
626  #[serde(alias = "hardened-runtime", default = "default_true")]
627  pub hardened_runtime: bool,
628  /// Provider short name for notarization.
629  #[serde(alias = "provider-short-name")]
630  pub provider_short_name: Option<String>,
631  /// Path to the entitlements file.
632  pub entitlements: Option<String>,
633  /// DMG-specific settings.
634  #[serde(default)]
635  pub dmg: DmgConfig,
636}
637
638impl Default for MacConfig {
639  fn default() -> Self {
640    Self {
641      frameworks: None,
642      files: HashMap::new(),
643      minimum_system_version: macos_minimum_system_version(),
644      exception_domain: None,
645      signing_identity: None,
646      hardened_runtime: true,
647      provider_short_name: None,
648      entitlements: None,
649      dmg: Default::default(),
650    }
651  }
652}
653
654fn macos_minimum_system_version() -> Option<String> {
655  Some("10.13".into())
656}
657
658fn ios_minimum_system_version() -> String {
659  "13.0".into()
660}
661
662/// Configuration for a target language for the WiX build.
663///
664/// See more: <https://v2.tauri.app/reference/config/#wixlanguageconfig>
665#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
666#[cfg_attr(feature = "schema", derive(JsonSchema))]
667#[serde(rename_all = "camelCase", deny_unknown_fields)]
668pub struct WixLanguageConfig {
669  /// The path to a locale (`.wxl`) file. See <https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html>.
670  #[serde(alias = "locale-path")]
671  pub locale_path: Option<String>,
672}
673
674/// The languages to build using WiX.
675#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
676#[cfg_attr(feature = "schema", derive(JsonSchema))]
677#[serde(untagged)]
678pub enum WixLanguage {
679  /// A single language to build, without configuration.
680  One(String),
681  /// A list of languages to build, without configuration.
682  List(Vec<String>),
683  /// A map of languages and its configuration.
684  Localized(HashMap<String, WixLanguageConfig>),
685}
686
687impl Default for WixLanguage {
688  fn default() -> Self {
689    Self::One("en-US".into())
690  }
691}
692
693/// Configuration for the MSI bundle using WiX.
694///
695/// See more: <https://v2.tauri.app/reference/config/#wixconfig>
696#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
697#[cfg_attr(feature = "schema", derive(JsonSchema))]
698#[serde(rename_all = "camelCase", deny_unknown_fields)]
699pub struct WixConfig {
700  /// MSI installer version in the format `major.minor.patch.build` (build is optional).
701  ///
702  /// Because a valid version is required for MSI installer, it will be derived from [`Config::version`] if this field is not set.
703  ///
704  /// The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255.
705  /// The third and foruth fields have a maximum value of 65,535.
706  ///
707  /// See <https://learn.microsoft.com/en-us/windows/win32/msi/productversion> for more info.
708  pub version: Option<String>,
709  /// A GUID upgrade code for MSI installer. This code **_must stay the same across all of your updates_**,
710  /// otherwise, Windows will treat your update as a different app and your users will have duplicate versions of your app.
711  ///
712  /// By default, tauri generates this code by generating a Uuid v5 using the string `<productName>.exe.app.x64` in the DNS namespace.
713  /// You can use Tauri's CLI to generate and print this code for you, run `tauri inspect wix-upgrade-code`.
714  ///
715  /// It is recommended that you set this value in your tauri config file to avoid accidental changes in your upgrade code
716  /// whenever you want to change your product name.
717  #[serde(alias = "upgrade-code")]
718  pub upgrade_code: Option<uuid::Uuid>,
719  /// The installer languages to build. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.
720  #[serde(default)]
721  pub language: WixLanguage,
722  /// A custom .wxs template to use.
723  pub template: Option<PathBuf>,
724  /// A list of paths to .wxs files with WiX fragments to use.
725  #[serde(default, alias = "fragment-paths")]
726  pub fragment_paths: Vec<PathBuf>,
727  /// The ComponentGroup element ids you want to reference from the fragments.
728  #[serde(default, alias = "component-group-refs")]
729  pub component_group_refs: Vec<String>,
730  /// The Component element ids you want to reference from the fragments.
731  #[serde(default, alias = "component-refs")]
732  pub component_refs: Vec<String>,
733  /// The FeatureGroup element ids you want to reference from the fragments.
734  #[serde(default, alias = "feature-group-refs")]
735  pub feature_group_refs: Vec<String>,
736  /// The Feature element ids you want to reference from the fragments.
737  #[serde(default, alias = "feature-refs")]
738  pub feature_refs: Vec<String>,
739  /// The Merge element ids you want to reference from the fragments.
740  #[serde(default, alias = "merge-refs")]
741  pub merge_refs: Vec<String>,
742  /// Create an elevated update task within Windows Task Scheduler.
743  #[serde(default, alias = "enable-elevated-update-task")]
744  pub enable_elevated_update_task: bool,
745  /// Path to a bitmap file to use as the installation user interface banner.
746  /// This bitmap will appear at the top of all but the first page of the installer.
747  ///
748  /// The required dimensions are 493px × 58px.
749  #[serde(alias = "banner-path")]
750  pub banner_path: Option<PathBuf>,
751  /// Path to a bitmap file to use on the installation user interface dialogs.
752  /// It is used on the welcome and completion dialogs.
753  ///
754  /// The required dimensions are 493px × 312px.
755  #[serde(alias = "dialog-image-path")]
756  pub dialog_image_path: Option<PathBuf>,
757}
758
759/// Compression algorithms used in the NSIS installer.
760///
761/// See <https://nsis.sourceforge.io/Reference/SetCompressor>
762#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
763#[cfg_attr(feature = "schema", derive(JsonSchema))]
764#[serde(rename_all = "camelCase", deny_unknown_fields)]
765pub enum NsisCompression {
766  /// ZLIB uses the deflate algorithm, it is a quick and simple method. With the default compression level it uses about 300 KB of memory.
767  Zlib,
768  /// BZIP2 usually gives better compression ratios than ZLIB, but it is a bit slower and uses more memory. With the default compression level it uses about 4 MB of memory.
769  Bzip2,
770  /// LZMA (default) is a new compression method that gives very good compression ratios. The decompression speed is high (10-20 MB/s on a 2 GHz CPU), the compression speed is lower. The memory size that will be used for decompression is the dictionary size plus a few KBs, the default is 8 MB.
771  Lzma,
772  /// Disable compression
773  None,
774}
775
776impl Default for NsisCompression {
777  fn default() -> Self {
778    Self::Lzma
779  }
780}
781
782/// Install Modes for the NSIS installer.
783#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
784#[serde(rename_all = "camelCase", deny_unknown_fields)]
785#[cfg_attr(feature = "schema", derive(JsonSchema))]
786pub enum NSISInstallerMode {
787  /// Default mode for the installer.
788  ///
789  /// Install the app by default in a directory that doesn't require Administrator access.
790  ///
791  /// Installer metadata will be saved under the `HKCU` registry path.
792  CurrentUser,
793  /// Install the app by default in the `Program Files` folder directory requires Administrator
794  /// access for the installation.
795  ///
796  /// Installer metadata will be saved under the `HKLM` registry path.
797  PerMachine,
798  /// Combines both modes and allows the user to choose at install time
799  /// whether to install for the current user or per machine. Note that this mode
800  /// will require Administrator access even if the user wants to install it for the current user only.
801  ///
802  /// Installer metadata will be saved under the `HKLM` or `HKCU` registry path based on the user's choice.
803  Both,
804}
805
806impl Default for NSISInstallerMode {
807  fn default() -> Self {
808    Self::CurrentUser
809  }
810}
811
812/// Configuration for the Installer bundle using NSIS.
813#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
814#[cfg_attr(feature = "schema", derive(JsonSchema))]
815#[serde(rename_all = "camelCase", deny_unknown_fields)]
816pub struct NsisConfig {
817  /// A custom .nsi template to use.
818  pub template: Option<PathBuf>,
819  /// The path to a bitmap file to display on the header of installers pages.
820  ///
821  /// The recommended dimensions are 150px x 57px.
822  #[serde(alias = "header-image")]
823  pub header_image: Option<PathBuf>,
824  /// The path to a bitmap file for the Welcome page and the Finish page.
825  ///
826  /// The recommended dimensions are 164px x 314px.
827  #[serde(alias = "sidebar-image")]
828  pub sidebar_image: Option<PathBuf>,
829  /// The path to an icon file used as the installer icon.
830  #[serde(alias = "install-icon")]
831  pub installer_icon: Option<PathBuf>,
832  /// Whether the installation will be for all users or just the current user.
833  #[serde(default, alias = "install-mode")]
834  pub install_mode: NSISInstallerMode,
835  /// A list of installer languages.
836  /// By default the OS language is used. If the OS language is not in the list of languages, the first language will be used.
837  /// To allow the user to select the language, set `display_language_selector` to `true`.
838  ///
839  /// See <https://github.com/kichik/nsis/tree/9465c08046f00ccb6eda985abbdbf52c275c6c4d/Contrib/Language%20files> for the complete list of languages.
840  pub languages: Option<Vec<String>>,
841  /// A key-value pair where the key is the language and the
842  /// value is the path to a custom `.nsh` file that holds the translated text for tauri's custom messages.
843  ///
844  /// See <https://github.com/tauri-apps/tauri/blob/dev/crates/tauri-bundler/src/bundle/windows/nsis/languages/English.nsh> for an example `.nsh` file.
845  ///
846  /// **Note**: the key must be a valid NSIS language and it must be added to [`NsisConfig`] languages array,
847  pub custom_language_files: Option<HashMap<String, PathBuf>>,
848  /// Whether to display a language selector dialog before the installer and uninstaller windows are rendered or not.
849  /// By default the OS language is selected, with a fallback to the first language in the `languages` array.
850  #[serde(default, alias = "display-language-selector")]
851  pub display_language_selector: bool,
852  /// Set the compression algorithm used to compress files in the installer.
853  ///
854  /// See <https://nsis.sourceforge.io/Reference/SetCompressor>
855  #[serde(default)]
856  pub compression: NsisCompression,
857  /// Set the folder name for the start menu shortcut.
858  ///
859  /// Use this option if you have multiple apps and wish to group their shortcuts under one folder
860  /// or if you generally prefer to set your shortcut inside a folder.
861  ///
862  /// Examples:
863  /// - `AwesomePublisher`, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\AwesomePublisher\<your-app>.lnk`
864  /// - If unset, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\<your-app>.lnk`
865  #[serde(alias = "start-menu-folder")]
866  pub start_menu_folder: Option<String>,
867  /// A path to a `.nsh` file that contains special NSIS macros to be hooked into the
868  /// main installer.nsi script.
869  ///
870  /// Supported hooks are:
871  /// - `NSIS_HOOK_PREINSTALL`: This hook runs before copying files, setting registry key values and creating shortcuts.
872  /// - `NSIS_HOOK_POSTINSTALL`: This hook runs after the installer has finished copying all files, setting the registry keys and created shortcuts.
873  /// - `NSIS_HOOK_PREUNINSTALL`: This hook runs before removing any files, registry keys and shortcuts.
874  /// - `NSIS_HOOK_POSTUNINSTALL`: This hook runs after files, registry keys and shortcuts have been removed.
875  ///
876  ///
877  /// ### Example
878  ///
879  /// ```nsh
880  /// !macro NSIS_HOOK_PREINSTALL
881  ///   MessageBox MB_OK "PreInstall"
882  /// !macroend
883  ///
884  /// !macro NSIS_HOOK_POSTINSTALL
885  ///   MessageBox MB_OK "PostInstall"
886  /// !macroend
887  ///
888  /// !macro NSIS_HOOK_PREUNINSTALL
889  ///   MessageBox MB_OK "PreUnInstall"
890  /// !macroend
891  ///
892  /// !macro NSIS_HOOK_POSTUNINSTALL
893  ///   MessageBox MB_OK "PostUninstall"
894  /// !macroend
895  ///
896  /// ```
897  #[serde(alias = "installer-hooks")]
898  pub installer_hooks: Option<PathBuf>,
899  /// Try to ensure that the WebView2 version is equal to or newer than this version,
900  /// if the user's WebView2 is older than this version,
901  /// the installer will try to trigger a WebView2 update.
902  #[serde(alias = "minimum-webview2-version")]
903  pub minimum_webview2_version: Option<String>,
904}
905
906/// Install modes for the Webview2 runtime.
907/// Note that for the updater bundle [`Self::DownloadBootstrapper`] is used.
908///
909/// For more information see <https://v2.tauri.app/distribute/windows-installer/#webview2-installation-options>.
910#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
911#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
912#[cfg_attr(feature = "schema", derive(JsonSchema))]
913pub enum WebviewInstallMode {
914  /// Do not install the Webview2 as part of the Windows Installer.
915  Skip,
916  /// Download the bootstrapper and run it.
917  /// Requires an internet connection.
918  /// Results in a smaller installer size, but is not recommended on Windows 7.
919  DownloadBootstrapper {
920    /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
921    #[serde(default = "default_true")]
922    silent: bool,
923  },
924  /// Embed the bootstrapper and run it.
925  /// Requires an internet connection.
926  /// Increases the installer size by around 1.8MB, but offers better support on Windows 7.
927  EmbedBootstrapper {
928    /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
929    #[serde(default = "default_true")]
930    silent: bool,
931  },
932  /// Embed the offline installer and run it.
933  /// Does not require an internet connection.
934  /// Increases the installer size by around 127MB.
935  OfflineInstaller {
936    /// Instructs the installer to run the installer in silent mode. Defaults to `true`.
937    #[serde(default = "default_true")]
938    silent: bool,
939  },
940  /// Embed a fixed webview2 version and use it at runtime.
941  /// Increases the installer size by around 180MB.
942  FixedRuntime {
943    /// The path to the fixed runtime to use.
944    ///
945    /// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
946    /// The `.cab` file must be extracted to a folder and this folder path must be defined on this field.
947    path: PathBuf,
948  },
949}
950
951impl Default for WebviewInstallMode {
952  fn default() -> Self {
953    Self::DownloadBootstrapper { silent: true }
954  }
955}
956
957/// Custom Signing Command configuration.
958#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
959#[cfg_attr(feature = "schema", derive(JsonSchema))]
960#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
961pub enum CustomSignCommandConfig {
962  /// A string notation of the script to execute.
963  ///
964  /// "%1" will be replaced with the path to the binary to be signed.
965  ///
966  /// This is a simpler notation for the command.
967  /// Tauri will split the string with `' '` and use the first element as the command name and the rest as arguments.
968  ///
969  /// If you need to use whitespace in the command or arguments, use the object notation [`Self::ScriptWithOptions`].
970  Command(String),
971  /// An object notation of the command.
972  ///
973  /// This is more complex notation for the command but
974  /// this allows you to use whitespace in the command and arguments.
975  CommandWithOptions {
976    /// The command to run to sign the binary.
977    cmd: String,
978    /// The arguments to pass to the command.
979    ///
980    /// "%1" will be replaced with the path to the binary to be signed.
981    args: Vec<String>,
982  },
983}
984
985/// Windows bundler configuration.
986///
987/// See more: <https://v2.tauri.app/reference/config/#windowsconfig>
988#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
989#[cfg_attr(feature = "schema", derive(JsonSchema))]
990#[serde(rename_all = "camelCase", deny_unknown_fields)]
991pub struct WindowsConfig {
992  /// Specifies the file digest algorithm to use for creating file signatures.
993  /// Required for code signing. SHA-256 is recommended.
994  #[serde(alias = "digest-algorithm")]
995  pub digest_algorithm: Option<String>,
996  /// Specifies the SHA1 hash of the signing certificate.
997  #[serde(alias = "certificate-thumbprint")]
998  pub certificate_thumbprint: Option<String>,
999  /// Server to use during timestamping.
1000  #[serde(alias = "timestamp-url")]
1001  pub timestamp_url: Option<String>,
1002  /// Whether to use Time-Stamp Protocol (TSP, a.k.a. RFC 3161) for the timestamp server. Your code signing provider may
1003  /// use a TSP timestamp server, like e.g. SSL.com does. If so, enable TSP by setting to true.
1004  #[serde(default)]
1005  pub tsp: bool,
1006  /// The installation mode for the Webview2 runtime.
1007  #[serde(default, alias = "webview-install-mode")]
1008  pub webview_install_mode: WebviewInstallMode,
1009  /// Validates a second app installation, blocking the user from installing an older version if set to `false`.
1010  ///
1011  /// For instance, if `1.2.1` is installed, the user won't be able to install app version `1.2.0` or `1.1.5`.
1012  ///
1013  /// The default value of this flag is `true`.
1014  #[serde(default = "default_true", alias = "allow-downgrades")]
1015  pub allow_downgrades: bool,
1016  /// Configuration for the MSI generated with WiX.
1017  pub wix: Option<WixConfig>,
1018  /// Configuration for the installer generated with NSIS.
1019  pub nsis: Option<NsisConfig>,
1020  /// Specify a custom command to sign the binaries.
1021  /// This command needs to have a `%1` in args which is just a placeholder for the binary path,
1022  /// which we will detect and replace before calling the command.
1023  ///
1024  /// By Default we use `signtool.exe` which can be found only on Windows so
1025  /// if you are on another platform and want to cross-compile and sign you will
1026  /// need to use another tool like `osslsigncode`.
1027  #[serde(alias = "sign-command")]
1028  pub sign_command: Option<CustomSignCommandConfig>,
1029}
1030
1031impl Default for WindowsConfig {
1032  fn default() -> Self {
1033    Self {
1034      digest_algorithm: None,
1035      certificate_thumbprint: None,
1036      timestamp_url: None,
1037      tsp: false,
1038      webview_install_mode: Default::default(),
1039      allow_downgrades: true,
1040      wix: None,
1041      nsis: None,
1042      sign_command: None,
1043    }
1044  }
1045}
1046
1047/// macOS-only. Corresponds to CFBundleTypeRole
1048#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1049#[cfg_attr(feature = "schema", derive(JsonSchema))]
1050pub enum BundleTypeRole {
1051  /// CFBundleTypeRole.Editor. Files can be read and edited.
1052  #[default]
1053  Editor,
1054  /// CFBundleTypeRole.Viewer. Files can be read.
1055  Viewer,
1056  /// CFBundleTypeRole.Shell
1057  Shell,
1058  /// CFBundleTypeRole.QLGenerator
1059  QLGenerator,
1060  /// CFBundleTypeRole.None
1061  None,
1062}
1063
1064impl Display for BundleTypeRole {
1065  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1066    match self {
1067      Self::Editor => write!(f, "Editor"),
1068      Self::Viewer => write!(f, "Viewer"),
1069      Self::Shell => write!(f, "Shell"),
1070      Self::QLGenerator => write!(f, "QLGenerator"),
1071      Self::None => write!(f, "None"),
1072    }
1073  }
1074}
1075
1076/// An extension for a [`FileAssociation`].
1077///
1078/// A leading `.` is automatically stripped.
1079#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
1080#[cfg_attr(feature = "schema", derive(JsonSchema))]
1081pub struct AssociationExt(pub String);
1082
1083impl fmt::Display for AssociationExt {
1084  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1085    write!(f, "{}", self.0)
1086  }
1087}
1088
1089impl<'d> serde::Deserialize<'d> for AssociationExt {
1090  fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
1091    let ext = String::deserialize(deserializer)?;
1092    if let Some(ext) = ext.strip_prefix('.') {
1093      Ok(AssociationExt(ext.into()))
1094    } else {
1095      Ok(AssociationExt(ext))
1096    }
1097  }
1098}
1099
1100/// File association
1101#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1102#[cfg_attr(feature = "schema", derive(JsonSchema))]
1103#[serde(rename_all = "camelCase", deny_unknown_fields)]
1104pub struct FileAssociation {
1105  /// File extensions to associate with this app. e.g. 'png'
1106  pub ext: Vec<AssociationExt>,
1107  /// The name. Maps to `CFBundleTypeName` on macOS. Default to `ext[0]`
1108  pub name: Option<String>,
1109  /// The association description. Windows-only. It is displayed on the `Type` column on Windows Explorer.
1110  pub description: Option<String>,
1111  /// The app's role with respect to the type. Maps to `CFBundleTypeRole` on macOS.
1112  #[serde(default)]
1113  pub role: BundleTypeRole,
1114  /// The mime-type e.g. 'image/png' or 'text/plain'. Linux-only.
1115  #[serde(alias = "mime-type")]
1116  pub mime_type: Option<String>,
1117}
1118
1119/// Deep link protocol configuration.
1120#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1121#[cfg_attr(feature = "schema", derive(JsonSchema))]
1122#[serde(rename_all = "camelCase", deny_unknown_fields)]
1123pub struct DeepLinkProtocol {
1124  /// URL schemes to associate with this app without `://`. For example `my-app`
1125  pub schemes: Vec<String>,
1126  /// The protocol name. **macOS-only** and maps to `CFBundleTypeName`. Defaults to `<bundle-id>.<schemes[0]>`
1127  pub name: Option<String>,
1128  /// The app's role for these schemes. **macOS-only** and maps to `CFBundleTypeRole`.
1129  #[serde(default)]
1130  pub role: BundleTypeRole,
1131}
1132
1133/// Definition for bundle resources.
1134/// Can be either a list of paths to include or a map of source to target paths.
1135#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1136#[cfg_attr(feature = "schema", derive(JsonSchema))]
1137#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1138pub enum BundleResources {
1139  /// A list of paths to include.
1140  List(Vec<String>),
1141  /// A map of source to target paths.
1142  Map(HashMap<String, String>),
1143}
1144
1145impl BundleResources {
1146  /// Adds a path to the resource collection.
1147  pub fn push(&mut self, path: impl Into<String>) {
1148    match self {
1149      Self::List(l) => l.push(path.into()),
1150      Self::Map(l) => {
1151        let path = path.into();
1152        l.insert(path.clone(), path);
1153      }
1154    }
1155  }
1156}
1157
1158/// Updater type
1159#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1160#[cfg_attr(feature = "schema", derive(JsonSchema))]
1161#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1162pub enum Updater {
1163  /// Generates lagacy zipped v1 compatible updaters
1164  String(V1Compatible),
1165  /// Produce updaters and their signatures or not
1166  // Can't use untagged on enum field here: https://github.com/GREsau/schemars/issues/222
1167  Bool(bool),
1168}
1169
1170impl Default for Updater {
1171  fn default() -> Self {
1172    Self::Bool(false)
1173  }
1174}
1175
1176/// Generates lagacy zipped v1 compatible updaters
1177#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1178#[cfg_attr(feature = "schema", derive(JsonSchema))]
1179#[serde(rename_all = "camelCase", deny_unknown_fields)]
1180pub enum V1Compatible {
1181  /// Generates lagacy zipped v1 compatible updaters
1182  V1Compatible,
1183}
1184
1185/// Configuration for tauri-bundler.
1186///
1187/// See more: <https://v2.tauri.app/reference/config/#bundleconfig>
1188#[skip_serializing_none]
1189#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1190#[cfg_attr(feature = "schema", derive(JsonSchema))]
1191#[serde(rename_all = "camelCase", deny_unknown_fields)]
1192pub struct BundleConfig {
1193  /// Whether Tauri should bundle your application or just output the executable.
1194  #[serde(default)]
1195  pub active: bool,
1196  /// The bundle targets, currently supports ["deb", "rpm", "appimage", "nsis", "msi", "app", "dmg"] or "all".
1197  #[serde(default)]
1198  pub targets: BundleTarget,
1199  #[serde(default)]
1200  /// Produce updaters and their signatures or not
1201  pub create_updater_artifacts: Updater,
1202  /// The application's publisher. Defaults to the second element in the identifier string.
1203  ///
1204  /// Currently maps to the Manufacturer property of the Windows Installer
1205  /// and the Maintainer field of debian packages if the Cargo.toml does not have the authors field.
1206  pub publisher: Option<String>,
1207  /// A url to the home page of your application. If unset, will
1208  /// fallback to `homepage` defined in `Cargo.toml`.
1209  ///
1210  /// Supported bundle targets: `deb`, `rpm`, `nsis` and `msi`.
1211  pub homepage: Option<String>,
1212  /// The app's icons
1213  #[serde(default)]
1214  pub icon: Vec<String>,
1215  /// App resources to bundle.
1216  /// Each resource is a path to a file or directory.
1217  /// Glob patterns are supported.
1218  pub resources: Option<BundleResources>,
1219  /// A copyright string associated with your application.
1220  pub copyright: Option<String>,
1221  /// The package's license identifier to be included in the appropriate bundles.
1222  /// If not set, defaults to the license from the Cargo.toml file.
1223  pub license: Option<String>,
1224  /// The path to the license file to be included in the appropriate bundles.
1225  #[serde(alias = "license-file")]
1226  pub license_file: Option<PathBuf>,
1227  /// The application kind.
1228  ///
1229  /// Should be one of the following:
1230  /// Business, DeveloperTool, Education, Entertainment, Finance, Game, ActionGame, AdventureGame, ArcadeGame, BoardGame, CardGame, CasinoGame, DiceGame, EducationalGame, FamilyGame, KidsGame, MusicGame, PuzzleGame, RacingGame, RolePlayingGame, SimulationGame, SportsGame, StrategyGame, TriviaGame, WordGame, GraphicsAndDesign, HealthcareAndFitness, Lifestyle, Medical, Music, News, Photography, Productivity, Reference, SocialNetworking, Sports, Travel, Utility, Video, Weather.
1231  pub category: Option<String>,
1232  /// File associations to application.
1233  pub file_associations: Option<Vec<FileAssociation>>,
1234  /// A short description of your application.
1235  #[serde(alias = "short-description")]
1236  pub short_description: Option<String>,
1237  /// A longer, multi-line description of the application.
1238  #[serde(alias = "long-description")]
1239  pub long_description: Option<String>,
1240  /// Whether to use the project's `target` directory, for caching build tools (e.g., Wix and NSIS) when building this application. Defaults to `false`.
1241  ///
1242  /// If true, tools will be cached in `target/.tauri/`.
1243  /// If false, tools will be cached in the current user's platform-specific cache directory.
1244  ///
1245  /// An example where it can be appropriate to set this to `true` is when building this application as a Windows System user (e.g., AWS EC2 workloads),
1246  /// because the Window system's app data directory is restricted.
1247  #[serde(default, alias = "use-local-tools-dir")]
1248  pub use_local_tools_dir: bool,
1249  /// A list of—either absolute or relative—paths to binaries to embed with your application.
1250  ///
1251  /// Note that Tauri will look for system-specific binaries following the pattern "binary-name{-target-triple}{.system-extension}".
1252  ///
1253  /// E.g. for the external binary "my-binary", Tauri looks for:
1254  ///
1255  /// - "my-binary-x86_64-pc-windows-msvc.exe" for Windows
1256  /// - "my-binary-x86_64-apple-darwin" for macOS
1257  /// - "my-binary-x86_64-unknown-linux-gnu" for Linux
1258  ///
1259  /// so don't forget to provide binaries for all targeted platforms.
1260  #[serde(alias = "external-bin")]
1261  pub external_bin: Option<Vec<String>>,
1262  /// Configuration for the Windows bundles.
1263  #[serde(default)]
1264  pub windows: WindowsConfig,
1265  /// Configuration for the Linux bundles.
1266  #[serde(default)]
1267  pub linux: LinuxConfig,
1268  /// Configuration for the macOS bundles.
1269  #[serde(rename = "macOS", alias = "macos", default)]
1270  pub macos: MacConfig,
1271  /// iOS configuration.
1272  #[serde(rename = "iOS", alias = "ios", default)]
1273  pub ios: IosConfig,
1274  /// Android configuration.
1275  #[serde(default)]
1276  pub android: AndroidConfig,
1277}
1278
1279/// A tuple struct of RGBA colors. Each value has minimum of 0 and maximum of 255.
1280#[derive(Debug, PartialEq, Eq, Serialize, Default, Clone, Copy)]
1281#[serde(rename_all = "camelCase", deny_unknown_fields)]
1282pub struct Color(pub u8, pub u8, pub u8, pub u8);
1283
1284impl From<Color> for (u8, u8, u8, u8) {
1285  fn from(value: Color) -> Self {
1286    (value.0, value.1, value.2, value.3)
1287  }
1288}
1289
1290impl From<Color> for (u8, u8, u8) {
1291  fn from(value: Color) -> Self {
1292    (value.0, value.1, value.2)
1293  }
1294}
1295
1296impl From<(u8, u8, u8, u8)> for Color {
1297  fn from(value: (u8, u8, u8, u8)) -> Self {
1298    Color(value.0, value.1, value.2, value.3)
1299  }
1300}
1301
1302impl From<(u8, u8, u8)> for Color {
1303  fn from(value: (u8, u8, u8)) -> Self {
1304    Color(value.0, value.1, value.2, 255)
1305  }
1306}
1307
1308impl From<Color> for [u8; 4] {
1309  fn from(value: Color) -> Self {
1310    [value.0, value.1, value.2, value.3]
1311  }
1312}
1313
1314impl From<Color> for [u8; 3] {
1315  fn from(value: Color) -> Self {
1316    [value.0, value.1, value.2]
1317  }
1318}
1319
1320impl From<[u8; 4]> for Color {
1321  fn from(value: [u8; 4]) -> Self {
1322    Color(value[0], value[1], value[2], value[3])
1323  }
1324}
1325
1326impl From<[u8; 3]> for Color {
1327  fn from(value: [u8; 3]) -> Self {
1328    Color(value[0], value[1], value[2], 255)
1329  }
1330}
1331
1332impl FromStr for Color {
1333  type Err = String;
1334  fn from_str(mut color: &str) -> Result<Self, Self::Err> {
1335    color = color.trim().strip_prefix('#').unwrap_or(color);
1336    let color = match color.len() {
1337      // TODO: use repeat_n once our MSRV is bumped to 1.82
1338      3 => color.chars()
1339            .flat_map(|c| std::iter::repeat(c).take(2))
1340            .chain(std::iter::repeat('f').take(2))
1341            .collect(),
1342      6 => format!("{color}FF"),
1343      8 => color.to_string(),
1344      _ => return Err("Invalid hex color length, must be either 3, 6 or 8, for example: #fff, #ffffff, or #ffffffff".into()),
1345    };
1346
1347    let r = u8::from_str_radix(&color[0..2], 16).map_err(|e| e.to_string())?;
1348    let g = u8::from_str_radix(&color[2..4], 16).map_err(|e| e.to_string())?;
1349    let b = u8::from_str_radix(&color[4..6], 16).map_err(|e| e.to_string())?;
1350    let a = u8::from_str_radix(&color[6..8], 16).map_err(|e| e.to_string())?;
1351
1352    Ok(Color(r, g, b, a))
1353  }
1354}
1355
1356fn default_alpha() -> u8 {
1357  255
1358}
1359
1360#[derive(Deserialize)]
1361#[cfg_attr(feature = "schema", derive(JsonSchema))]
1362#[serde(untagged)]
1363enum InnerColor {
1364  /// Color hex string, for example: #fff, #ffffff, or #ffffffff.
1365  String(String),
1366  /// Array of RGB colors. Each value has minimum of 0 and maximum of 255.
1367  Rgb((u8, u8, u8)),
1368  /// Array of RGBA colors. Each value has minimum of 0 and maximum of 255.
1369  Rgba((u8, u8, u8, u8)),
1370  /// Object of red, green, blue, alpha color values. Each value has minimum of 0 and maximum of 255.
1371  RgbaObject {
1372    red: u8,
1373    green: u8,
1374    blue: u8,
1375    #[serde(default = "default_alpha")]
1376    alpha: u8,
1377  },
1378}
1379
1380impl<'de> Deserialize<'de> for Color {
1381  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1382  where
1383    D: Deserializer<'de>,
1384  {
1385    let color = InnerColor::deserialize(deserializer)?;
1386    let color = match color {
1387      InnerColor::String(string) => string.parse().map_err(serde::de::Error::custom)?,
1388      InnerColor::Rgb(rgb) => Color(rgb.0, rgb.1, rgb.2, 255),
1389      InnerColor::Rgba(rgb) => rgb.into(),
1390      InnerColor::RgbaObject {
1391        red,
1392        green,
1393        blue,
1394        alpha,
1395      } => Color(red, green, blue, alpha),
1396    };
1397
1398    Ok(color)
1399  }
1400}
1401
1402#[cfg(feature = "schema")]
1403impl schemars::JsonSchema for Color {
1404  fn schema_name() -> String {
1405    "Color".to_string()
1406  }
1407
1408  fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1409    let mut schema = schemars::schema_for!(InnerColor).schema;
1410    schema.metadata = None; // Remove `title: InnerColor` from schema
1411
1412    // add hex color pattern validation
1413    let any_of = schema.subschemas().any_of.as_mut().unwrap();
1414    let schemars::schema::Schema::Object(str_schema) = any_of.first_mut().unwrap() else {
1415      unreachable!()
1416    };
1417    str_schema.string().pattern = Some("^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$".into());
1418
1419    schema.into()
1420  }
1421}
1422
1423/// Background throttling policy.
1424#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1425#[cfg_attr(feature = "schema", derive(JsonSchema))]
1426#[serde(rename_all = "camelCase", deny_unknown_fields)]
1427pub enum BackgroundThrottlingPolicy {
1428  /// A policy where background throttling is disabled
1429  Disabled,
1430  /// A policy where a web view that’s not in a window fully suspends tasks. This is usually the default behavior in case no policy is set.
1431  Suspend,
1432  /// A policy where a web view that’s not in a window limits processing, but does not fully suspend tasks.
1433  Throttle,
1434}
1435
1436/// The window effects configuration object
1437#[skip_serializing_none]
1438#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1439#[cfg_attr(feature = "schema", derive(JsonSchema))]
1440#[serde(rename_all = "camelCase", deny_unknown_fields)]
1441pub struct WindowEffectsConfig {
1442  /// List of Window effects to apply to the Window.
1443  /// Conflicting effects will apply the first one and ignore the rest.
1444  pub effects: Vec<WindowEffect>,
1445  /// Window effect state **macOS Only**
1446  pub state: Option<WindowEffectState>,
1447  /// Window effect corner radius **macOS Only**
1448  pub radius: Option<f64>,
1449  /// Window effect color. Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only
1450  /// on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.
1451  pub color: Option<Color>,
1452}
1453
1454/// The window configuration object.
1455///
1456/// See more: <https://v2.tauri.app/reference/config/#windowconfig>
1457#[skip_serializing_none]
1458#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
1459#[cfg_attr(feature = "schema", derive(JsonSchema))]
1460#[serde(rename_all = "camelCase", deny_unknown_fields)]
1461pub struct WindowConfig {
1462  /// The window identifier. It must be alphanumeric.
1463  #[serde(default = "default_window_label")]
1464  pub label: String,
1465  /// Whether Tauri should create this window at app startup or not.
1466  ///
1467  /// When this is set to `false` you must manually grab the config object via `app.config().app.windows`
1468  /// and create it with [`WebviewWindowBuilder::from_config`](https://docs.rs/tauri/2.0.0-rc/tauri/webview/struct.WebviewWindowBuilder.html#method.from_config).
1469  #[serde(default = "default_true")]
1470  pub create: bool,
1471  /// The window webview URL.
1472  #[serde(default)]
1473  pub url: WebviewUrl,
1474  /// The user agent for the webview
1475  #[serde(alias = "user-agent")]
1476  pub user_agent: Option<String>,
1477  /// Whether the drag and drop is enabled or not on the webview. By default it is enabled.
1478  ///
1479  /// Disabling it is required to use HTML5 drag and drop on the frontend on Windows.
1480  #[serde(default = "default_true", alias = "drag-drop-enabled")]
1481  pub drag_drop_enabled: bool,
1482  /// Whether or not the window starts centered or not.
1483  #[serde(default)]
1484  pub center: bool,
1485  /// The horizontal position of the window's top left corner
1486  pub x: Option<f64>,
1487  /// The vertical position of the window's top left corner
1488  pub y: Option<f64>,
1489  /// The window width.
1490  #[serde(default = "default_width")]
1491  pub width: f64,
1492  /// The window height.
1493  #[serde(default = "default_height")]
1494  pub height: f64,
1495  /// The min window width.
1496  #[serde(alias = "min-width")]
1497  pub min_width: Option<f64>,
1498  /// The min window height.
1499  #[serde(alias = "min-height")]
1500  pub min_height: Option<f64>,
1501  /// The max window width.
1502  #[serde(alias = "max-width")]
1503  pub max_width: Option<f64>,
1504  /// The max window height.
1505  #[serde(alias = "max-height")]
1506  pub max_height: Option<f64>,
1507  /// Whether the window is resizable or not. When resizable is set to false, native window's maximize button is automatically disabled.
1508  #[serde(default = "default_true")]
1509  pub resizable: bool,
1510  /// Whether the window's native maximize button is enabled or not.
1511  /// If resizable is set to false, this setting is ignored.
1512  ///
1513  /// ## Platform-specific
1514  ///
1515  /// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode.
1516  /// - **Linux / iOS / Android:** Unsupported.
1517  #[serde(default = "default_true")]
1518  pub maximizable: bool,
1519  /// Whether the window's native minimize button is enabled or not.
1520  ///
1521  /// ## Platform-specific
1522  ///
1523  /// - **Linux / iOS / Android:** Unsupported.
1524  #[serde(default = "default_true")]
1525  pub minimizable: bool,
1526  /// Whether the window's native close button is enabled or not.
1527  ///
1528  /// ## Platform-specific
1529  ///
1530  /// - **Linux:** "GTK+ will do its best to convince the window manager not to show a close button.
1531  ///   Depending on the system, this function may not have any effect when called on a window that is already visible"
1532  /// - **iOS / Android:** Unsupported.
1533  #[serde(default = "default_true")]
1534  pub closable: bool,
1535  /// The window title.
1536  #[serde(default = "default_title")]
1537  pub title: String,
1538  /// Whether the window starts as fullscreen or not.
1539  #[serde(default)]
1540  pub fullscreen: bool,
1541  /// Whether the window will be initially focused or not.
1542  #[serde(default = "default_true")]
1543  pub focus: bool,
1544  /// Whether the window is transparent or not.
1545  ///
1546  /// Note that on `macOS` this requires the `macos-private-api` feature flag, enabled under `tauri > macOSPrivateApi`.
1547  /// WARNING: Using private APIs on `macOS` prevents your application from being accepted to the `App Store`.
1548  #[serde(default)]
1549  pub transparent: bool,
1550  /// Whether the window is maximized or not.
1551  #[serde(default)]
1552  pub maximized: bool,
1553  /// Whether the window is visible or not.
1554  #[serde(default = "default_true")]
1555  pub visible: bool,
1556  /// Whether the window should have borders and bars.
1557  #[serde(default = "default_true")]
1558  pub decorations: bool,
1559  /// Whether the window should always be below other windows.
1560  #[serde(default, alias = "always-on-bottom")]
1561  pub always_on_bottom: bool,
1562  /// Whether the window should always be on top of other windows.
1563  #[serde(default, alias = "always-on-top")]
1564  pub always_on_top: bool,
1565  /// Whether the window should be visible on all workspaces or virtual desktops.
1566  ///
1567  /// ## Platform-specific
1568  ///
1569  /// - **Windows / iOS / Android:** Unsupported.
1570  #[serde(default, alias = "visible-on-all-workspaces")]
1571  pub visible_on_all_workspaces: bool,
1572  /// Prevents the window contents from being captured by other apps.
1573  #[serde(default, alias = "content-protected")]
1574  pub content_protected: bool,
1575  /// If `true`, hides the window icon from the taskbar on Windows and Linux.
1576  #[serde(default, alias = "skip-taskbar")]
1577  pub skip_taskbar: bool,
1578  /// The name of the window class created on Windows to create the window. **Windows only**.
1579  pub window_classname: Option<String>,
1580  /// The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.
1581  pub theme: Option<crate::Theme>,
1582  /// The style of the macOS title bar.
1583  #[serde(default, alias = "title-bar-style")]
1584  pub title_bar_style: TitleBarStyle,
1585  /// If `true`, sets the window title to be hidden on macOS.
1586  #[serde(default, alias = "hidden-title")]
1587  pub hidden_title: bool,
1588  /// Whether clicking an inactive window also clicks through to the webview on macOS.
1589  #[serde(default, alias = "accept-first-mouse")]
1590  pub accept_first_mouse: bool,
1591  /// Defines the window [tabbing identifier] for macOS.
1592  ///
1593  /// Windows with matching tabbing identifiers will be grouped together.
1594  /// If the tabbing identifier is not set, automatic tabbing will be disabled.
1595  ///
1596  /// [tabbing identifier]: <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
1597  #[serde(default, alias = "tabbing-identifier")]
1598  pub tabbing_identifier: Option<String>,
1599  /// Defines additional browser arguments on Windows. By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
1600  /// so if you use this method, you also need to disable these components by yourself if you want.
1601  #[serde(default, alias = "additional-browser-args")]
1602  pub additional_browser_args: Option<String>,
1603  /// Whether or not the window has shadow.
1604  ///
1605  /// ## Platform-specific
1606  ///
1607  /// - **Windows:**
1608  ///   - `false` has no effect on decorated window, shadow are always ON.
1609  ///   - `true` will make undecorated window have a 1px white border,
1610  /// and on Windows 11, it will have a rounded corners.
1611  /// - **Linux:** Unsupported.
1612  #[serde(default = "default_true")]
1613  pub shadow: bool,
1614  /// Window effects.
1615  ///
1616  /// Requires the window to be transparent.
1617  ///
1618  /// ## Platform-specific:
1619  ///
1620  /// - **Windows**: If using decorations or shadows, you may want to try this workaround <https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891>
1621  /// - **Linux**: Unsupported
1622  #[serde(default, alias = "window-effects")]
1623  pub window_effects: Option<WindowEffectsConfig>,
1624  /// Whether or not the webview should be launched in incognito  mode.
1625  ///
1626  ///  ## Platform-specific:
1627  ///
1628  ///  - **Android**: Unsupported.
1629  #[serde(default)]
1630  pub incognito: bool,
1631  /// Sets the window associated with this label to be the parent of the window to be created.
1632  ///
1633  /// ## Platform-specific
1634  ///
1635  /// - **Windows**: This sets the passed parent as an owner window to the window to be created.
1636  ///   From [MSDN owned windows docs](https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows):
1637  ///     - An owned window is always above its owner in the z-order.
1638  ///     - The system automatically destroys an owned window when its owner is destroyed.
1639  ///     - An owned window is hidden when its owner is minimized.
1640  /// - **Linux**: This makes the new window transient for parent, see <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
1641  /// - **macOS**: This adds the window as a child of parent, see <https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow?language=objc>
1642  pub parent: Option<String>,
1643  /// The proxy URL for the WebView for all network requests.
1644  ///
1645  /// Must be either a `http://` or a `socks5://` URL.
1646  ///
1647  /// ## Platform-specific
1648  ///
1649  /// - **macOS**: Requires the `macos-proxy` feature flag and only compiles for macOS 14+.
1650  #[serde(alias = "proxy-url")]
1651  pub proxy_url: Option<Url>,
1652  /// Whether page zooming by hotkeys is enabled
1653  ///
1654  /// ## Platform-specific:
1655  ///
1656  /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting.
1657  /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`,
1658  /// 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission
1659  ///
1660  /// - **Android / iOS**: Unsupported.
1661  #[serde(default, alias = "zoom-hotkeys-enabled")]
1662  pub zoom_hotkeys_enabled: bool,
1663  /// Whether browser extensions can be installed for the webview process
1664  ///
1665  /// ## Platform-specific:
1666  ///
1667  /// - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled)
1668  /// - **MacOS / Linux / iOS / Android** - Unsupported.
1669  #[serde(default, alias = "browser-extensions-enabled")]
1670  pub browser_extensions_enabled: bool,
1671
1672  /// Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.
1673  ///
1674  /// ## Note
1675  ///
1676  /// Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
1677  ///
1678  /// ## Warning
1679  ///
1680  /// Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.
1681  #[serde(default, alias = "use-https-scheme")]
1682  pub use_https_scheme: bool,
1683  /// Enable web inspector which is usually called browser devtools. Enabled by default.
1684  ///
1685  /// This API works in **debug** builds, but requires `devtools` feature flag to enable it in **release** builds.
1686  ///
1687  /// ## Platform-specific
1688  ///
1689  /// - macOS: This will call private functions on **macOS**.
1690  /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
1691  /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
1692  pub devtools: Option<bool>,
1693
1694  /// Set the window and webview background color.
1695  ///
1696  /// ## Platform-specific:
1697  ///
1698  /// - **Windows**: alpha channel is ignored for the window layer.
1699  /// - **Windows**: On Windows 7, alpha channel is ignored for the webview layer.
1700  /// - **Windows**: On Windows 8 and newer, if alpha channel is not `0`, it will be ignored for the webview layer.
1701  #[serde(alias = "background-color")]
1702  pub background_color: Option<Color>,
1703
1704  /// Change the default background throttling behaviour.
1705  ///
1706  /// By default, browsers use a suspend policy that will throttle timers and even unload
1707  /// the whole tab (view) to free resources after roughly 5 minutes when a view became
1708  /// minimized or hidden. This will pause all tasks until the documents visibility state
1709  /// changes back from hidden to visible by bringing the view back to the foreground.
1710  ///
1711  /// ## Platform-specific
1712  ///
1713  /// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.
1714  /// - **iOS**: Supported since version 17.0+.
1715  /// - **macOS**: Supported since version 14.0+.
1716  ///
1717  /// see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578
1718  #[serde(default, alias = "background-throttling")]
1719  pub background_throttling: Option<BackgroundThrottlingPolicy>,
1720}
1721
1722impl Default for WindowConfig {
1723  fn default() -> Self {
1724    Self {
1725      label: default_window_label(),
1726      url: WebviewUrl::default(),
1727      create: true,
1728      user_agent: None,
1729      drag_drop_enabled: true,
1730      center: false,
1731      x: None,
1732      y: None,
1733      width: default_width(),
1734      height: default_height(),
1735      min_width: None,
1736      min_height: None,
1737      max_width: None,
1738      max_height: None,
1739      resizable: true,
1740      maximizable: true,
1741      minimizable: true,
1742      closable: true,
1743      title: default_title(),
1744      fullscreen: false,
1745      focus: false,
1746      transparent: false,
1747      maximized: false,
1748      visible: true,
1749      decorations: true,
1750      always_on_bottom: false,
1751      always_on_top: false,
1752      visible_on_all_workspaces: false,
1753      content_protected: false,
1754      skip_taskbar: false,
1755      window_classname: None,
1756      theme: None,
1757      title_bar_style: Default::default(),
1758      hidden_title: false,
1759      accept_first_mouse: false,
1760      tabbing_identifier: None,
1761      additional_browser_args: None,
1762      shadow: true,
1763      window_effects: None,
1764      incognito: false,
1765      parent: None,
1766      proxy_url: None,
1767      zoom_hotkeys_enabled: false,
1768      browser_extensions_enabled: false,
1769      use_https_scheme: false,
1770      devtools: None,
1771      background_color: None,
1772      background_throttling: None,
1773    }
1774  }
1775}
1776
1777fn default_window_label() -> String {
1778  "main".to_string()
1779}
1780
1781fn default_width() -> f64 {
1782  800f64
1783}
1784
1785fn default_height() -> f64 {
1786  600f64
1787}
1788
1789fn default_title() -> String {
1790  "Tauri App".to_string()
1791}
1792
1793/// A Content-Security-Policy directive source list.
1794/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources>.
1795#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1796#[cfg_attr(feature = "schema", derive(JsonSchema))]
1797#[serde(rename_all = "camelCase", untagged)]
1798pub enum CspDirectiveSources {
1799  /// An inline list of CSP sources. Same as [`Self::List`], but concatenated with a space separator.
1800  Inline(String),
1801  /// A list of CSP sources. The collection will be concatenated with a space separator for the CSP string.
1802  List(Vec<String>),
1803}
1804
1805impl Default for CspDirectiveSources {
1806  fn default() -> Self {
1807    Self::List(Vec::new())
1808  }
1809}
1810
1811impl From<CspDirectiveSources> for Vec<String> {
1812  fn from(sources: CspDirectiveSources) -> Self {
1813    match sources {
1814      CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
1815      CspDirectiveSources::List(l) => l,
1816    }
1817  }
1818}
1819
1820impl CspDirectiveSources {
1821  /// Whether the given source is configured on this directive or not.
1822  pub fn contains(&self, source: &str) -> bool {
1823    match self {
1824      Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
1825      Self::List(l) => l.contains(&source.into()),
1826    }
1827  }
1828
1829  /// Appends the given source to this directive.
1830  pub fn push<S: AsRef<str>>(&mut self, source: S) {
1831    match self {
1832      Self::Inline(s) => {
1833        s.push(' ');
1834        s.push_str(source.as_ref());
1835      }
1836      Self::List(l) => {
1837        l.push(source.as_ref().to_string());
1838      }
1839    }
1840  }
1841
1842  /// Extends this CSP directive source list with the given array of sources.
1843  pub fn extend(&mut self, sources: Vec<String>) {
1844    for s in sources {
1845      self.push(s);
1846    }
1847  }
1848}
1849
1850/// A Content-Security-Policy definition.
1851/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
1852#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1853#[cfg_attr(feature = "schema", derive(JsonSchema))]
1854#[serde(rename_all = "camelCase", untagged)]
1855pub enum Csp {
1856  /// The entire CSP policy in a single text string.
1857  Policy(String),
1858  /// An object mapping a directive with its sources values as a list of strings.
1859  DirectiveMap(HashMap<String, CspDirectiveSources>),
1860}
1861
1862impl From<HashMap<String, CspDirectiveSources>> for Csp {
1863  fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
1864    Self::DirectiveMap(map)
1865  }
1866}
1867
1868impl From<Csp> for HashMap<String, CspDirectiveSources> {
1869  fn from(csp: Csp) -> Self {
1870    match csp {
1871      Csp::Policy(policy) => {
1872        let mut map = HashMap::new();
1873        for directive in policy.split(';') {
1874          let mut tokens = directive.trim().split(' ');
1875          if let Some(directive) = tokens.next() {
1876            let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
1877            map.insert(directive.to_string(), CspDirectiveSources::List(sources));
1878          }
1879        }
1880        map
1881      }
1882      Csp::DirectiveMap(m) => m,
1883    }
1884  }
1885}
1886
1887impl Display for Csp {
1888  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1889    match self {
1890      Self::Policy(s) => write!(f, "{s}"),
1891      Self::DirectiveMap(m) => {
1892        let len = m.len();
1893        let mut i = 0;
1894        for (directive, sources) in m {
1895          let sources: Vec<String> = sources.clone().into();
1896          write!(f, "{} {}", directive, sources.join(" "))?;
1897          i += 1;
1898          if i != len {
1899            write!(f, "; ")?;
1900          }
1901        }
1902        Ok(())
1903      }
1904    }
1905  }
1906}
1907
1908/// The possible values for the `dangerous_disable_asset_csp_modification` config option.
1909#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1910#[serde(untagged)]
1911#[cfg_attr(feature = "schema", derive(JsonSchema))]
1912pub enum DisabledCspModificationKind {
1913  /// If `true`, disables all CSP modification.
1914  /// `false` is the default value and it configures Tauri to control the CSP.
1915  Flag(bool),
1916  /// Disables the given list of CSP directives modifications.
1917  List(Vec<String>),
1918}
1919
1920impl DisabledCspModificationKind {
1921  /// Determines whether the given CSP directive can be modified or not.
1922  pub fn can_modify(&self, directive: &str) -> bool {
1923    match self {
1924      Self::Flag(f) => !f,
1925      Self::List(l) => !l.contains(&directive.into()),
1926    }
1927  }
1928}
1929
1930impl Default for DisabledCspModificationKind {
1931  fn default() -> Self {
1932    Self::Flag(false)
1933  }
1934}
1935
1936/// Protocol scope definition.
1937/// It is a list of glob patterns that restrict the API access from the webview.
1938///
1939/// Each pattern can start with a variable that resolves to a system base directory.
1940/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
1941/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
1942/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
1943/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
1944#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1945#[serde(untagged)]
1946#[cfg_attr(feature = "schema", derive(JsonSchema))]
1947pub enum FsScope {
1948  /// A list of paths that are allowed by this scope.
1949  AllowedPaths(Vec<PathBuf>),
1950  /// A complete scope configuration.
1951  #[serde(rename_all = "camelCase")]
1952  Scope {
1953    /// A list of paths that are allowed by this scope.
1954    #[serde(default)]
1955    allow: Vec<PathBuf>,
1956    /// A list of paths that are not allowed by this scope.
1957    /// This gets precedence over the [`Self::Scope::allow`] list.
1958    #[serde(default)]
1959    deny: Vec<PathBuf>,
1960    /// Whether or not paths that contain components that start with a `.`
1961    /// will require that `.` appears literally in the pattern; `*`, `?`, `**`,
1962    /// or `[...]` will not match. This is useful because such files are
1963    /// conventionally considered hidden on Unix systems and it might be
1964    /// desirable to skip them when listing files.
1965    ///
1966    /// Defaults to `true` on Unix systems and `false` on Windows
1967    // dotfiles are not supposed to be exposed by default on unix
1968    #[serde(alias = "require-literal-leading-dot")]
1969    require_literal_leading_dot: Option<bool>,
1970  },
1971}
1972
1973impl Default for FsScope {
1974  fn default() -> Self {
1975    Self::AllowedPaths(Vec::new())
1976  }
1977}
1978
1979impl FsScope {
1980  /// The list of allowed paths.
1981  pub fn allowed_paths(&self) -> &Vec<PathBuf> {
1982    match self {
1983      Self::AllowedPaths(p) => p,
1984      Self::Scope { allow, .. } => allow,
1985    }
1986  }
1987
1988  /// The list of forbidden paths.
1989  pub fn forbidden_paths(&self) -> Option<&Vec<PathBuf>> {
1990    match self {
1991      Self::AllowedPaths(_) => None,
1992      Self::Scope { deny, .. } => Some(deny),
1993    }
1994  }
1995}
1996
1997/// Config for the asset custom protocol.
1998///
1999/// See more: <https://v2.tauri.app/reference/config/#assetprotocolconfig>
2000#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2001#[cfg_attr(feature = "schema", derive(JsonSchema))]
2002#[serde(rename_all = "camelCase", deny_unknown_fields)]
2003pub struct AssetProtocolConfig {
2004  /// The access scope for the asset protocol.
2005  #[serde(default)]
2006  pub scope: FsScope,
2007  /// Enables the asset protocol.
2008  #[serde(default)]
2009  pub enable: bool,
2010}
2011
2012/// definition of a header source
2013///
2014/// The header value to a header name
2015#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2016#[cfg_attr(feature = "schema", derive(JsonSchema))]
2017#[serde(rename_all = "camelCase", untagged)]
2018pub enum HeaderSource {
2019  /// string version of the header Value
2020  Inline(String),
2021  /// list version of the header value. Item are joined by "," for the real header value
2022  List(Vec<String>),
2023  /// (Rust struct | Json | JavaScript Object) equivalent of the header value. Items are composed from: key + space + value. Item are then joined by ";" for the real header value
2024  Map(HashMap<String, String>),
2025}
2026
2027impl Display for HeaderSource {
2028  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2029    match self {
2030      Self::Inline(s) => write!(f, "{s}"),
2031      Self::List(l) => write!(f, "{}", l.join(", ")),
2032      Self::Map(m) => {
2033        let len = m.len();
2034        let mut i = 0;
2035        for (key, value) in m {
2036          write!(f, "{} {}", key, value)?;
2037          i += 1;
2038          if i != len {
2039            write!(f, "; ")?;
2040          }
2041        }
2042        Ok(())
2043      }
2044    }
2045  }
2046}
2047
2048/// A trait which implements on the [`Builder`] of the http create
2049///
2050/// Must add headers defined in the tauri configuration file to http responses
2051pub trait HeaderAddition {
2052  /// adds all headers defined on the config file, given the current HeaderConfig
2053  fn add_configured_headers(self, headers: Option<&HeaderConfig>) -> http::response::Builder;
2054}
2055
2056impl HeaderAddition for Builder {
2057  /// Add the headers defined in the tauri configuration file to http responses
2058  ///
2059  /// this is a utility function, which is used in the same way as the `.header(..)` of the rust http library
2060  fn add_configured_headers(mut self, headers: Option<&HeaderConfig>) -> http::response::Builder {
2061    if let Some(headers) = headers {
2062      // Add the header Access-Control-Allow-Credentials, if we find a value for it
2063      if let Some(value) = &headers.access_control_allow_credentials {
2064        self = self.header("Access-Control-Allow-Credentials", value.to_string());
2065      };
2066
2067      // Add the header Access-Control-Allow-Headers, if we find a value for it
2068      if let Some(value) = &headers.access_control_allow_headers {
2069        self = self.header("Access-Control-Allow-Headers", value.to_string());
2070      };
2071
2072      // Add the header Access-Control-Allow-Methods, if we find a value for it
2073      if let Some(value) = &headers.access_control_allow_methods {
2074        self = self.header("Access-Control-Allow-Methods", value.to_string());
2075      };
2076
2077      // Add the header Access-Control-Expose-Headers, if we find a value for it
2078      if let Some(value) = &headers.access_control_expose_headers {
2079        self = self.header("Access-Control-Expose-Headers", value.to_string());
2080      };
2081
2082      // Add the header Access-Control-Max-Age, if we find a value for it
2083      if let Some(value) = &headers.access_control_max_age {
2084        self = self.header("Access-Control-Max-Age", value.to_string());
2085      };
2086
2087      // Add the header Cross-Origin-Embedder-Policy, if we find a value for it
2088      if let Some(value) = &headers.cross_origin_embedder_policy {
2089        self = self.header("Cross-Origin-Embedder-Policy", value.to_string());
2090      };
2091
2092      // Add the header Cross-Origin-Opener-Policy, if we find a value for it
2093      if let Some(value) = &headers.cross_origin_opener_policy {
2094        self = self.header("Cross-Origin-Opener-Policy", value.to_string());
2095      };
2096
2097      // Add the header Cross-Origin-Resource-Policy, if we find a value for it
2098      if let Some(value) = &headers.cross_origin_resource_policy {
2099        self = self.header("Cross-Origin-Resource-Policy", value.to_string());
2100      };
2101
2102      // Add the header Permission-Policy, if we find a value for it
2103      if let Some(value) = &headers.permissions_policy {
2104        self = self.header("Permission-Policy", value.to_string());
2105      };
2106
2107      // Add the header Timing-Allow-Origin, if we find a value for it
2108      if let Some(value) = &headers.timing_allow_origin {
2109        self = self.header("Timing-Allow-Origin", value.to_string());
2110      };
2111
2112      // Add the header X-Content-Type-Options, if we find a value for it
2113      if let Some(value) = &headers.x_content_type_options {
2114        self = self.header("X-Content-Type-Options", value.to_string());
2115      };
2116
2117      // Add the header Tauri-Custom-Header, if we find a value for it
2118      if let Some(value) = &headers.tauri_custom_header {
2119        // Keep in mind to correctly set the Access-Control-Expose-Headers
2120        self = self.header("Tauri-Custom-Header", value.to_string());
2121      };
2122    }
2123    self
2124  }
2125}
2126
2127/// A struct, where the keys are some specific http header names.
2128/// If the values to those keys are defined, then they will be send as part of a response message.
2129/// This does not include error messages and ipc messages
2130///
2131/// ## Example configuration
2132/// ```javascript
2133/// {
2134///  //..
2135///   app:{
2136///     //..
2137///     security: {
2138///       headers: {
2139///         "Cross-Origin-Opener-Policy": "same-origin",
2140///         "Cross-Origin-Embedder-Policy": "require-corp",
2141///         "Timing-Allow-Origin": [
2142///           "https://developer.mozilla.org",
2143///           "https://example.com",
2144///         ],
2145///         "Access-Control-Expose-Headers": "Tauri-Custom-Header",
2146///         "Tauri-Custom-Header": {
2147///           "key1": "'value1' 'value2'",
2148///           "key2": "'value3'"
2149///         }
2150///       },
2151///       csp: "default-src 'self'; connect-src ipc: http://ipc.localhost",
2152///     }
2153///     //..
2154///   }
2155///  //..
2156/// }
2157/// ```
2158/// In this example `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` are set to allow for the use of [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer).
2159/// The result is, that those headers are then set on every response sent via the `get_response` function in crates/tauri/src/protocol/tauri.rs.
2160/// The Content-Security-Policy header is defined separately, because it is also handled separately.
2161///
2162/// For the helloworld example, this config translates into those response headers:
2163/// ```http
2164/// access-control-allow-origin:  http://tauri.localhost
2165/// access-control-expose-headers: Tauri-Custom-Header
2166/// content-security-policy: default-src 'self'; connect-src ipc: http://ipc.localhost; script-src 'self' 'sha256-Wjjrs6qinmnr+tOry8x8PPwI77eGpUFR3EEGZktjJNs='
2167/// content-type: text/html
2168/// cross-origin-embedder-policy: require-corp
2169/// cross-origin-opener-policy: same-origin
2170/// tauri-custom-header: key1 'value1' 'value2'; key2 'value3'
2171/// timing-allow-origin: https://developer.mozilla.org, https://example.com
2172/// ```
2173/// Since the resulting header values are always 'string-like'. So depending on the what data type the HeaderSource is, they need to be converted.
2174///  - `String`(JS/Rust): stay the same for the resulting header value
2175///  - `Array`(JS)/`Vec\<String\>`(Rust): Item are joined by ", " for the resulting header value
2176///  - `Object`(JS)/ `Hashmap\<String,String\>`(Rust): Items are composed from: key + space + value. Item are then joined by "; " for the resulting header value
2177#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2178#[cfg_attr(feature = "schema", derive(JsonSchema))]
2179#[serde(deny_unknown_fields)]
2180pub struct HeaderConfig {
2181  /// The Access-Control-Allow-Credentials response header tells browsers whether the
2182  /// server allows cross-origin HTTP requests to include credentials.
2183  ///
2184  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials>
2185  #[serde(rename = "Access-Control-Allow-Credentials")]
2186  pub access_control_allow_credentials: Option<HeaderSource>,
2187  /// The Access-Control-Allow-Headers response header is used in response
2188  /// to a preflight request which includes the Access-Control-Request-Headers
2189  /// to indicate which HTTP headers can be used during the actual request.
2190  ///
2191  /// This header is required if the request has an Access-Control-Request-Headers header.
2192  ///
2193  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers>
2194  #[serde(rename = "Access-Control-Allow-Headers")]
2195  pub access_control_allow_headers: Option<HeaderSource>,
2196  /// The Access-Control-Allow-Methods response header specifies one or more methods
2197  /// allowed when accessing a resource in response to a preflight request.
2198  ///
2199  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods>
2200  #[serde(rename = "Access-Control-Allow-Methods")]
2201  pub access_control_allow_methods: Option<HeaderSource>,
2202  /// The Access-Control-Expose-Headers response header allows a server to indicate
2203  /// which response headers should be made available to scripts running in the browser,
2204  /// in response to a cross-origin request.
2205  ///
2206  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers>
2207  #[serde(rename = "Access-Control-Expose-Headers")]
2208  pub access_control_expose_headers: Option<HeaderSource>,
2209  /// The Access-Control-Max-Age response header indicates how long the results of a
2210  /// preflight request (that is the information contained in the
2211  /// Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can
2212  /// be cached.
2213  ///
2214  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age>
2215  #[serde(rename = "Access-Control-Max-Age")]
2216  pub access_control_max_age: Option<HeaderSource>,
2217  /// The HTTP Cross-Origin-Embedder-Policy (COEP) response header configures embedding
2218  /// cross-origin resources into the document.
2219  ///
2220  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy>
2221  #[serde(rename = "Cross-Origin-Embedder-Policy")]
2222  pub cross_origin_embedder_policy: Option<HeaderSource>,
2223  /// The HTTP Cross-Origin-Opener-Policy (COOP) response header allows you to ensure a
2224  /// top-level document does not share a browsing context group with cross-origin documents.
2225  /// COOP will process-isolate your document and potential attackers can't access your global
2226  /// object if they were to open it in a popup, preventing a set of cross-origin attacks dubbed XS-Leaks.
2227  ///
2228  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy>
2229  #[serde(rename = "Cross-Origin-Opener-Policy")]
2230  pub cross_origin_opener_policy: Option<HeaderSource>,
2231  /// The HTTP Cross-Origin-Resource-Policy response header conveys a desire that the
2232  /// browser blocks no-cors cross-origin/cross-site requests to the given resource.
2233  ///
2234  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy>
2235  #[serde(rename = "Cross-Origin-Resource-Policy")]
2236  pub cross_origin_resource_policy: Option<HeaderSource>,
2237  /// The HTTP Permissions-Policy header provides a mechanism to allow and deny the
2238  /// use of browser features in a document or within any \<iframe\> elements in the document.
2239  ///
2240  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy>
2241  #[serde(rename = "Permissions-Policy")]
2242  pub permissions_policy: Option<HeaderSource>,
2243  /// The Timing-Allow-Origin response header specifies origins that are allowed to see values
2244  /// of attributes retrieved via features of the Resource Timing API, which would otherwise be
2245  /// reported as zero due to cross-origin restrictions.
2246  ///
2247  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin>
2248  #[serde(rename = "Timing-Allow-Origin")]
2249  pub timing_allow_origin: Option<HeaderSource>,
2250  /// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate
2251  /// that the MIME types advertised in the Content-Type headers should be followed and not be
2252  /// changed. The header allows you to avoid MIME type sniffing by saying that the MIME types
2253  /// are deliberately configured.
2254  ///
2255  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options>
2256  #[serde(rename = "X-Content-Type-Options")]
2257  pub x_content_type_options: Option<HeaderSource>,
2258  /// A custom header field Tauri-Custom-Header, don't use it.
2259  /// Remember to set Access-Control-Expose-Headers accordingly
2260  ///
2261  /// **NOT INTENDED FOR PRODUCTION USE**
2262  #[serde(rename = "Tauri-Custom-Header")]
2263  pub tauri_custom_header: Option<HeaderSource>,
2264}
2265
2266impl HeaderConfig {
2267  /// creates a new header config
2268  pub fn new() -> Self {
2269    HeaderConfig {
2270      access_control_allow_credentials: None,
2271      access_control_allow_methods: None,
2272      access_control_allow_headers: None,
2273      access_control_expose_headers: None,
2274      access_control_max_age: None,
2275      cross_origin_embedder_policy: None,
2276      cross_origin_opener_policy: None,
2277      cross_origin_resource_policy: None,
2278      permissions_policy: None,
2279      timing_allow_origin: None,
2280      x_content_type_options: None,
2281      tauri_custom_header: None,
2282    }
2283  }
2284}
2285
2286/// Security configuration.
2287///
2288/// See more: <https://v2.tauri.app/reference/config/#securityconfig>
2289#[skip_serializing_none]
2290#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2291#[cfg_attr(feature = "schema", derive(JsonSchema))]
2292#[serde(rename_all = "camelCase", deny_unknown_fields)]
2293pub struct SecurityConfig {
2294  /// The Content Security Policy that will be injected on all HTML files on the built application.
2295  /// If [`dev_csp`](#SecurityConfig.devCsp) is not specified, this value is also injected on dev.
2296  ///
2297  /// This is a really important part of the configuration since it helps you ensure your WebView is secured.
2298  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
2299  pub csp: Option<Csp>,
2300  /// The Content Security Policy that will be injected on all HTML files on development.
2301  ///
2302  /// This is a really important part of the configuration since it helps you ensure your WebView is secured.
2303  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
2304  #[serde(alias = "dev-csp")]
2305  pub dev_csp: Option<Csp>,
2306  /// Freeze the `Object.prototype` when using the custom protocol.
2307  #[serde(default, alias = "freeze-prototype")]
2308  pub freeze_prototype: bool,
2309  /// Disables the Tauri-injected CSP sources.
2310  ///
2311  /// At compile time, Tauri parses all the frontend assets and changes the Content-Security-Policy
2312  /// to only allow loading of your own scripts and styles by injecting nonce and hash sources.
2313  /// This stricts your CSP, which may introduce issues when using along with other flexing sources.
2314  ///
2315  /// This configuration option allows both a boolean and a list of strings as value.
2316  /// A boolean instructs Tauri to disable the injection for all CSP injections,
2317  /// and a list of strings indicates the CSP directives that Tauri cannot inject.
2318  ///
2319  /// **WARNING:** Only disable this if you know what you are doing and have properly configured the CSP.
2320  /// Your application might be vulnerable to XSS attacks without this Tauri protection.
2321  #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
2322  pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
2323  /// Custom protocol config.
2324  #[serde(default, alias = "asset-protocol")]
2325  pub asset_protocol: AssetProtocolConfig,
2326  /// The pattern to use.
2327  #[serde(default)]
2328  pub pattern: PatternKind,
2329  /// List of capabilities that are enabled on the application.
2330  ///
2331  /// If the list is empty, all capabilities are included.
2332  #[serde(default)]
2333  pub capabilities: Vec<CapabilityEntry>,
2334  /// The headers, which are added to every http response from tauri to the web view
2335  /// This doesn't include IPC Messages and error responses
2336  #[serde(default)]
2337  pub headers: Option<HeaderConfig>,
2338}
2339
2340/// A capability entry which can be either an inlined capability or a reference to a capability defined on its own file.
2341#[derive(Debug, Clone, PartialEq, Serialize)]
2342#[cfg_attr(feature = "schema", derive(JsonSchema))]
2343#[serde(untagged)]
2344pub enum CapabilityEntry {
2345  /// An inlined capability.
2346  Inlined(Capability),
2347  /// Reference to a capability identifier.
2348  Reference(String),
2349}
2350
2351impl<'de> Deserialize<'de> for CapabilityEntry {
2352  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2353  where
2354    D: Deserializer<'de>,
2355  {
2356    UntaggedEnumVisitor::new()
2357      .string(|string| Ok(Self::Reference(string.to_owned())))
2358      .map(|map| map.deserialize::<Capability>().map(Self::Inlined))
2359      .deserialize(deserializer)
2360  }
2361}
2362
2363/// The application pattern.
2364#[skip_serializing_none]
2365#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
2366#[serde(rename_all = "lowercase", tag = "use", content = "options")]
2367#[cfg_attr(feature = "schema", derive(JsonSchema))]
2368pub enum PatternKind {
2369  /// Brownfield pattern.
2370  Brownfield,
2371  /// Isolation pattern. Recommended for security purposes.
2372  Isolation {
2373    /// The dir containing the index.html file that contains the secure isolation application.
2374    dir: PathBuf,
2375  },
2376}
2377
2378impl Default for PatternKind {
2379  fn default() -> Self {
2380    Self::Brownfield
2381  }
2382}
2383
2384/// The App configuration object.
2385///
2386/// See more: <https://v2.tauri.app/reference/config/#appconfig>
2387#[skip_serializing_none]
2388#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2389#[cfg_attr(feature = "schema", derive(JsonSchema))]
2390#[serde(rename_all = "camelCase", deny_unknown_fields)]
2391pub struct AppConfig {
2392  /// The app windows configuration.
2393  #[serde(default)]
2394  pub windows: Vec<WindowConfig>,
2395  /// Security configuration.
2396  #[serde(default)]
2397  pub security: SecurityConfig,
2398  /// Configuration for app tray icon.
2399  #[serde(alias = "tray-icon")]
2400  pub tray_icon: Option<TrayIconConfig>,
2401  /// MacOS private API configuration. Enables the transparent background API and sets the `fullScreenEnabled` preference to `true`.
2402  #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
2403  pub macos_private_api: bool,
2404  /// Whether we should inject the Tauri API on `window.__TAURI__` or not.
2405  #[serde(default, alias = "with-global-tauri")]
2406  pub with_global_tauri: bool,
2407  /// If set to true "identifier" will be set as GTK app ID (on systems that use GTK).
2408  #[serde(rename = "enableGTKAppId", alias = "enable-gtk-app-id", default)]
2409  pub enable_gtk_app_id: bool,
2410}
2411
2412impl AppConfig {
2413  /// Returns all Cargo features.
2414  pub fn all_features() -> Vec<&'static str> {
2415    vec![
2416      "tray-icon",
2417      "macos-private-api",
2418      "protocol-asset",
2419      "isolation",
2420    ]
2421  }
2422
2423  /// Returns the enabled Cargo features.
2424  pub fn features(&self) -> Vec<&str> {
2425    let mut features = Vec::new();
2426    if self.tray_icon.is_some() {
2427      features.push("tray-icon");
2428    }
2429    if self.macos_private_api {
2430      features.push("macos-private-api");
2431    }
2432    if self.security.asset_protocol.enable {
2433      features.push("protocol-asset");
2434    }
2435
2436    if let PatternKind::Isolation { .. } = self.security.pattern {
2437      features.push("isolation");
2438    }
2439
2440    features.sort_unstable();
2441    features
2442  }
2443}
2444
2445/// Configuration for application tray icon.
2446///
2447/// See more: <https://v2.tauri.app/reference/config/#trayiconconfig>
2448#[skip_serializing_none]
2449#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2450#[cfg_attr(feature = "schema", derive(JsonSchema))]
2451#[serde(rename_all = "camelCase", deny_unknown_fields)]
2452pub struct TrayIconConfig {
2453  /// Set an id for this tray icon so you can reference it later, defaults to `main`.
2454  pub id: Option<String>,
2455  /// Path to the default icon to use for the tray icon.
2456  ///
2457  /// Note: this stores the image in raw pixels to the final binary,
2458  /// so keep the icon size (width and height) small
2459  /// or else it's going to bloat your final executable
2460  #[serde(alias = "icon-path")]
2461  pub icon_path: PathBuf,
2462  /// A Boolean value that determines whether the image represents a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc) image on macOS.
2463  #[serde(default, alias = "icon-as-template")]
2464  pub icon_as_template: bool,
2465  /// A Boolean value that determines whether the menu should appear when the tray icon receives a left click.
2466  ///
2467  /// ## Platform-specific:
2468  ///
2469  /// - **Linux**: Unsupported.
2470  #[serde(default = "default_true", alias = "menu-on-left-click")]
2471  #[deprecated(since = "2.2.0", note = "Use `show_menu_on_left_click` instead.")]
2472  pub menu_on_left_click: bool,
2473  /// A Boolean value that determines whether the menu should appear when the tray icon receives a left click.
2474  ///
2475  /// ## Platform-specific:
2476  ///
2477  /// - **Linux**: Unsupported.
2478  #[serde(default = "default_true", alias = "show-menu-on-left-click")]
2479  pub show_menu_on_left_click: bool,
2480  /// Title for MacOS tray
2481  pub title: Option<String>,
2482  /// Tray icon tooltip on Windows and macOS
2483  pub tooltip: Option<String>,
2484}
2485
2486/// General configuration for the iOS target.
2487#[skip_serializing_none]
2488#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2489#[cfg_attr(feature = "schema", derive(JsonSchema))]
2490#[serde(rename_all = "camelCase", deny_unknown_fields)]
2491pub struct IosConfig {
2492  /// A custom [XcodeGen] project.yml template to use.
2493  ///
2494  /// [XcodeGen]: <https://github.com/yonaskolb/XcodeGen>
2495  pub template: Option<PathBuf>,
2496  /// A list of strings indicating any iOS frameworks that need to be bundled with the application.
2497  ///
2498  /// Note that you need to recreate the iOS project for the changes to be applied.
2499  pub frameworks: Option<Vec<String>>,
2500  /// The development team. This value is required for iOS development because code signing is enforced.
2501  /// The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.
2502  #[serde(alias = "development-team")]
2503  pub development_team: Option<String>,
2504  /// A version string indicating the minimum iOS version that the bundled application supports. Defaults to `13.0`.
2505  ///
2506  /// Maps to the IPHONEOS_DEPLOYMENT_TARGET value.
2507  #[serde(
2508    alias = "minimum-system-version",
2509    default = "ios_minimum_system_version"
2510  )]
2511  pub minimum_system_version: String,
2512}
2513
2514impl Default for IosConfig {
2515  fn default() -> Self {
2516    Self {
2517      template: None,
2518      frameworks: None,
2519      development_team: None,
2520      minimum_system_version: ios_minimum_system_version(),
2521    }
2522  }
2523}
2524
2525/// General configuration for the Android target.
2526#[skip_serializing_none]
2527#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2528#[cfg_attr(feature = "schema", derive(JsonSchema))]
2529#[serde(rename_all = "camelCase", deny_unknown_fields)]
2530pub struct AndroidConfig {
2531  /// The minimum API level required for the application to run.
2532  /// The Android system will prevent the user from installing the application if the system's API level is lower than the value specified.
2533  #[serde(alias = "min-sdk-version", default = "default_min_sdk_version")]
2534  pub min_sdk_version: u32,
2535
2536  /// The version code of the application.
2537  /// It is limited to 2,100,000,000 as per Google Play Store requirements.
2538  ///
2539  /// By default we use your configured version and perform the following math:
2540  /// versionCode = version.major * 1000000 + version.minor * 1000 + version.patch
2541  #[serde(alias = "version-code")]
2542  #[cfg_attr(feature = "schema", validate(range(min = 1, max = 2_100_000_000)))]
2543  pub version_code: Option<u32>,
2544}
2545
2546impl Default for AndroidConfig {
2547  fn default() -> Self {
2548    Self {
2549      min_sdk_version: default_min_sdk_version(),
2550      version_code: None,
2551    }
2552  }
2553}
2554
2555fn default_min_sdk_version() -> u32 {
2556  24
2557}
2558
2559/// Defines the URL or assets to embed in the application.
2560#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2561#[cfg_attr(feature = "schema", derive(JsonSchema))]
2562#[serde(untagged, deny_unknown_fields)]
2563#[non_exhaustive]
2564pub enum FrontendDist {
2565  /// An external URL that should be used as the default application URL.
2566  Url(Url),
2567  /// Path to a directory containing the frontend dist assets.
2568  Directory(PathBuf),
2569  /// An array of files to embed on the app.
2570  Files(Vec<PathBuf>),
2571}
2572
2573impl std::fmt::Display for FrontendDist {
2574  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2575    match self {
2576      Self::Url(url) => write!(f, "{url}"),
2577      Self::Directory(p) => write!(f, "{}", p.display()),
2578      Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
2579    }
2580  }
2581}
2582
2583/// Describes the shell command to run before `tauri dev`.
2584#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2585#[cfg_attr(feature = "schema", derive(JsonSchema))]
2586#[serde(rename_all = "camelCase", untagged)]
2587pub enum BeforeDevCommand {
2588  /// Run the given script with the default options.
2589  Script(String),
2590  /// Run the given script with custom options.
2591  ScriptWithOptions {
2592    /// The script to execute.
2593    script: String,
2594    /// The current working directory.
2595    cwd: Option<String>,
2596    /// Whether `tauri dev` should wait for the command to finish or not. Defaults to `false`.
2597    #[serde(default)]
2598    wait: bool,
2599  },
2600}
2601
2602/// Describes a shell command to be executed when a CLI hook is triggered.
2603#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2604#[cfg_attr(feature = "schema", derive(JsonSchema))]
2605#[serde(rename_all = "camelCase", untagged)]
2606pub enum HookCommand {
2607  /// Run the given script with the default options.
2608  Script(String),
2609  /// Run the given script with custom options.
2610  ScriptWithOptions {
2611    /// The script to execute.
2612    script: String,
2613    /// The current working directory.
2614    cwd: Option<String>,
2615  },
2616}
2617
2618/// The Build configuration object.
2619///
2620/// See more: <https://v2.tauri.app/reference/config/#buildconfig>
2621#[skip_serializing_none]
2622#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
2623#[cfg_attr(feature = "schema", derive(JsonSchema))]
2624#[serde(rename_all = "camelCase", deny_unknown_fields)]
2625pub struct BuildConfig {
2626  /// The binary used to build and run the application.
2627  pub runner: Option<String>,
2628  /// The URL to load in development.
2629  ///
2630  /// This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.
2631  /// Most modern JavaScript bundlers like [Vite](https://vite.dev/guide/) provides a way to start a dev server by default.
2632  ///
2633  /// If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)
2634  /// and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.
2635  #[serde(alias = "dev-url")]
2636  pub dev_url: Option<Url>,
2637  /// The path to the application assets (usually the `dist` folder of your javascript bundler)
2638  /// or a URL that could be either a custom protocol registered in the tauri app (for example: `myprotocol://`)
2639  /// or a remote URL (for example: `https://site.com/app`).
2640  ///
2641  /// When a path relative to the configuration file is provided,
2642  /// it is read recursively and all files are embedded in the application binary.
2643  /// Tauri then looks for an `index.html` and serves it as the default entry point for your application.
2644  ///
2645  /// You can also provide a list of paths to be embedded, which allows granular control over what files are added to the binary.
2646  /// In this case, all files are added to the root and you must reference it that way in your HTML files.
2647  ///
2648  /// When a URL is provided, the application won't have bundled assets
2649  /// and the application will load that URL by default.
2650  #[serde(alias = "frontend-dist")]
2651  pub frontend_dist: Option<FrontendDist>,
2652  /// A shell command to run before `tauri dev` kicks in.
2653  ///
2654  /// The TAURI_ENV_PLATFORM, TAURI_ENV_ARCH, TAURI_ENV_FAMILY, TAURI_ENV_PLATFORM_VERSION, TAURI_ENV_PLATFORM_TYPE and TAURI_ENV_DEBUG environment variables are set if you perform conditional compilation.
2655  #[serde(alias = "before-dev-command")]
2656  pub before_dev_command: Option<BeforeDevCommand>,
2657  /// A shell command to run before `tauri build` kicks in.
2658  ///
2659  /// The TAURI_ENV_PLATFORM, TAURI_ENV_ARCH, TAURI_ENV_FAMILY, TAURI_ENV_PLATFORM_VERSION, TAURI_ENV_PLATFORM_TYPE and TAURI_ENV_DEBUG environment variables are set if you perform conditional compilation.
2660  #[serde(alias = "before-build-command")]
2661  pub before_build_command: Option<HookCommand>,
2662  /// A shell command to run before the bundling phase in `tauri build` kicks in.
2663  ///
2664  /// The TAURI_ENV_PLATFORM, TAURI_ENV_ARCH, TAURI_ENV_FAMILY, TAURI_ENV_PLATFORM_VERSION, TAURI_ENV_PLATFORM_TYPE and TAURI_ENV_DEBUG environment variables are set if you perform conditional compilation.
2665  #[serde(alias = "before-bundle-command")]
2666  pub before_bundle_command: Option<HookCommand>,
2667  /// Features passed to `cargo` commands.
2668  pub features: Option<Vec<String>>,
2669}
2670
2671#[derive(Debug, PartialEq, Eq)]
2672struct PackageVersion(String);
2673
2674impl<'d> serde::Deserialize<'d> for PackageVersion {
2675  fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
2676    struct PackageVersionVisitor;
2677
2678    impl Visitor<'_> for PackageVersionVisitor {
2679      type Value = PackageVersion;
2680
2681      fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2682        write!(
2683          formatter,
2684          "a semver string or a path to a package.json file"
2685        )
2686      }
2687
2688      fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
2689        let path = PathBuf::from(value);
2690        if path.exists() {
2691          let json_str = read_to_string(&path)
2692            .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2693          let package_json: serde_json::Value = serde_json::from_str(&json_str)
2694            .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2695          if let Some(obj) = package_json.as_object() {
2696            let version = obj
2697              .get("version")
2698              .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
2699              .as_str()
2700              .ok_or_else(|| {
2701                DeError::custom(format!("`{} > version` must be a string", path.display()))
2702              })?;
2703            Ok(PackageVersion(
2704              Version::from_str(version)
2705                .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2706                .to_string(),
2707            ))
2708          } else {
2709            Err(DeError::custom(
2710              "`package > version` value is not a path to a JSON object",
2711            ))
2712          }
2713        } else {
2714          Ok(PackageVersion(
2715            Version::from_str(value)
2716              .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2717              .to_string(),
2718          ))
2719        }
2720      }
2721    }
2722
2723    deserializer.deserialize_string(PackageVersionVisitor {})
2724  }
2725}
2726
2727fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
2728where
2729  D: Deserializer<'de>,
2730{
2731  Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
2732}
2733
2734/// The Tauri configuration object.
2735/// It is read from a file where you can define your frontend assets,
2736/// configure the bundler and define a tray icon.
2737///
2738/// The configuration file is generated by the
2739/// [`tauri init`](https://v2.tauri.app/reference/cli/#init) command that lives in
2740/// your Tauri application source directory (src-tauri).
2741///
2742/// Once generated, you may modify it at will to customize your Tauri application.
2743///
2744/// ## File Formats
2745///
2746/// By default, the configuration is defined as a JSON file named `tauri.conf.json`.
2747///
2748/// Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.
2749/// The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.
2750/// The TOML file name is `Tauri.toml`.
2751///
2752/// ## Platform-Specific Configuration
2753///
2754/// In addition to the default configuration file, Tauri can
2755/// read a platform-specific configuration from `tauri.linux.conf.json`,
2756/// `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`
2757/// (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),
2758/// which gets merged with the main configuration object.
2759///
2760/// ## Configuration Structure
2761///
2762/// The configuration is composed of the following objects:
2763///
2764/// - [`app`](#appconfig): The Tauri configuration
2765/// - [`build`](#buildconfig): The build configuration
2766/// - [`bundle`](#bundleconfig): The bundle configurations
2767/// - [`plugins`](#pluginconfig): The plugins configuration
2768///
2769/// Example tauri.config.json file:
2770///
2771/// ```json
2772/// {
2773///   "productName": "tauri-app",
2774///   "version": "0.1.0",
2775///   "build": {
2776///     "beforeBuildCommand": "",
2777///     "beforeDevCommand": "",
2778///     "devUrl": "http://localhost:3000",
2779///     "frontendDist": "../dist"
2780///   },
2781///   "app": {
2782///     "security": {
2783///       "csp": null
2784///     },
2785///     "windows": [
2786///       {
2787///         "fullscreen": false,
2788///         "height": 600,
2789///         "resizable": true,
2790///         "title": "Tauri App",
2791///         "width": 800
2792///       }
2793///     ]
2794///   },
2795///   "bundle": {},
2796///   "plugins": {}
2797/// }
2798/// ```
2799#[skip_serializing_none]
2800#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2801#[cfg_attr(feature = "schema", derive(JsonSchema))]
2802#[serde(rename_all = "camelCase", deny_unknown_fields)]
2803pub struct Config {
2804  /// The JSON schema for the Tauri config.
2805  #[serde(rename = "$schema")]
2806  pub schema: Option<String>,
2807  /// App name.
2808  #[serde(alias = "product-name")]
2809  #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
2810  pub product_name: Option<String>,
2811  /// App main binary filename. Defaults to the name of your cargo crate.
2812  #[serde(alias = "main-binary-name")]
2813  pub main_binary_name: Option<String>,
2814  /// App version. It is a semver version number or a path to a `package.json` file containing the `version` field. If removed the version number from `Cargo.toml` is used.
2815  ///
2816  /// By default version 1.0 is used on Android.
2817  #[serde(deserialize_with = "version_deserializer", default)]
2818  pub version: Option<String>,
2819  /// The application identifier in reverse domain name notation (e.g. `com.tauri.example`).
2820  /// This string must be unique across applications since it is used in system configurations like
2821  /// the bundle ID and path to the webview data directory.
2822  /// This string must contain only alphanumeric characters (A-Z, a-z, and 0-9), hyphens (-),
2823  /// and periods (.).
2824  pub identifier: String,
2825  /// The App configuration.
2826  #[serde(default)]
2827  pub app: AppConfig,
2828  /// The build configuration.
2829  #[serde(default = "default_build")]
2830  pub build: BuildConfig,
2831  /// The bundler configuration.
2832  #[serde(default)]
2833  pub bundle: BundleConfig,
2834  /// The plugins config.
2835  #[serde(default)]
2836  pub plugins: PluginConfig,
2837}
2838
2839/// The plugin configs holds a HashMap mapping a plugin name to its configuration object.
2840///
2841/// See more: <https://v2.tauri.app/reference/config/#pluginconfig>
2842#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
2843#[cfg_attr(feature = "schema", derive(JsonSchema))]
2844pub struct PluginConfig(pub HashMap<String, JsonValue>);
2845
2846fn default_build() -> BuildConfig {
2847  BuildConfig {
2848    runner: None,
2849    dev_url: None,
2850    frontend_dist: None,
2851    before_dev_command: None,
2852    before_build_command: None,
2853    before_bundle_command: None,
2854    features: None,
2855  }
2856}
2857
2858/// Implement `ToTokens` for all config structs, allowing a literal `Config` to be built.
2859///
2860/// This allows for a build script to output the values in a `Config` to a `TokenStream`, which can
2861/// then be consumed by another crate. Useful for passing a config to both the build script and the
2862/// application using tauri while only parsing it once (in the build script).
2863#[cfg(feature = "build")]
2864mod build {
2865  use super::*;
2866  use crate::{literal_struct, tokens::*};
2867  use proc_macro2::TokenStream;
2868  use quote::{quote, ToTokens, TokenStreamExt};
2869  use std::convert::identity;
2870
2871  impl ToTokens for WebviewUrl {
2872    fn to_tokens(&self, tokens: &mut TokenStream) {
2873      let prefix = quote! { ::tauri::utils::config::WebviewUrl };
2874
2875      tokens.append_all(match self {
2876        Self::App(path) => {
2877          let path = path_buf_lit(path);
2878          quote! { #prefix::App(#path) }
2879        }
2880        Self::External(url) => {
2881          let url = url_lit(url);
2882          quote! { #prefix::External(#url) }
2883        }
2884        Self::CustomProtocol(url) => {
2885          let url = url_lit(url);
2886          quote! { #prefix::CustomProtocol(#url) }
2887        }
2888      })
2889    }
2890  }
2891
2892  impl ToTokens for BackgroundThrottlingPolicy {
2893    fn to_tokens(&self, tokens: &mut TokenStream) {
2894      let prefix = quote! { ::tauri::utils::config::BackgroundThrottlingPolicy };
2895      tokens.append_all(match self {
2896        Self::Disabled => quote! { #prefix::Disabled },
2897        Self::Throttle => quote! { #prefix::Throttle },
2898        Self::Suspend => quote! { #prefix::Suspend },
2899      })
2900    }
2901  }
2902
2903  impl ToTokens for crate::Theme {
2904    fn to_tokens(&self, tokens: &mut TokenStream) {
2905      let prefix = quote! { ::tauri::utils::Theme };
2906
2907      tokens.append_all(match self {
2908        Self::Light => quote! { #prefix::Light },
2909        Self::Dark => quote! { #prefix::Dark },
2910      })
2911    }
2912  }
2913
2914  impl ToTokens for Color {
2915    fn to_tokens(&self, tokens: &mut TokenStream) {
2916      let Color(r, g, b, a) = self;
2917      tokens.append_all(quote! {::tauri::utils::config::Color(#r,#g,#b,#a)});
2918    }
2919  }
2920  impl ToTokens for WindowEffectsConfig {
2921    fn to_tokens(&self, tokens: &mut TokenStream) {
2922      let effects = vec_lit(self.effects.clone(), |d| d);
2923      let state = opt_lit(self.state.as_ref());
2924      let radius = opt_lit(self.radius.as_ref());
2925      let color = opt_lit(self.color.as_ref());
2926
2927      literal_struct!(
2928        tokens,
2929        ::tauri::utils::config::WindowEffectsConfig,
2930        effects,
2931        state,
2932        radius,
2933        color
2934      )
2935    }
2936  }
2937
2938  impl ToTokens for crate::TitleBarStyle {
2939    fn to_tokens(&self, tokens: &mut TokenStream) {
2940      let prefix = quote! { ::tauri::utils::TitleBarStyle };
2941
2942      tokens.append_all(match self {
2943        Self::Visible => quote! { #prefix::Visible },
2944        Self::Transparent => quote! { #prefix::Transparent },
2945        Self::Overlay => quote! { #prefix::Overlay },
2946      })
2947    }
2948  }
2949
2950  impl ToTokens for crate::WindowEffect {
2951    fn to_tokens(&self, tokens: &mut TokenStream) {
2952      let prefix = quote! { ::tauri::utils::WindowEffect };
2953
2954      #[allow(deprecated)]
2955      tokens.append_all(match self {
2956        WindowEffect::AppearanceBased => quote! { #prefix::AppearanceBased},
2957        WindowEffect::Light => quote! { #prefix::Light},
2958        WindowEffect::Dark => quote! { #prefix::Dark},
2959        WindowEffect::MediumLight => quote! { #prefix::MediumLight},
2960        WindowEffect::UltraDark => quote! { #prefix::UltraDark},
2961        WindowEffect::Titlebar => quote! { #prefix::Titlebar},
2962        WindowEffect::Selection => quote! { #prefix::Selection},
2963        WindowEffect::Menu => quote! { #prefix::Menu},
2964        WindowEffect::Popover => quote! { #prefix::Popover},
2965        WindowEffect::Sidebar => quote! { #prefix::Sidebar},
2966        WindowEffect::HeaderView => quote! { #prefix::HeaderView},
2967        WindowEffect::Sheet => quote! { #prefix::Sheet},
2968        WindowEffect::WindowBackground => quote! { #prefix::WindowBackground},
2969        WindowEffect::HudWindow => quote! { #prefix::HudWindow},
2970        WindowEffect::FullScreenUI => quote! { #prefix::FullScreenUI},
2971        WindowEffect::Tooltip => quote! { #prefix::Tooltip},
2972        WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
2973        WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
2974        WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
2975        WindowEffect::Mica => quote! { #prefix::Mica},
2976        WindowEffect::MicaDark => quote! { #prefix::MicaDark},
2977        WindowEffect::MicaLight => quote! { #prefix::MicaLight},
2978        WindowEffect::Blur => quote! { #prefix::Blur},
2979        WindowEffect::Acrylic => quote! { #prefix::Acrylic},
2980        WindowEffect::Tabbed => quote! { #prefix::Tabbed },
2981        WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
2982        WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
2983      })
2984    }
2985  }
2986
2987  impl ToTokens for crate::WindowEffectState {
2988    fn to_tokens(&self, tokens: &mut TokenStream) {
2989      let prefix = quote! { ::tauri::utils::WindowEffectState };
2990
2991      #[allow(deprecated)]
2992      tokens.append_all(match self {
2993        WindowEffectState::Active => quote! { #prefix::Active},
2994        WindowEffectState::FollowsWindowActiveState => quote! { #prefix::FollowsWindowActiveState},
2995        WindowEffectState::Inactive => quote! { #prefix::Inactive},
2996      })
2997    }
2998  }
2999
3000  impl ToTokens for WindowConfig {
3001    fn to_tokens(&self, tokens: &mut TokenStream) {
3002      let label = str_lit(&self.label);
3003      let create = &self.create;
3004      let url = &self.url;
3005      let user_agent = opt_str_lit(self.user_agent.as_ref());
3006      let drag_drop_enabled = self.drag_drop_enabled;
3007      let center = self.center;
3008      let x = opt_lit(self.x.as_ref());
3009      let y = opt_lit(self.y.as_ref());
3010      let width = self.width;
3011      let height = self.height;
3012      let min_width = opt_lit(self.min_width.as_ref());
3013      let min_height = opt_lit(self.min_height.as_ref());
3014      let max_width = opt_lit(self.max_width.as_ref());
3015      let max_height = opt_lit(self.max_height.as_ref());
3016      let resizable = self.resizable;
3017      let maximizable = self.maximizable;
3018      let minimizable = self.minimizable;
3019      let closable = self.closable;
3020      let title = str_lit(&self.title);
3021      let proxy_url = opt_lit(self.proxy_url.as_ref().map(url_lit).as_ref());
3022      let fullscreen = self.fullscreen;
3023      let focus = self.focus;
3024      let transparent = self.transparent;
3025      let maximized = self.maximized;
3026      let visible = self.visible;
3027      let decorations = self.decorations;
3028      let always_on_bottom = self.always_on_bottom;
3029      let always_on_top = self.always_on_top;
3030      let visible_on_all_workspaces = self.visible_on_all_workspaces;
3031      let content_protected = self.content_protected;
3032      let skip_taskbar = self.skip_taskbar;
3033      let window_classname = opt_str_lit(self.window_classname.as_ref());
3034      let theme = opt_lit(self.theme.as_ref());
3035      let title_bar_style = &self.title_bar_style;
3036      let hidden_title = self.hidden_title;
3037      let accept_first_mouse = self.accept_first_mouse;
3038      let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
3039      let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
3040      let shadow = self.shadow;
3041      let window_effects = opt_lit(self.window_effects.as_ref());
3042      let incognito = self.incognito;
3043      let parent = opt_str_lit(self.parent.as_ref());
3044      let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled;
3045      let browser_extensions_enabled = self.browser_extensions_enabled;
3046      let use_https_scheme = self.use_https_scheme;
3047      let devtools = opt_lit(self.devtools.as_ref());
3048      let background_color = opt_lit(self.background_color.as_ref());
3049      let background_throttling = opt_lit(self.background_throttling.as_ref());
3050
3051      literal_struct!(
3052        tokens,
3053        ::tauri::utils::config::WindowConfig,
3054        label,
3055        url,
3056        create,
3057        user_agent,
3058        drag_drop_enabled,
3059        center,
3060        x,
3061        y,
3062        width,
3063        height,
3064        min_width,
3065        min_height,
3066        max_width,
3067        max_height,
3068        resizable,
3069        maximizable,
3070        minimizable,
3071        closable,
3072        title,
3073        proxy_url,
3074        fullscreen,
3075        focus,
3076        transparent,
3077        maximized,
3078        visible,
3079        decorations,
3080        always_on_bottom,
3081        always_on_top,
3082        visible_on_all_workspaces,
3083        content_protected,
3084        skip_taskbar,
3085        window_classname,
3086        theme,
3087        title_bar_style,
3088        hidden_title,
3089        accept_first_mouse,
3090        tabbing_identifier,
3091        additional_browser_args,
3092        shadow,
3093        window_effects,
3094        incognito,
3095        parent,
3096        zoom_hotkeys_enabled,
3097        browser_extensions_enabled,
3098        use_https_scheme,
3099        devtools,
3100        background_color,
3101        background_throttling
3102      );
3103    }
3104  }
3105
3106  impl ToTokens for PatternKind {
3107    fn to_tokens(&self, tokens: &mut TokenStream) {
3108      let prefix = quote! { ::tauri::utils::config::PatternKind };
3109
3110      tokens.append_all(match self {
3111        Self::Brownfield => quote! { #prefix::Brownfield },
3112        #[cfg(not(feature = "isolation"))]
3113        Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
3114        #[cfg(feature = "isolation")]
3115        Self::Isolation { dir } => {
3116          let dir = path_buf_lit(dir);
3117          quote! { #prefix::Isolation { dir: #dir } }
3118        }
3119      })
3120    }
3121  }
3122
3123  impl ToTokens for WebviewInstallMode {
3124    fn to_tokens(&self, tokens: &mut TokenStream) {
3125      let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
3126
3127      tokens.append_all(match self {
3128        Self::Skip => quote! { #prefix::Skip },
3129        Self::DownloadBootstrapper { silent } => {
3130          quote! { #prefix::DownloadBootstrapper { silent: #silent } }
3131        }
3132        Self::EmbedBootstrapper { silent } => {
3133          quote! { #prefix::EmbedBootstrapper { silent: #silent } }
3134        }
3135        Self::OfflineInstaller { silent } => {
3136          quote! { #prefix::OfflineInstaller { silent: #silent } }
3137        }
3138        Self::FixedRuntime { path } => {
3139          let path = path_buf_lit(path);
3140          quote! { #prefix::FixedRuntime { path: #path } }
3141        }
3142      })
3143    }
3144  }
3145
3146  impl ToTokens for WindowsConfig {
3147    fn to_tokens(&self, tokens: &mut TokenStream) {
3148      let webview_install_mode = &self.webview_install_mode;
3149      tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
3150        webview_install_mode: #webview_install_mode,
3151        ..Default::default()
3152      }})
3153    }
3154  }
3155
3156  impl ToTokens for BundleConfig {
3157    fn to_tokens(&self, tokens: &mut TokenStream) {
3158      let publisher = quote!(None);
3159      let homepage = quote!(None);
3160      let icon = vec_lit(&self.icon, str_lit);
3161      let active = self.active;
3162      let targets = quote!(Default::default());
3163      let create_updater_artifacts = quote!(Default::default());
3164      let resources = quote!(None);
3165      let copyright = quote!(None);
3166      let category = quote!(None);
3167      let file_associations = quote!(None);
3168      let short_description = quote!(None);
3169      let long_description = quote!(None);
3170      let use_local_tools_dir = self.use_local_tools_dir;
3171      let external_bin = opt_vec_lit(self.external_bin.as_ref(), str_lit);
3172      let windows = &self.windows;
3173      let license = opt_str_lit(self.license.as_ref());
3174      let license_file = opt_lit(self.license_file.as_ref().map(path_buf_lit).as_ref());
3175      let linux = quote!(Default::default());
3176      let macos = quote!(Default::default());
3177      let ios = quote!(Default::default());
3178      let android = quote!(Default::default());
3179
3180      literal_struct!(
3181        tokens,
3182        ::tauri::utils::config::BundleConfig,
3183        active,
3184        publisher,
3185        homepage,
3186        icon,
3187        targets,
3188        create_updater_artifacts,
3189        resources,
3190        copyright,
3191        category,
3192        license,
3193        license_file,
3194        file_associations,
3195        short_description,
3196        long_description,
3197        use_local_tools_dir,
3198        external_bin,
3199        windows,
3200        linux,
3201        macos,
3202        ios,
3203        android
3204      );
3205    }
3206  }
3207
3208  impl ToTokens for FrontendDist {
3209    fn to_tokens(&self, tokens: &mut TokenStream) {
3210      let prefix = quote! { ::tauri::utils::config::FrontendDist };
3211
3212      tokens.append_all(match self {
3213        Self::Url(url) => {
3214          let url = url_lit(url);
3215          quote! { #prefix::Url(#url) }
3216        }
3217        Self::Directory(path) => {
3218          let path = path_buf_lit(path);
3219          quote! { #prefix::Directory(#path) }
3220        }
3221        Self::Files(files) => {
3222          let files = vec_lit(files, path_buf_lit);
3223          quote! { #prefix::Files(#files) }
3224        }
3225      })
3226    }
3227  }
3228
3229  impl ToTokens for BuildConfig {
3230    fn to_tokens(&self, tokens: &mut TokenStream) {
3231      let dev_url = opt_lit(self.dev_url.as_ref().map(url_lit).as_ref());
3232      let frontend_dist = opt_lit(self.frontend_dist.as_ref());
3233      let runner = quote!(None);
3234      let before_dev_command = quote!(None);
3235      let before_build_command = quote!(None);
3236      let before_bundle_command = quote!(None);
3237      let features = quote!(None);
3238
3239      literal_struct!(
3240        tokens,
3241        ::tauri::utils::config::BuildConfig,
3242        runner,
3243        dev_url,
3244        frontend_dist,
3245        before_dev_command,
3246        before_build_command,
3247        before_bundle_command,
3248        features
3249      );
3250    }
3251  }
3252
3253  impl ToTokens for CspDirectiveSources {
3254    fn to_tokens(&self, tokens: &mut TokenStream) {
3255      let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
3256
3257      tokens.append_all(match self {
3258        Self::Inline(sources) => {
3259          let sources = sources.as_str();
3260          quote!(#prefix::Inline(#sources.into()))
3261        }
3262        Self::List(list) => {
3263          let list = vec_lit(list, str_lit);
3264          quote!(#prefix::List(#list))
3265        }
3266      })
3267    }
3268  }
3269
3270  impl ToTokens for Csp {
3271    fn to_tokens(&self, tokens: &mut TokenStream) {
3272      let prefix = quote! { ::tauri::utils::config::Csp };
3273
3274      tokens.append_all(match self {
3275        Self::Policy(policy) => {
3276          let policy = policy.as_str();
3277          quote!(#prefix::Policy(#policy.into()))
3278        }
3279        Self::DirectiveMap(list) => {
3280          let map = map_lit(
3281            quote! { ::std::collections::HashMap },
3282            list,
3283            str_lit,
3284            identity,
3285          );
3286          quote!(#prefix::DirectiveMap(#map))
3287        }
3288      })
3289    }
3290  }
3291
3292  impl ToTokens for DisabledCspModificationKind {
3293    fn to_tokens(&self, tokens: &mut TokenStream) {
3294      let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
3295
3296      tokens.append_all(match self {
3297        Self::Flag(flag) => {
3298          quote! { #prefix::Flag(#flag) }
3299        }
3300        Self::List(directives) => {
3301          let directives = vec_lit(directives, str_lit);
3302          quote! { #prefix::List(#directives) }
3303        }
3304      });
3305    }
3306  }
3307
3308  impl ToTokens for CapabilityEntry {
3309    fn to_tokens(&self, tokens: &mut TokenStream) {
3310      let prefix = quote! { ::tauri::utils::config::CapabilityEntry };
3311
3312      tokens.append_all(match self {
3313        Self::Inlined(capability) => {
3314          quote! { #prefix::Inlined(#capability) }
3315        }
3316        Self::Reference(id) => {
3317          let id = str_lit(id);
3318          quote! { #prefix::Reference(#id) }
3319        }
3320      });
3321    }
3322  }
3323
3324  impl ToTokens for HeaderSource {
3325    fn to_tokens(&self, tokens: &mut TokenStream) {
3326      let prefix = quote! { ::tauri::utils::config::HeaderSource };
3327
3328      tokens.append_all(match self {
3329        Self::Inline(s) => {
3330          let line = s.as_str();
3331          quote!(#prefix::Inline(#line.into()))
3332        }
3333        Self::List(l) => {
3334          let list = vec_lit(l, str_lit);
3335          quote!(#prefix::List(#list))
3336        }
3337        Self::Map(m) => {
3338          let map = map_lit(quote! { ::std::collections::HashMap }, m, str_lit, str_lit);
3339          quote!(#prefix::Map(#map))
3340        }
3341      })
3342    }
3343  }
3344
3345  impl ToTokens for HeaderConfig {
3346    fn to_tokens(&self, tokens: &mut TokenStream) {
3347      let access_control_allow_credentials =
3348        opt_lit(self.access_control_allow_credentials.as_ref());
3349      let access_control_allow_headers = opt_lit(self.access_control_allow_headers.as_ref());
3350      let access_control_allow_methods = opt_lit(self.access_control_allow_methods.as_ref());
3351      let access_control_expose_headers = opt_lit(self.access_control_expose_headers.as_ref());
3352      let access_control_max_age = opt_lit(self.access_control_max_age.as_ref());
3353      let cross_origin_embedder_policy = opt_lit(self.cross_origin_embedder_policy.as_ref());
3354      let cross_origin_opener_policy = opt_lit(self.cross_origin_opener_policy.as_ref());
3355      let cross_origin_resource_policy = opt_lit(self.cross_origin_resource_policy.as_ref());
3356      let permissions_policy = opt_lit(self.permissions_policy.as_ref());
3357      let timing_allow_origin = opt_lit(self.timing_allow_origin.as_ref());
3358      let x_content_type_options = opt_lit(self.x_content_type_options.as_ref());
3359      let tauri_custom_header = opt_lit(self.tauri_custom_header.as_ref());
3360
3361      literal_struct!(
3362        tokens,
3363        ::tauri::utils::config::HeaderConfig,
3364        access_control_allow_credentials,
3365        access_control_allow_headers,
3366        access_control_allow_methods,
3367        access_control_expose_headers,
3368        access_control_max_age,
3369        cross_origin_embedder_policy,
3370        cross_origin_opener_policy,
3371        cross_origin_resource_policy,
3372        permissions_policy,
3373        timing_allow_origin,
3374        x_content_type_options,
3375        tauri_custom_header
3376      );
3377    }
3378  }
3379
3380  impl ToTokens for SecurityConfig {
3381    fn to_tokens(&self, tokens: &mut TokenStream) {
3382      let csp = opt_lit(self.csp.as_ref());
3383      let dev_csp = opt_lit(self.dev_csp.as_ref());
3384      let freeze_prototype = self.freeze_prototype;
3385      let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
3386      let asset_protocol = &self.asset_protocol;
3387      let pattern = &self.pattern;
3388      let capabilities = vec_lit(&self.capabilities, identity);
3389      let headers = opt_lit(self.headers.as_ref());
3390
3391      literal_struct!(
3392        tokens,
3393        ::tauri::utils::config::SecurityConfig,
3394        csp,
3395        dev_csp,
3396        freeze_prototype,
3397        dangerous_disable_asset_csp_modification,
3398        asset_protocol,
3399        pattern,
3400        capabilities,
3401        headers
3402      );
3403    }
3404  }
3405
3406  impl ToTokens for TrayIconConfig {
3407    fn to_tokens(&self, tokens: &mut TokenStream) {
3408      let id = opt_str_lit(self.id.as_ref());
3409      let icon_as_template = self.icon_as_template;
3410      #[allow(deprecated)]
3411      let menu_on_left_click = self.menu_on_left_click;
3412      let show_menu_on_left_click = self.show_menu_on_left_click;
3413      let icon_path = path_buf_lit(&self.icon_path);
3414      let title = opt_str_lit(self.title.as_ref());
3415      let tooltip = opt_str_lit(self.tooltip.as_ref());
3416      literal_struct!(
3417        tokens,
3418        ::tauri::utils::config::TrayIconConfig,
3419        id,
3420        icon_path,
3421        icon_as_template,
3422        menu_on_left_click,
3423        show_menu_on_left_click,
3424        title,
3425        tooltip
3426      );
3427    }
3428  }
3429
3430  impl ToTokens for FsScope {
3431    fn to_tokens(&self, tokens: &mut TokenStream) {
3432      let prefix = quote! { ::tauri::utils::config::FsScope };
3433
3434      tokens.append_all(match self {
3435        Self::AllowedPaths(allow) => {
3436          let allowed_paths = vec_lit(allow, path_buf_lit);
3437          quote! { #prefix::AllowedPaths(#allowed_paths) }
3438        }
3439        Self::Scope { allow, deny , require_literal_leading_dot} => {
3440          let allow = vec_lit(allow, path_buf_lit);
3441          let deny = vec_lit(deny, path_buf_lit);
3442          let  require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref());
3443          quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
3444        }
3445      });
3446    }
3447  }
3448
3449  impl ToTokens for AssetProtocolConfig {
3450    fn to_tokens(&self, tokens: &mut TokenStream) {
3451      let scope = &self.scope;
3452      tokens.append_all(quote! { ::tauri::utils::config::AssetProtocolConfig { scope: #scope, ..Default::default() } })
3453    }
3454  }
3455
3456  impl ToTokens for AppConfig {
3457    fn to_tokens(&self, tokens: &mut TokenStream) {
3458      let windows = vec_lit(&self.windows, identity);
3459      let security = &self.security;
3460      let tray_icon = opt_lit(self.tray_icon.as_ref());
3461      let macos_private_api = self.macos_private_api;
3462      let with_global_tauri = self.with_global_tauri;
3463      let enable_gtk_app_id = self.enable_gtk_app_id;
3464
3465      literal_struct!(
3466        tokens,
3467        ::tauri::utils::config::AppConfig,
3468        windows,
3469        security,
3470        tray_icon,
3471        macos_private_api,
3472        with_global_tauri,
3473        enable_gtk_app_id
3474      );
3475    }
3476  }
3477
3478  impl ToTokens for PluginConfig {
3479    fn to_tokens(&self, tokens: &mut TokenStream) {
3480      let config = map_lit(
3481        quote! { ::std::collections::HashMap },
3482        &self.0,
3483        str_lit,
3484        json_value_lit,
3485      );
3486      tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
3487    }
3488  }
3489
3490  impl ToTokens for Config {
3491    fn to_tokens(&self, tokens: &mut TokenStream) {
3492      let schema = quote!(None);
3493      let product_name = opt_str_lit(self.product_name.as_ref());
3494      let main_binary_name = opt_str_lit(self.main_binary_name.as_ref());
3495      let version = opt_str_lit(self.version.as_ref());
3496      let identifier = str_lit(&self.identifier);
3497      let app = &self.app;
3498      let build = &self.build;
3499      let bundle = &self.bundle;
3500      let plugins = &self.plugins;
3501
3502      literal_struct!(
3503        tokens,
3504        ::tauri::utils::config::Config,
3505        schema,
3506        product_name,
3507        main_binary_name,
3508        version,
3509        identifier,
3510        app,
3511        build,
3512        bundle,
3513        plugins
3514      );
3515    }
3516  }
3517}
3518
3519#[cfg(test)]
3520mod test {
3521  use super::*;
3522
3523  // TODO: create a test that compares a config to a json config
3524
3525  #[test]
3526  // test all of the default functions
3527  fn test_defaults() {
3528    // get default app config
3529    let a_config = AppConfig::default();
3530    // get default build config
3531    let b_config = BuildConfig::default();
3532    // get default window
3533    let d_windows: Vec<WindowConfig> = vec![];
3534    // get default bundle
3535    let d_bundle = BundleConfig::default();
3536
3537    // create a tauri config.
3538    let app = AppConfig {
3539      windows: vec![],
3540      security: SecurityConfig {
3541        csp: None,
3542        dev_csp: None,
3543        freeze_prototype: false,
3544        dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
3545        asset_protocol: AssetProtocolConfig::default(),
3546        pattern: Default::default(),
3547        capabilities: Vec::new(),
3548        headers: None,
3549      },
3550      tray_icon: None,
3551      macos_private_api: false,
3552      with_global_tauri: false,
3553      enable_gtk_app_id: false,
3554    };
3555
3556    // create a build config
3557    let build = BuildConfig {
3558      runner: None,
3559      dev_url: None,
3560      frontend_dist: None,
3561      before_dev_command: None,
3562      before_build_command: None,
3563      before_bundle_command: None,
3564      features: None,
3565    };
3566
3567    // create a bundle config
3568    let bundle = BundleConfig {
3569      active: false,
3570      targets: Default::default(),
3571      create_updater_artifacts: Default::default(),
3572      publisher: None,
3573      homepage: None,
3574      icon: Vec::new(),
3575      resources: None,
3576      copyright: None,
3577      category: None,
3578      file_associations: None,
3579      short_description: None,
3580      long_description: None,
3581      use_local_tools_dir: false,
3582      license: None,
3583      license_file: None,
3584      linux: Default::default(),
3585      macos: Default::default(),
3586      external_bin: None,
3587      windows: Default::default(),
3588      ios: Default::default(),
3589      android: Default::default(),
3590    };
3591
3592    // test the configs
3593    assert_eq!(a_config, app);
3594    assert_eq!(b_config, build);
3595    assert_eq!(d_bundle, bundle);
3596    assert_eq!(d_windows, app.windows);
3597  }
3598
3599  #[test]
3600  fn parse_hex_color() {
3601    use super::Color;
3602
3603    assert_eq!(Color(255, 255, 255, 255), "fff".parse().unwrap());
3604    assert_eq!(Color(255, 255, 255, 255), "#fff".parse().unwrap());
3605    assert_eq!(Color(0, 0, 0, 255), "#000000".parse().unwrap());
3606    assert_eq!(Color(0, 0, 0, 255), "#000000ff".parse().unwrap());
3607    assert_eq!(Color(0, 255, 0, 255), "#00ff00ff".parse().unwrap());
3608  }
3609}