tauri_utils/config_v1/
mod.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//! This is a core functionality that is not considered part of the stable API.
11//! If you use it, note that it may include breaking changes in the future.
12
13use semver::Version;
14use serde::{
15  de::{Deserializer, Error as DeError, Visitor},
16  Deserialize, Serialize, Serializer,
17};
18use serde_json::Value as JsonValue;
19use serde_with::skip_serializing_none;
20use url::Url;
21
22use std::{
23  collections::HashMap,
24  fmt::{self, Display},
25  fs::read_to_string,
26  path::PathBuf,
27  str::FromStr,
28};
29
30/// Items to help with parsing content into a [`Config`].
31pub mod parse;
32
33fn default_true() -> bool {
34  true
35}
36
37/// An URL to open on a Tauri webview window.
38#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
39#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
40#[serde(untagged)]
41#[non_exhaustive]
42pub enum WindowUrl {
43  /// An external URL.
44  External(Url),
45  /// The path portion of an app URL.
46  /// For instance, to load `tauri://localhost/users/john`,
47  /// you can simply provide `users/john` in this configuration.
48  App(PathBuf),
49}
50
51impl fmt::Display for WindowUrl {
52  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53    match self {
54      Self::External(url) => write!(f, "{url}"),
55      Self::App(path) => write!(f, "{}", path.display()),
56    }
57  }
58}
59
60impl Default for WindowUrl {
61  fn default() -> Self {
62    Self::App("index.html".into())
63  }
64}
65
66/// A bundle referenced by tauri-bundler.
67#[derive(Debug, PartialEq, Eq, Clone)]
68#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
69#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))]
70pub enum BundleType {
71  /// The debian bundle (.deb).
72  Deb,
73  /// The AppImage bundle (.appimage).
74  AppImage,
75  /// The Microsoft Installer bundle (.msi).
76  Msi,
77  /// The NSIS bundle (.exe).
78  Nsis,
79  /// The macOS application bundle (.app).
80  App,
81  /// The Apple Disk Image bundle (.dmg).
82  Dmg,
83  /// The Tauri updater bundle.
84  Updater,
85}
86
87impl Display for BundleType {
88  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89    write!(
90      f,
91      "{}",
92      match self {
93        Self::Deb => "deb",
94        Self::AppImage => "appimage",
95        Self::Msi => "msi",
96        Self::Nsis => "nsis",
97        Self::App => "app",
98        Self::Dmg => "dmg",
99        Self::Updater => "updater",
100      }
101    )
102  }
103}
104
105impl Serialize for BundleType {
106  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
107  where
108    S: Serializer,
109  {
110    serializer.serialize_str(self.to_string().as_ref())
111  }
112}
113
114impl<'de> Deserialize<'de> for BundleType {
115  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
116  where
117    D: Deserializer<'de>,
118  {
119    let s = String::deserialize(deserializer)?;
120    match s.to_lowercase().as_str() {
121      "deb" => Ok(Self::Deb),
122      "appimage" => Ok(Self::AppImage),
123      "msi" => Ok(Self::Msi),
124      "nsis" => Ok(Self::Nsis),
125      "app" => Ok(Self::App),
126      "dmg" => Ok(Self::Dmg),
127      "updater" => Ok(Self::Updater),
128      _ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
129    }
130  }
131}
132
133/// Targets to bundle. Each value is case insensitive.
134#[derive(Debug, PartialEq, Eq, Clone)]
135pub enum BundleTarget {
136  /// Bundle all targets.
137  All,
138  /// A list of bundle targets.
139  List(Vec<BundleType>),
140  /// A single bundle target.
141  One(BundleType),
142}
143
144#[cfg(feature = "schemars")]
145pub(crate) trait Merge: Sized {
146  fn merge(self, other: Self) -> Self;
147}
148
149#[cfg(feature = "schema")]
150use schemars::schema::{Metadata, Schema};
151
152#[cfg(feature = "schema")]
153impl<T: Merge> Merge for Option<T> {
154  fn merge(self, other: Self) -> Self {
155    match (self, other) {
156      (Some(x), Some(y)) => Some(x.merge(y)),
157      (None, y) => y,
158      (x, None) => x,
159    }
160  }
161}
162
163#[cfg(feature = "schema")]
164impl<T: Merge> Merge for Box<T> {
165  fn merge(mut self, other: Self) -> Self {
166    *self = (*self).merge(*other);
167    self
168  }
169}
170
171#[cfg(feature = "schema")]
172impl<T> Merge for Vec<T> {
173  fn merge(mut self, other: Self) -> Self {
174    self.extend(other);
175    self
176  }
177}
178
179#[cfg(feature = "schema")]
180impl Merge for Metadata {
181  fn merge(self, other: Self) -> Self {
182    Metadata {
183      id: self.id.or(other.id),
184      title: self.title.or(other.title),
185      description: self.description.or(other.description),
186      default: self.default.or(other.default),
187      deprecated: self.deprecated || other.deprecated,
188      read_only: self.read_only || other.read_only,
189      write_only: self.write_only || other.write_only,
190      examples: self.examples.merge(other.examples),
191    }
192  }
193}
194
195#[cfg(feature = "schema")]
196fn apply_metadata(schema: Schema, metadata: Metadata) -> Schema {
197  if metadata == Metadata::default() {
198    schema
199  } else {
200    let mut schema_obj = schema.into_object();
201    schema_obj.metadata = Some(Box::new(metadata)).merge(schema_obj.metadata);
202    Schema::Object(schema_obj)
203  }
204}
205
206#[cfg(feature = "schema")]
207impl schemars::JsonSchema for BundleTarget {
208  fn schema_name() -> std::string::String {
209    "BundleTarget".to_owned()
210  }
211
212  fn json_schema(generator: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
213    let any_of = vec![
214      schemars::schema::SchemaObject {
215        enum_values: Some(vec!["all".into()]),
216        metadata: Some(Box::new(schemars::schema::Metadata {
217          description: Some("Bundle all targets.".to_owned()),
218          ..Default::default()
219        })),
220        ..Default::default()
221      }
222      .into(),
223      apply_metadata(
224        generator.subschema_for::<Vec<BundleType>>(),
225        schemars::schema::Metadata {
226          description: Some("A list of bundle targets.".to_owned()),
227          ..Default::default()
228        },
229      ),
230      apply_metadata(
231        generator.subschema_for::<BundleType>(),
232        schemars::schema::Metadata {
233          description: Some("A single bundle target.".to_owned()),
234          ..Default::default()
235        },
236      ),
237    ];
238
239    schemars::schema::SchemaObject {
240      subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
241        any_of: Some(any_of),
242        ..Default::default()
243      })),
244      metadata: Some(Box::new(schemars::schema::Metadata {
245        description: Some("Targets to bundle. Each value is case insensitive.".to_owned()),
246        ..Default::default()
247      })),
248      ..Default::default()
249    }
250    .into()
251  }
252}
253
254impl Default for BundleTarget {
255  fn default() -> Self {
256    Self::All
257  }
258}
259
260impl Serialize for BundleTarget {
261  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
262  where
263    S: Serializer,
264  {
265    match self {
266      Self::All => serializer.serialize_str("all"),
267      Self::List(l) => l.serialize(serializer),
268      Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
269    }
270  }
271}
272
273impl<'de> Deserialize<'de> for BundleTarget {
274  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
275  where
276    D: Deserializer<'de>,
277  {
278    #[derive(Deserialize, Serialize)]
279    #[serde(untagged)]
280    pub enum BundleTargetInner {
281      List(Vec<BundleType>),
282      One(BundleType),
283      All(String),
284    }
285
286    match BundleTargetInner::deserialize(deserializer)? {
287      BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
288      BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {t}"))),
289      BundleTargetInner::List(l) => Ok(Self::List(l)),
290      BundleTargetInner::One(t) => Ok(Self::One(t)),
291    }
292  }
293}
294
295/// Configuration for AppImage bundles.
296///
297/// See more: https://tauri.app/v1/api/config#appimageconfig
298#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
299#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
300#[serde(rename_all = "camelCase", deny_unknown_fields)]
301pub struct AppImageConfig {
302  /// Include additional gstreamer dependencies needed for audio and video playback.
303  /// This increases the bundle size by ~15-35MB depending on your build system.
304  #[serde(default, alias = "bundle-media-framework")]
305  pub bundle_media_framework: bool,
306}
307
308/// Configuration for Debian (.deb) bundles.
309///
310/// See more: https://tauri.app/v1/api/config#debconfig
311#[skip_serializing_none]
312#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
313#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
314#[serde(rename_all = "camelCase", deny_unknown_fields)]
315pub struct DebConfig {
316  /// The list of deb dependencies your application relies on.
317  pub depends: Option<Vec<String>>,
318  /// The files to include on the package.
319  #[serde(default)]
320  pub files: HashMap<PathBuf, PathBuf>,
321  /// Path to a custom desktop file Handlebars template.
322  ///
323  /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.
324  pub desktop_template: Option<PathBuf>,
325  /// Define the section in Debian Control file. See : https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections
326  pub section: Option<String>,
327  /// Change the priority of the Debian Package. By default, it is set to `optional`.
328  /// Recognized Priorities as of now are :  `required`, `important`, `standard`, `optional`, `extra`
329  pub priority: Option<String>,
330  /// Path of the uncompressed Changelog file, to be stored at /usr/share/doc/package-name/changelog.gz. See
331  /// https://www.debian.org/doc/debian-policy/ch-docs.html#changelog-files-and-release-notes
332  pub changelog: Option<PathBuf>,
333}
334
335fn de_minimum_system_version<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
336where
337  D: Deserializer<'de>,
338{
339  let version = Option::<String>::deserialize(deserializer)?;
340  match version {
341    Some(v) if v.is_empty() => Ok(minimum_system_version()),
342    e => Ok(e),
343  }
344}
345
346/// Configuration for the macOS bundles.
347///
348/// See more: https://tauri.app/v1/api/config#macconfig
349#[skip_serializing_none]
350#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
351#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
352#[serde(rename_all = "camelCase", deny_unknown_fields)]
353pub struct MacConfig {
354  /// A list of strings indicating any macOS X frameworks that need to be bundled with the application.
355  ///
356  /// 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.
357  pub frameworks: Option<Vec<String>>,
358  /// A version string indicating the minimum macOS X version that the bundled application supports. Defaults to `10.13`.
359  ///
360  /// Setting it to `null` completely removes the `LSMinimumSystemVersion` field on the bundle's `Info.plist`
361  /// and the `MACOSX_DEPLOYMENT_TARGET` environment variable.
362  ///
363  /// An empty string is considered an invalid value so the default value is used.
364  #[serde(
365    deserialize_with = "de_minimum_system_version",
366    default = "minimum_system_version",
367    alias = "minimum-system-version"
368  )]
369  pub minimum_system_version: Option<String>,
370  /// Allows your application to communicate with the outside world.
371  /// It should be a lowercase, without port and protocol domain name.
372  #[serde(alias = "exception-domain")]
373  pub exception_domain: Option<String>,
374  /// The path to the license file to add to the DMG bundle.
375  pub license: Option<String>,
376  /// Identity to use for code signing.
377  #[serde(alias = "signing-identity")]
378  pub signing_identity: Option<String>,
379  /// Provider short name for notarization.
380  #[serde(alias = "provider-short-name")]
381  pub provider_short_name: Option<String>,
382  /// Path to the entitlements file.
383  pub entitlements: Option<String>,
384}
385
386impl Default for MacConfig {
387  fn default() -> Self {
388    Self {
389      frameworks: None,
390      minimum_system_version: minimum_system_version(),
391      exception_domain: None,
392      license: None,
393      signing_identity: None,
394      provider_short_name: None,
395      entitlements: None,
396    }
397  }
398}
399
400fn minimum_system_version() -> Option<String> {
401  Some("10.13".into())
402}
403
404/// Configuration for a target language for the WiX build.
405///
406/// See more: https://tauri.app/v1/api/config#wixlanguageconfig
407#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
408#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
409#[serde(rename_all = "camelCase", deny_unknown_fields)]
410pub struct WixLanguageConfig {
411  /// The path to a locale (`.wxl`) file. See <https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html>.
412  #[serde(alias = "locale-path")]
413  pub locale_path: Option<String>,
414}
415
416/// The languages to build using WiX.
417#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
418#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
419#[serde(untagged)]
420pub enum WixLanguage {
421  /// A single language to build, without configuration.
422  One(String),
423  /// A list of languages to build, without configuration.
424  List(Vec<String>),
425  /// A map of languages and its configuration.
426  Localized(HashMap<String, WixLanguageConfig>),
427}
428
429impl Default for WixLanguage {
430  fn default() -> Self {
431    Self::One("en-US".into())
432  }
433}
434
435/// Configuration for the MSI bundle using WiX.
436///
437/// See more: https://tauri.app/v1/api/config#wixconfig
438#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
439#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
440#[serde(rename_all = "camelCase", deny_unknown_fields)]
441pub struct WixConfig {
442  /// The installer languages to build. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.
443  #[serde(default)]
444  pub language: WixLanguage,
445  /// A custom .wxs template to use.
446  pub template: Option<PathBuf>,
447  /// A list of paths to .wxs files with WiX fragments to use.
448  #[serde(default, alias = "fragment-paths")]
449  pub fragment_paths: Vec<PathBuf>,
450  /// The ComponentGroup element ids you want to reference from the fragments.
451  #[serde(default, alias = "component-group-refs")]
452  pub component_group_refs: Vec<String>,
453  /// The Component element ids you want to reference from the fragments.
454  #[serde(default, alias = "component-refs")]
455  pub component_refs: Vec<String>,
456  /// The FeatureGroup element ids you want to reference from the fragments.
457  #[serde(default, alias = "feature-group-refs")]
458  pub feature_group_refs: Vec<String>,
459  /// The Feature element ids you want to reference from the fragments.
460  #[serde(default, alias = "feature-refs")]
461  pub feature_refs: Vec<String>,
462  /// The Merge element ids you want to reference from the fragments.
463  #[serde(default, alias = "merge-refs")]
464  pub merge_refs: Vec<String>,
465  /// Disables the Webview2 runtime installation after app install.
466  ///
467  /// Will be removed in v2, prefer the [`WindowsConfig::webview_install_mode`] option.
468  #[serde(default, alias = "skip-webview-install")]
469  pub skip_webview_install: bool,
470  /// The path to the license file to render on the installer.
471  ///
472  /// Must be an RTF file, so if a different extension is provided, we convert it to the RTF format.
473  pub license: Option<PathBuf>,
474  /// Create an elevated update task within Windows Task Scheduler.
475  #[serde(default, alias = "enable-elevated-update-task")]
476  pub enable_elevated_update_task: bool,
477  /// Path to a bitmap file to use as the installation user interface banner.
478  /// This bitmap will appear at the top of all but the first page of the installer.
479  ///
480  /// The required dimensions are 493px × 58px.
481  #[serde(alias = "banner-path")]
482  pub banner_path: Option<PathBuf>,
483  /// Path to a bitmap file to use on the installation user interface dialogs.
484  /// It is used on the welcome and completion dialogs.
485  ///
486  /// The required dimensions are 493px × 312px.
487  #[serde(alias = "dialog-image-path")]
488  pub dialog_image_path: Option<PathBuf>,
489}
490
491/// Compression algorithms used in the NSIS installer.
492///
493/// See <https://nsis.sourceforge.io/Reference/SetCompressor>
494#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
495#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
496#[serde(rename_all = "camelCase", deny_unknown_fields)]
497pub enum NsisCompression {
498  /// ZLIB uses the deflate algorithm, it is a quick and simple method. With the default compression level it uses about 300 KB of memory.
499  Zlib,
500  /// 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.
501  Bzip2,
502  /// 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.
503  Lzma,
504}
505
506/// Configuration for the Installer bundle using NSIS.
507#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
508#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
509#[serde(rename_all = "camelCase", deny_unknown_fields)]
510pub struct NsisConfig {
511  /// A custom .nsi template to use.
512  pub template: Option<PathBuf>,
513  /// The path to the license file to render on the installer.
514  pub license: Option<PathBuf>,
515  /// The path to a bitmap file to display on the header of installers pages.
516  ///
517  /// The recommended dimensions are 150px x 57px.
518  #[serde(alias = "header-image")]
519  pub header_image: Option<PathBuf>,
520  /// The path to a bitmap file for the Welcome page and the Finish page.
521  ///
522  /// The recommended dimensions are 164px x 314px.
523  #[serde(alias = "sidebar-image")]
524  pub sidebar_image: Option<PathBuf>,
525  /// The path to an icon file used as the installer icon.
526  #[serde(alias = "install-icon")]
527  pub installer_icon: Option<PathBuf>,
528  /// Whether the installation will be for all users or just the current user.
529  #[serde(default, alias = "install-mode")]
530  pub install_mode: NSISInstallerMode,
531  /// A list of installer languages.
532  /// By default the OS language is used. If the OS language is not in the list of languages, the first language will be used.
533  /// To allow the user to select the language, set `display_language_selector` to `true`.
534  ///
535  /// See <https://github.com/kichik/nsis/tree/9465c08046f00ccb6eda985abbdbf52c275c6c4d/Contrib/Language%20files> for the complete list of languages.
536  pub languages: Option<Vec<String>>,
537  /// A key-value pair where the key is the language and the
538  /// value is the path to a custom `.nsh` file that holds the translated text for tauri's custom messages.
539  ///
540  /// See <https://github.com/tauri-apps/tauri/blob/dev/tooling/bundler/src/bundle/windows/templates/nsis-languages/English.nsh> for an example `.nsh` file.
541  ///
542  /// **Note**: the key must be a valid NSIS language and it must be added to [`NsisConfig`] languages array,
543  pub custom_language_files: Option<HashMap<String, PathBuf>>,
544  /// Whether to display a language selector dialog before the installer and uninstaller windows are rendered or not.
545  /// By default the OS language is selected, with a fallback to the first language in the `languages` array.
546  #[serde(default, alias = "display-language-selector")]
547  pub display_language_selector: bool,
548  /// Set the compression algorithm used to compress files in the installer.
549  ///
550  /// See <https://nsis.sourceforge.io/Reference/SetCompressor>
551  pub compression: Option<NsisCompression>,
552}
553
554/// Install Modes for the NSIS installer.
555#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
556#[serde(rename_all = "camelCase", deny_unknown_fields)]
557#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
558pub enum NSISInstallerMode {
559  /// Default mode for the installer.
560  ///
561  /// Install the app by default in a directory that doesn't require Administrator access.
562  ///
563  /// Installer metadata will be saved under the `HKCU` registry path.
564  CurrentUser,
565  /// Install the app by default in the `Program Files` folder directory requires Administrator
566  /// access for the installation.
567  ///
568  /// Installer metadata will be saved under the `HKLM` registry path.
569  PerMachine,
570  /// Combines both modes and allows the user to choose at install time
571  /// whether to install for the current user or per machine. Note that this mode
572  /// will require Administrator access even if the user wants to install it for the current user only.
573  ///
574  /// Installer metadata will be saved under the `HKLM` or `HKCU` registry path based on the user's choice.
575  Both,
576}
577
578impl Default for NSISInstallerMode {
579  fn default() -> Self {
580    Self::CurrentUser
581  }
582}
583
584/// Install modes for the Webview2 runtime.
585/// Note that for the updater bundle [`Self::DownloadBootstrapper`] is used.
586///
587/// For more information see <https://tauri.app/v1/guides/building/windows>.
588#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
589#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
590#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
591pub enum WebviewInstallMode {
592  /// Do not install the Webview2 as part of the Windows Installer.
593  Skip,
594  /// Download the bootstrapper and run it.
595  /// Requires an internet connection.
596  /// Results in a smaller installer size, but is not recommended on Windows 7.
597  DownloadBootstrapper {
598    /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
599    #[serde(default = "default_true")]
600    silent: bool,
601  },
602  /// Embed the bootstrapper and run it.
603  /// Requires an internet connection.
604  /// Increases the installer size by around 1.8MB, but offers better support on Windows 7.
605  EmbedBootstrapper {
606    /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
607    #[serde(default = "default_true")]
608    silent: bool,
609  },
610  /// Embed the offline installer and run it.
611  /// Does not require an internet connection.
612  /// Increases the installer size by around 127MB.
613  OfflineInstaller {
614    /// Instructs the installer to run the installer in silent mode. Defaults to `true`.
615    #[serde(default = "default_true")]
616    silent: bool,
617  },
618  /// Embed a fixed webview2 version and use it at runtime.
619  /// Increases the installer size by around 180MB.
620  FixedRuntime {
621    /// The path to the fixed runtime to use.
622    ///
623    /// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
624    /// The `.cab` file must be extracted to a folder and this folder path must be defined on this field.
625    path: PathBuf,
626  },
627}
628
629impl Default for WebviewInstallMode {
630  fn default() -> Self {
631    Self::DownloadBootstrapper { silent: true }
632  }
633}
634
635/// Windows bundler configuration.
636///
637/// See more: https://tauri.app/v1/api/config#windowsconfig
638#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
639#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
640#[serde(rename_all = "camelCase", deny_unknown_fields)]
641pub struct WindowsConfig {
642  /// Specifies the file digest algorithm to use for creating file signatures.
643  /// Required for code signing. SHA-256 is recommended.
644  #[serde(alias = "digest-algorithm")]
645  pub digest_algorithm: Option<String>,
646  /// Specifies the SHA1 hash of the signing certificate.
647  #[serde(alias = "certificate-thumbprint")]
648  pub certificate_thumbprint: Option<String>,
649  /// Server to use during timestamping.
650  #[serde(alias = "timestamp-url")]
651  pub timestamp_url: Option<String>,
652  /// Whether to use Time-Stamp Protocol (TSP, a.k.a. RFC 3161) for the timestamp server. Your code signing provider may
653  /// use a TSP timestamp server, like e.g. SSL.com does. If so, enable TSP by setting to true.
654  #[serde(default)]
655  pub tsp: bool,
656  /// The installation mode for the Webview2 runtime.
657  #[serde(default, alias = "webview-install-mode")]
658  pub webview_install_mode: WebviewInstallMode,
659  /// Path to the webview fixed runtime to use. Overwrites [`Self::webview_install_mode`] if set.
660  ///
661  /// Will be removed in v2, prefer the [`Self::webview_install_mode`] option.
662  ///
663  /// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
664  /// The `.cab` file must be extracted to a folder and this folder path must be defined on this field.
665  #[serde(alias = "webview-fixed-runtime-path")]
666  pub webview_fixed_runtime_path: Option<PathBuf>,
667  /// Validates a second app installation, blocking the user from installing an older version if set to `false`.
668  ///
669  /// 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`.
670  ///
671  /// The default value of this flag is `true`.
672  #[serde(default = "default_true", alias = "allow-downgrades")]
673  pub allow_downgrades: bool,
674  /// Configuration for the MSI generated with WiX.
675  pub wix: Option<WixConfig>,
676  /// Configuration for the installer generated with NSIS.
677  pub nsis: Option<NsisConfig>,
678}
679
680impl Default for WindowsConfig {
681  fn default() -> Self {
682    Self {
683      digest_algorithm: None,
684      certificate_thumbprint: None,
685      timestamp_url: None,
686      tsp: false,
687      webview_install_mode: Default::default(),
688      webview_fixed_runtime_path: None,
689      allow_downgrades: true,
690      wix: None,
691      nsis: None,
692    }
693  }
694}
695
696/// Definition for bundle resources.
697/// Can be either a list of paths to include or a map of source to target paths.
698#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
699#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
700#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
701pub enum BundleResources {
702  /// A list of paths to include.
703  List(Vec<String>),
704  /// A map of source to target paths.
705  Map(HashMap<String, String>),
706}
707
708/// Configuration for tauri-bundler.
709///
710/// See more: https://tauri.app/v1/api/config#bundleconfig
711#[skip_serializing_none]
712#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
713#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
714#[serde(rename_all = "camelCase", deny_unknown_fields)]
715pub struct BundleConfig {
716  /// Whether Tauri should bundle your application or just output the executable.
717  #[serde(default)]
718  pub active: bool,
719  /// The bundle targets, currently supports ["deb", "appimage", "nsis", "msi", "app", "dmg", "updater"] or "all".
720  #[serde(default)]
721  pub targets: BundleTarget,
722  /// The application identifier in reverse domain name notation (e.g. `com.tauri.example`).
723  /// This string must be unique across applications since it is used in system configurations like
724  /// the bundle ID and path to the webview data directory.
725  /// This string must contain only alphanumeric characters (A-Z, a-z, and 0-9), hyphens (-),
726  /// and periods (.).
727  pub identifier: String,
728  /// The application's publisher. Defaults to the second element in the identifier string.
729  /// Currently maps to the Manufacturer property of the Windows Installer.
730  pub publisher: Option<String>,
731  /// The app's icons
732  #[serde(default)]
733  pub icon: Vec<String>,
734  /// App resources to bundle.
735  /// Each resource is a path to a file or directory.
736  /// Glob patterns are supported.
737  pub resources: Option<BundleResources>,
738  /// A copyright string associated with your application.
739  pub copyright: Option<String>,
740  /// The application kind.
741  ///
742  /// Should be one of the following:
743  /// 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.
744  pub category: Option<String>,
745  /// A short description of your application.
746  #[serde(alias = "short-description")]
747  pub short_description: Option<String>,
748  /// A longer, multi-line description of the application.
749  #[serde(alias = "long-description")]
750  pub long_description: Option<String>,
751  /// Configuration for the AppImage bundle.
752  #[serde(default)]
753  pub appimage: AppImageConfig,
754  /// Configuration for the Debian bundle.
755  #[serde(default)]
756  pub deb: DebConfig,
757  /// Configuration for the macOS bundles.
758  #[serde(rename = "macOS", default)]
759  pub macos: MacConfig,
760  /// A list of—either absolute or relative—paths to binaries to embed with your application.
761  ///
762  /// Note that Tauri will look for system-specific binaries following the pattern "binary-name{-target-triple}{.system-extension}".
763  ///
764  /// E.g. for the external binary "my-binary", Tauri looks for:
765  ///
766  /// - "my-binary-x86_64-pc-windows-msvc.exe" for Windows
767  /// - "my-binary-x86_64-apple-darwin" for macOS
768  /// - "my-binary-x86_64-unknown-linux-gnu" for Linux
769  ///
770  /// so don't forget to provide binaries for all targeted platforms.
771  #[serde(alias = "external-bin")]
772  pub external_bin: Option<Vec<String>>,
773  /// Configuration for the Windows bundle.
774  #[serde(default)]
775  pub windows: WindowsConfig,
776}
777
778/// A CLI argument definition.
779#[skip_serializing_none]
780#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
781#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
782#[serde(rename_all = "camelCase", deny_unknown_fields)]
783pub struct CliArg {
784  /// The short version of the argument, without the preceding -.
785  ///
786  /// NOTE: Any leading `-` characters will be stripped, and only the first non-character will be used as the short version.
787  pub short: Option<char>,
788  /// The unique argument name
789  pub name: String,
790  /// The argument description which will be shown on the help information.
791  /// Typically, this is a short (one line) description of the arg.
792  pub description: Option<String>,
793  /// The argument long description which will be shown on the help information.
794  /// Typically this a more detailed (multi-line) message that describes the argument.
795  #[serde(alias = "long-description")]
796  pub long_description: Option<String>,
797  /// Specifies that the argument takes a value at run time.
798  ///
799  /// NOTE: values for arguments may be specified in any of the following methods
800  /// - Using a space such as -o value or --option value
801  /// - Using an equals and no space such as -o=value or --option=value
802  /// - Use a short and no space such as -ovalue
803  #[serde(default, alias = "takes-value")]
804  pub takes_value: bool,
805  /// Specifies that the argument may have an unknown number of multiple values. Without any other settings, this argument may appear only once.
806  ///
807  /// For example, --opt val1 val2 is allowed, but --opt val1 val2 --opt val3 is not.
808  ///
809  /// NOTE: Setting this requires `takes_value` to be set to true.
810  #[serde(default)]
811  pub multiple: bool,
812  /// Specifies that the argument may appear more than once.
813  /// For flags, this results in the number of occurrences of the flag being recorded. For example -ddd or -d -d -d would count as three occurrences.
814  /// For options or arguments that take a value, this does not affect how many values they can accept. (i.e. only one at a time is allowed)
815  ///
816  /// For example, --opt val1 --opt val2 is allowed, but --opt val1 val2 is not.
817  #[serde(default, alias = "multiple-occurrences")]
818  pub multiple_occurrences: bool,
819  /// Specifies how many values are required to satisfy this argument. For example, if you had a
820  /// `-f <file>` argument where you wanted exactly 3 'files' you would set
821  /// `number_of_values = 3`, and this argument wouldn't be satisfied unless the user provided
822  /// 3 and only 3 values.
823  ///
824  /// **NOTE:** Does *not* require `multiple_occurrences = true` to be set. Setting
825  /// `multiple_occurrences = true` would allow `-f <file> <file> <file> -f <file> <file> <file>` where
826  /// as *not* setting it would only allow one occurrence of this argument.
827  ///
828  /// **NOTE:** implicitly sets `takes_value = true` and `multiple_values = true`.
829  #[serde(alias = "number-of-values")]
830  pub number_of_values: Option<usize>,
831  /// Specifies a list of possible values for this argument.
832  /// At runtime, the CLI verifies that only one of the specified values was used, or fails with an error message.
833  #[serde(alias = "possible-values")]
834  pub possible_values: Option<Vec<String>>,
835  /// Specifies the minimum number of values for this argument.
836  /// For example, if you had a -f `<file>` argument where you wanted at least 2 'files',
837  /// you would set `minValues: 2`, and this argument would be satisfied if the user provided, 2 or more values.
838  #[serde(alias = "min-values")]
839  pub min_values: Option<usize>,
840  /// Specifies the maximum number of values are for this argument.
841  /// For example, if you had a -f `<file>` argument where you wanted up to 3 'files',
842  /// you would set .max_values(3), and this argument would be satisfied if the user provided, 1, 2, or 3 values.
843  #[serde(alias = "max-values")]
844  pub max_values: Option<usize>,
845  /// Sets whether or not the argument is required by default.
846  ///
847  /// - Required by default means it is required, when no other conflicting rules have been evaluated
848  /// - Conflicting rules take precedence over being required.
849  #[serde(default)]
850  pub required: bool,
851  /// Sets an arg that override this arg's required setting
852  /// i.e. this arg will be required unless this other argument is present.
853  #[serde(alias = "required-unless-present")]
854  pub required_unless_present: Option<String>,
855  /// Sets args that override this arg's required setting
856  /// i.e. this arg will be required unless all these other arguments are present.
857  #[serde(alias = "required-unless-present-all")]
858  pub required_unless_present_all: Option<Vec<String>>,
859  /// Sets args that override this arg's required setting
860  /// i.e. this arg will be required unless at least one of these other arguments are present.
861  #[serde(alias = "required-unless-present-any")]
862  pub required_unless_present_any: Option<Vec<String>>,
863  /// Sets a conflicting argument by name
864  /// i.e. when using this argument, the following argument can't be present and vice versa.
865  #[serde(alias = "conflicts-with")]
866  pub conflicts_with: Option<String>,
867  /// The same as conflictsWith but allows specifying multiple two-way conflicts per argument.
868  #[serde(alias = "conflicts-with-all")]
869  pub conflicts_with_all: Option<Vec<String>>,
870  /// Tets an argument by name that is required when this one is present
871  /// i.e. when using this argument, the following argument must be present.
872  pub requires: Option<String>,
873  /// Sts multiple arguments by names that are required when this one is present
874  /// i.e. when using this argument, the following arguments must be present.
875  #[serde(alias = "requires-all")]
876  pub requires_all: Option<Vec<String>>,
877  /// Allows a conditional requirement with the signature [arg, value]
878  /// the requirement will only become valid if `arg`'s value equals `${value}`.
879  #[serde(alias = "requires-if")]
880  pub requires_if: Option<Vec<String>>,
881  /// Allows specifying that an argument is required conditionally with the signature [arg, value]
882  /// the requirement will only become valid if the `arg`'s value equals `${value}`.
883  #[serde(alias = "requires-if-eq")]
884  pub required_if_eq: Option<Vec<String>>,
885  /// Requires that options use the --option=val syntax
886  /// i.e. an equals between the option and associated value.
887  #[serde(alias = "requires-equals")]
888  pub require_equals: Option<bool>,
889  /// The positional argument index, starting at 1.
890  ///
891  /// The index refers to position according to other positional argument.
892  /// It does not define position in the argument list as a whole. When utilized with multiple=true,
893  /// only the last positional argument may be defined as multiple (i.e. the one with the highest index).
894  #[cfg_attr(feature = "schema", validate(range(min = 1)))]
895  pub index: Option<usize>,
896}
897
898/// describes a CLI configuration
899///
900/// See more: https://tauri.app/v1/api/config#cliconfig
901#[skip_serializing_none]
902#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
903#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
904#[serde(rename_all = "camelCase", deny_unknown_fields)]
905pub struct CliConfig {
906  /// Command description which will be shown on the help information.
907  pub description: Option<String>,
908  /// Command long description which will be shown on the help information.
909  #[serde(alias = "long-description")]
910  pub long_description: Option<String>,
911  /// Adds additional help information to be displayed in addition to auto-generated help.
912  /// This information is displayed before the auto-generated help information.
913  /// This is often used for header information.
914  #[serde(alias = "before-help")]
915  pub before_help: Option<String>,
916  /// Adds additional help information to be displayed in addition to auto-generated help.
917  /// This information is displayed after the auto-generated help information.
918  /// This is often used to describe how to use the arguments, or caveats to be noted.
919  #[serde(alias = "after-help")]
920  pub after_help: Option<String>,
921  /// List of arguments for the command
922  pub args: Option<Vec<CliArg>>,
923  /// List of subcommands of this command
924  pub subcommands: Option<HashMap<String, CliConfig>>,
925}
926
927/// The window configuration object.
928///
929/// See more: https://tauri.app/v1/api/config#windowconfig
930#[skip_serializing_none]
931#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
932#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
933#[serde(rename_all = "camelCase", deny_unknown_fields)]
934pub struct WindowConfig {
935  /// The window identifier. It must be alphanumeric.
936  #[serde(default = "default_window_label")]
937  pub label: String,
938  /// The window webview URL.
939  #[serde(default)]
940  pub url: WindowUrl,
941  /// The user agent for the webview
942  #[serde(alias = "user-agent")]
943  pub user_agent: Option<String>,
944  /// Whether the file drop is enabled or not on the webview. By default it is enabled.
945  ///
946  /// Disabling it is required to use drag and drop on the frontend on Windows.
947  #[serde(default = "default_true", alias = "file-drop-enabled")]
948  pub file_drop_enabled: bool,
949  /// Whether or not the window starts centered or not.
950  #[serde(default)]
951  pub center: bool,
952  /// The horizontal position of the window's top left corner
953  pub x: Option<f64>,
954  /// The vertical position of the window's top left corner
955  pub y: Option<f64>,
956  /// The window width.
957  #[serde(default = "default_width")]
958  pub width: f64,
959  /// The window height.
960  #[serde(default = "default_height")]
961  pub height: f64,
962  /// The min window width.
963  #[serde(alias = "min-width")]
964  pub min_width: Option<f64>,
965  /// The min window height.
966  #[serde(alias = "min-height")]
967  pub min_height: Option<f64>,
968  /// The max window width.
969  #[serde(alias = "max-width")]
970  pub max_width: Option<f64>,
971  /// The max window height.
972  #[serde(alias = "max-height")]
973  pub max_height: Option<f64>,
974  /// Whether the window is resizable or not. When resizable is set to false, native window's maximize button is automatically disabled.
975  #[serde(default = "default_true")]
976  pub resizable: bool,
977  /// Whether the window's native maximize button is enabled or not.
978  /// If resizable is set to false, this setting is ignored.
979  ///
980  /// ## Platform-specific
981  ///
982  /// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode.
983  /// - **Linux / iOS / Android:** Unsupported.
984  #[serde(default = "default_true")]
985  pub maximizable: bool,
986  /// Whether the window's native minimize button is enabled or not.
987  ///
988  /// ## Platform-specific
989  ///
990  /// - **Linux / iOS / Android:** Unsupported.
991  #[serde(default = "default_true")]
992  pub minimizable: bool,
993  /// Whether the window's native close button is enabled or not.
994  ///
995  /// ## Platform-specific
996  ///
997  /// - **Linux:** "GTK+ will do its best to convince the window manager not to show a close button.
998  ///   Depending on the system, this function may not have any effect when called on a window that is already visible"
999  /// - **iOS / Android:** Unsupported.
1000  #[serde(default = "default_true")]
1001  pub closable: bool,
1002  /// The window title.
1003  #[serde(default = "default_title")]
1004  pub title: String,
1005  /// Whether the window starts as fullscreen or not.
1006  #[serde(default)]
1007  pub fullscreen: bool,
1008  /// Whether the window will be initially focused or not.
1009  #[serde(default = "default_true")]
1010  pub focus: bool,
1011  /// Whether the window is transparent or not.
1012  ///
1013  /// Note that on `macOS` this requires the `macos-private-api` feature flag, enabled under `tauri > macOSPrivateApi`.
1014  /// WARNING: Using private APIs on `macOS` prevents your application from being accepted to the `App Store`.
1015  #[serde(default)]
1016  pub transparent: bool,
1017  /// Whether the window is maximized or not.
1018  #[serde(default)]
1019  pub maximized: bool,
1020  /// Whether the window is visible or not.
1021  #[serde(default = "default_true")]
1022  pub visible: bool,
1023  /// Whether the window should have borders and bars.
1024  #[serde(default = "default_true")]
1025  pub decorations: bool,
1026  /// Whether the window should always be on top of other windows.
1027  #[serde(default, alias = "always-on-top")]
1028  pub always_on_top: bool,
1029  /// Prevents the window contents from being captured by other apps.
1030  #[serde(default, alias = "content-protected")]
1031  pub content_protected: bool,
1032  /// If `true`, hides the window icon from the taskbar on Windows and Linux.
1033  #[serde(default, alias = "skip-taskbar")]
1034  pub skip_taskbar: bool,
1035  /// The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.
1036  pub theme: Option<Theme>,
1037  /// The style of the macOS title bar.
1038  #[serde(default, alias = "title-bar-style")]
1039  pub title_bar_style: TitleBarStyle,
1040  /// If `true`, sets the window title to be hidden on macOS.
1041  #[serde(default, alias = "hidden-title")]
1042  pub hidden_title: bool,
1043  /// Whether clicking an inactive window also clicks through to the webview on macOS.
1044  #[serde(default, alias = "accept-first-mouse")]
1045  pub accept_first_mouse: bool,
1046  /// Defines the window [tabbing identifier] for macOS.
1047  ///
1048  /// Windows with matching tabbing identifiers will be grouped together.
1049  /// If the tabbing identifier is not set, automatic tabbing will be disabled.
1050  ///
1051  /// [tabbing identifier]: <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
1052  #[serde(default, alias = "tabbing-identifier")]
1053  pub tabbing_identifier: Option<String>,
1054  /// Defines additional browser arguments on Windows. By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
1055  /// so if you use this method, you also need to disable these components by yourself if you want.
1056  #[serde(default, alias = "additional-browser-args")]
1057  pub additional_browser_args: Option<String>,
1058}
1059
1060impl Default for WindowConfig {
1061  fn default() -> Self {
1062    Self {
1063      label: default_window_label(),
1064      url: WindowUrl::default(),
1065      user_agent: None,
1066      file_drop_enabled: true,
1067      center: false,
1068      x: None,
1069      y: None,
1070      width: default_width(),
1071      height: default_height(),
1072      min_width: None,
1073      min_height: None,
1074      max_width: None,
1075      max_height: None,
1076      resizable: true,
1077      maximizable: true,
1078      minimizable: true,
1079      closable: true,
1080      title: default_title(),
1081      fullscreen: false,
1082      focus: false,
1083      transparent: false,
1084      maximized: false,
1085      visible: true,
1086      decorations: true,
1087      always_on_top: false,
1088      content_protected: false,
1089      skip_taskbar: false,
1090      theme: None,
1091      title_bar_style: Default::default(),
1092      hidden_title: false,
1093      accept_first_mouse: false,
1094      tabbing_identifier: None,
1095      additional_browser_args: None,
1096    }
1097  }
1098}
1099
1100fn default_window_label() -> String {
1101  "main".to_string()
1102}
1103
1104fn default_width() -> f64 {
1105  800f64
1106}
1107
1108fn default_height() -> f64 {
1109  600f64
1110}
1111
1112fn default_title() -> String {
1113  "Tauri App".to_string()
1114}
1115
1116/// A Content-Security-Policy directive source list.
1117/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources>.
1118#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1119#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1120#[serde(rename_all = "camelCase", untagged)]
1121pub enum CspDirectiveSources {
1122  /// An inline list of CSP sources. Same as [`Self::List`], but concatenated with a space separator.
1123  Inline(String),
1124  /// A list of CSP sources. The collection will be concatenated with a space separator for the CSP string.
1125  List(Vec<String>),
1126}
1127
1128impl Default for CspDirectiveSources {
1129  fn default() -> Self {
1130    Self::List(Vec::new())
1131  }
1132}
1133
1134impl From<CspDirectiveSources> for Vec<String> {
1135  fn from(sources: CspDirectiveSources) -> Self {
1136    match sources {
1137      CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
1138      CspDirectiveSources::List(l) => l,
1139    }
1140  }
1141}
1142
1143impl CspDirectiveSources {
1144  /// Whether the given source is configured on this directive or not.
1145  pub fn contains(&self, source: &str) -> bool {
1146    match self {
1147      Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
1148      Self::List(l) => l.contains(&source.into()),
1149    }
1150  }
1151
1152  /// Appends the given source to this directive.
1153  pub fn push<S: AsRef<str>>(&mut self, source: S) {
1154    match self {
1155      Self::Inline(s) => {
1156        s.push(' ');
1157        s.push_str(source.as_ref());
1158      }
1159      Self::List(l) => {
1160        l.push(source.as_ref().to_string());
1161      }
1162    }
1163  }
1164}
1165
1166/// A Content-Security-Policy definition.
1167/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
1168#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1169#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1170#[serde(rename_all = "camelCase", untagged)]
1171pub enum Csp {
1172  /// The entire CSP policy in a single text string.
1173  Policy(String),
1174  /// An object mapping a directive with its sources values as a list of strings.
1175  DirectiveMap(HashMap<String, CspDirectiveSources>),
1176}
1177
1178impl From<HashMap<String, CspDirectiveSources>> for Csp {
1179  fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
1180    Self::DirectiveMap(map)
1181  }
1182}
1183
1184impl From<Csp> for HashMap<String, CspDirectiveSources> {
1185  fn from(csp: Csp) -> Self {
1186    match csp {
1187      Csp::Policy(policy) => {
1188        let mut map = HashMap::new();
1189        for directive in policy.split(';') {
1190          let mut tokens = directive.trim().split(' ');
1191          if let Some(directive) = tokens.next() {
1192            let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
1193            map.insert(directive.to_string(), CspDirectiveSources::List(sources));
1194          }
1195        }
1196        map
1197      }
1198      Csp::DirectiveMap(m) => m,
1199    }
1200  }
1201}
1202
1203impl Display for Csp {
1204  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1205    match self {
1206      Self::Policy(s) => write!(f, "{s}"),
1207      Self::DirectiveMap(m) => {
1208        let len = m.len();
1209        let mut i = 0;
1210        for (directive, sources) in m {
1211          let sources: Vec<String> = sources.clone().into();
1212          write!(f, "{} {}", directive, sources.join(" "))?;
1213          i += 1;
1214          if i != len {
1215            write!(f, "; ")?;
1216          }
1217        }
1218        Ok(())
1219      }
1220    }
1221  }
1222}
1223
1224/// The possible values for the `dangerous_disable_asset_csp_modification` config option.
1225#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1226#[serde(untagged)]
1227#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1228pub enum DisabledCspModificationKind {
1229  /// If `true`, disables all CSP modification.
1230  /// `false` is the default value and it configures Tauri to control the CSP.
1231  Flag(bool),
1232  /// Disables the given list of CSP directives modifications.
1233  List(Vec<String>),
1234}
1235
1236impl Default for DisabledCspModificationKind {
1237  fn default() -> Self {
1238    Self::Flag(false)
1239  }
1240}
1241
1242/// External command access definition.
1243#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1244#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1245#[serde(rename_all = "camelCase", deny_unknown_fields)]
1246pub struct RemoteDomainAccessScope {
1247  /// The URL scheme to allow. By default, all schemas are allowed.
1248  pub scheme: Option<String>,
1249  /// The domain to allow.
1250  pub domain: String,
1251  /// The list of window labels this scope applies to.
1252  pub windows: Vec<String>,
1253  /// The list of plugins that are allowed in this scope.
1254  /// The names should be without the `tauri-plugin-` prefix, for example `"store"` for `tauri-plugin-store`.
1255  #[serde(default)]
1256  pub plugins: Vec<String>,
1257  /// Enables access to the Tauri API.
1258  #[serde(default, rename = "enableTauriAPI", alias = "enable-tauri-api")]
1259  pub enable_tauri_api: bool,
1260}
1261
1262/// Security configuration.
1263///
1264/// See more: https://tauri.app/v1/api/config#securityconfig
1265#[skip_serializing_none]
1266#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1267#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1268#[serde(rename_all = "camelCase", deny_unknown_fields)]
1269pub struct SecurityConfig {
1270  /// The Content Security Policy that will be injected on all HTML files on the built application.
1271  /// If [`dev_csp`](#SecurityConfig.devCsp) is not specified, this value is also injected on dev.
1272  ///
1273  /// This is a really important part of the configuration since it helps you ensure your WebView is secured.
1274  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
1275  pub csp: Option<Csp>,
1276  /// The Content Security Policy that will be injected on all HTML files on development.
1277  ///
1278  /// This is a really important part of the configuration since it helps you ensure your WebView is secured.
1279  /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
1280  #[serde(alias = "dev-csp")]
1281  pub dev_csp: Option<Csp>,
1282  /// Freeze the `Object.prototype` when using the custom protocol.
1283  #[serde(default, alias = "freeze-prototype")]
1284  pub freeze_prototype: bool,
1285  /// Disables the Tauri-injected CSP sources.
1286  ///
1287  /// At compile time, Tauri parses all the frontend assets and changes the Content-Security-Policy
1288  /// to only allow loading of your own scripts and styles by injecting nonce and hash sources.
1289  /// This stricts your CSP, which may introduce issues when using along with other flexing sources.
1290  ///
1291  /// This configuration option allows both a boolean and a list of strings as value.
1292  /// A boolean instructs Tauri to disable the injection for all CSP injections,
1293  /// and a list of strings indicates the CSP directives that Tauri cannot inject.
1294  ///
1295  /// **WARNING:** Only disable this if you know what you are doing and have properly configured the CSP.
1296  /// Your application might be vulnerable to XSS attacks without this Tauri protection.
1297  #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
1298  pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
1299  /// Allow external domains to send command to Tauri.
1300  ///
1301  /// By default, external domains do not have access to `window.__TAURI__`, which means they cannot
1302  /// communicate with the commands defined in Rust. This prevents attacks where an externally
1303  /// loaded malicious or compromised sites could start executing commands on the user's device.
1304  ///
1305  /// This configuration allows a set of external domains to have access to the Tauri commands.
1306  /// When you configure a domain to be allowed to access the IPC, all subpaths are allowed. Subdomains are not allowed.
1307  ///
1308  /// **WARNING:** Only use this option if you either have internal checks against malicious
1309  /// external sites or you can trust the allowed external sites. You application might be
1310  /// vulnerable to dangerous Tauri command related attacks otherwise.
1311  #[serde(default, alias = "dangerous-remote-domain-ipc-access")]
1312  pub dangerous_remote_domain_ipc_access: Vec<RemoteDomainAccessScope>,
1313  /// Sets whether the custom protocols should use `http://<scheme>.localhost` instead of the default `https://<scheme>.localhost` on Windows.
1314  ///
1315  /// **WARNING:** Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints and is therefore less secure but will match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
1316  #[serde(default, alias = "dangerous-use-http-scheme")]
1317  pub dangerous_use_http_scheme: bool,
1318}
1319
1320/// Defines an allowlist type.
1321pub trait Allowlist {
1322  /// Returns all features associated with the allowlist struct.
1323  fn all_features() -> Vec<&'static str>;
1324  /// Returns the tauri features enabled on this allowlist.
1325  fn to_features(&self) -> Vec<&'static str>;
1326}
1327
1328macro_rules! check_feature {
1329  ($self:ident, $features:ident, $flag:ident, $feature_name: expr) => {
1330    if $self.$flag {
1331      $features.push($feature_name)
1332    }
1333  };
1334}
1335
1336/// Filesystem scope definition.
1337/// It is a list of glob patterns that restrict the API access from the webview.
1338///
1339/// Each pattern can start with a variable that resolves to a system base directory.
1340/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
1341/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
1342/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
1343/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
1344#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1345#[serde(untagged)]
1346#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1347pub enum FsAllowlistScope {
1348  /// A list of paths that are allowed by this scope.
1349  AllowedPaths(Vec<PathBuf>),
1350  /// A complete scope configuration.
1351  #[serde(rename_all = "camelCase")]
1352  Scope {
1353    /// A list of paths that are allowed by this scope.
1354    #[serde(default)]
1355    allow: Vec<PathBuf>,
1356    /// A list of paths that are not allowed by this scope.
1357    /// This gets precedence over the [`Self::Scope::allow`] list.
1358    #[serde(default)]
1359    deny: Vec<PathBuf>,
1360    /// Whether or not paths that contain components that start with a `.`
1361    /// will require that `.` appears literally in the pattern; `*`, `?`, `**`,
1362    /// or `[...]` will not match. This is useful because such files are
1363    /// conventionally considered hidden on Unix systems and it might be
1364    /// desirable to skip them when listing files.
1365    ///
1366    /// Defaults to `true` on Unix systems and `false` on Windows
1367    // dotfiles are not supposed to be exposed by default on unix
1368    #[serde(alias = "require-literal-leading-dot")]
1369    require_literal_leading_dot: Option<bool>,
1370  },
1371}
1372
1373impl Default for FsAllowlistScope {
1374  fn default() -> Self {
1375    Self::AllowedPaths(Vec::new())
1376  }
1377}
1378
1379/// Allowlist for the file system APIs.
1380///
1381/// See more: https://tauri.app/v1/api/config#fsallowlistconfig
1382#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1383#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1384#[serde(rename_all = "camelCase", deny_unknown_fields)]
1385pub struct FsAllowlistConfig {
1386  /// The access scope for the filesystem APIs.
1387  #[serde(default)]
1388  pub scope: FsAllowlistScope,
1389  /// Use this flag to enable all file system API features.
1390  #[serde(default)]
1391  pub all: bool,
1392  /// Read file from local filesystem.
1393  #[serde(default, alias = "read-file")]
1394  pub read_file: bool,
1395  /// Write file to local filesystem.
1396  #[serde(default, alias = "write-file")]
1397  pub write_file: bool,
1398  /// Read directory from local filesystem.
1399  #[serde(default, alias = "read-dir")]
1400  pub read_dir: bool,
1401  /// Copy file from local filesystem.
1402  #[serde(default, alias = "copy-file")]
1403  pub copy_file: bool,
1404  /// Create directory from local filesystem.
1405  #[serde(default, alias = "create-dir")]
1406  pub create_dir: bool,
1407  /// Remove directory from local filesystem.
1408  #[serde(default, alias = "remove-dir")]
1409  pub remove_dir: bool,
1410  /// Remove file from local filesystem.
1411  #[serde(default, alias = "remove-file")]
1412  pub remove_file: bool,
1413  /// Rename file from local filesystem.
1414  #[serde(default, alias = "rename-file")]
1415  pub rename_file: bool,
1416  /// Check if path exists on the local filesystem.
1417  #[serde(default)]
1418  pub exists: bool,
1419}
1420
1421impl Allowlist for FsAllowlistConfig {
1422  fn all_features() -> Vec<&'static str> {
1423    let allowlist = Self {
1424      scope: Default::default(),
1425      all: false,
1426      read_file: true,
1427      write_file: true,
1428      read_dir: true,
1429      copy_file: true,
1430      create_dir: true,
1431      remove_dir: true,
1432      remove_file: true,
1433      rename_file: true,
1434      exists: true,
1435    };
1436    let mut features = allowlist.to_features();
1437    features.push("fs-all");
1438    features
1439  }
1440
1441  fn to_features(&self) -> Vec<&'static str> {
1442    if self.all {
1443      vec!["fs-all"]
1444    } else {
1445      let mut features = Vec::new();
1446      check_feature!(self, features, read_file, "fs-read-file");
1447      check_feature!(self, features, write_file, "fs-write-file");
1448      check_feature!(self, features, read_dir, "fs-read-dir");
1449      check_feature!(self, features, copy_file, "fs-copy-file");
1450      check_feature!(self, features, create_dir, "fs-create-dir");
1451      check_feature!(self, features, remove_dir, "fs-remove-dir");
1452      check_feature!(self, features, remove_file, "fs-remove-file");
1453      check_feature!(self, features, rename_file, "fs-rename-file");
1454      check_feature!(self, features, exists, "fs-exists");
1455      features
1456    }
1457  }
1458}
1459
1460/// Allowlist for the window APIs.
1461///
1462/// See more: https://tauri.app/v1/api/config#windowallowlistconfig
1463#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1464#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1465#[serde(rename_all = "camelCase", deny_unknown_fields)]
1466pub struct WindowAllowlistConfig {
1467  /// Use this flag to enable all window API features.
1468  #[serde(default)]
1469  pub all: bool,
1470  /// Allows dynamic window creation.
1471  #[serde(default)]
1472  pub create: bool,
1473  /// Allows centering the window.
1474  #[serde(default)]
1475  pub center: bool,
1476  /// Allows requesting user attention on the window.
1477  #[serde(default, alias = "request-user-attention")]
1478  pub request_user_attention: bool,
1479  /// Allows setting the resizable flag of the window.
1480  #[serde(default, alias = "set-resizable")]
1481  pub set_resizable: bool,
1482  /// Allows setting whether the window's native maximize button is enabled or not.
1483  #[serde(default, alias = "set-maximizable")]
1484  pub set_maximizable: bool,
1485  /// Allows setting whether the window's native minimize button is enabled or not.
1486  #[serde(default, alias = "set-minimizable")]
1487  pub set_minimizable: bool,
1488  /// Allows setting whether the window's native close button is enabled or not.
1489  #[serde(default, alias = "set-closable")]
1490  pub set_closable: bool,
1491  /// Allows changing the window title.
1492  #[serde(default, alias = "set-title")]
1493  pub set_title: bool,
1494  /// Allows maximizing the window.
1495  #[serde(default)]
1496  pub maximize: bool,
1497  /// Allows unmaximizing the window.
1498  #[serde(default)]
1499  pub unmaximize: bool,
1500  /// Allows minimizing the window.
1501  #[serde(default)]
1502  pub minimize: bool,
1503  /// Allows unminimizing the window.
1504  #[serde(default)]
1505  pub unminimize: bool,
1506  /// Allows showing the window.
1507  #[serde(default)]
1508  pub show: bool,
1509  /// Allows hiding the window.
1510  #[serde(default)]
1511  pub hide: bool,
1512  /// Allows closing the window.
1513  #[serde(default)]
1514  pub close: bool,
1515  /// Allows setting the decorations flag of the window.
1516  #[serde(default, alias = "set-decorations")]
1517  pub set_decorations: bool,
1518  /// Allows setting the always_on_top flag of the window.
1519  #[serde(default, alias = "set-always-on-top")]
1520  pub set_always_on_top: bool,
1521  /// Allows preventing the window contents from being captured by other apps.
1522  #[serde(default, alias = "set-content-protected")]
1523  pub set_content_protected: bool,
1524  /// Allows setting the window size.
1525  #[serde(default, alias = "set-size")]
1526  pub set_size: bool,
1527  /// Allows setting the window minimum size.
1528  #[serde(default, alias = "set-min-size")]
1529  pub set_min_size: bool,
1530  /// Allows setting the window maximum size.
1531  #[serde(default, alias = "set-max-size")]
1532  pub set_max_size: bool,
1533  /// Allows changing the position of the window.
1534  #[serde(default, alias = "set-position")]
1535  pub set_position: bool,
1536  /// Allows setting the fullscreen flag of the window.
1537  #[serde(default, alias = "set-fullscreen")]
1538  pub set_fullscreen: bool,
1539  /// Allows focusing the window.
1540  #[serde(default, alias = "set-focus")]
1541  pub set_focus: bool,
1542  /// Allows changing the window icon.
1543  #[serde(default, alias = "set-icon")]
1544  pub set_icon: bool,
1545  /// Allows setting the skip_taskbar flag of the window.
1546  #[serde(default, alias = "set-skip-taskbar")]
1547  pub set_skip_taskbar: bool,
1548  /// Allows grabbing the cursor.
1549  #[serde(default, alias = "set-cursor-grab")]
1550  pub set_cursor_grab: bool,
1551  /// Allows setting the cursor visibility.
1552  #[serde(default, alias = "set-cursor-visible")]
1553  pub set_cursor_visible: bool,
1554  /// Allows changing the cursor icon.
1555  #[serde(default, alias = "set-cursor-icon")]
1556  pub set_cursor_icon: bool,
1557  /// Allows setting the cursor position.
1558  #[serde(default, alias = "set-cursor-position")]
1559  pub set_cursor_position: bool,
1560  /// Allows ignoring cursor events.
1561  #[serde(default, alias = "set-ignore-cursor-events")]
1562  pub set_ignore_cursor_events: bool,
1563  /// Allows start dragging on the window.
1564  #[serde(default, alias = "start-dragging")]
1565  pub start_dragging: bool,
1566  /// Allows opening the system dialog to print the window content.
1567  #[serde(default)]
1568  pub print: bool,
1569}
1570
1571impl Allowlist for WindowAllowlistConfig {
1572  fn all_features() -> Vec<&'static str> {
1573    let allowlist = Self {
1574      all: false,
1575      create: true,
1576      center: true,
1577      request_user_attention: true,
1578      set_resizable: true,
1579      set_maximizable: true,
1580      set_minimizable: true,
1581      set_closable: true,
1582      set_title: true,
1583      maximize: true,
1584      unmaximize: true,
1585      minimize: true,
1586      unminimize: true,
1587      show: true,
1588      hide: true,
1589      close: true,
1590      set_decorations: true,
1591      set_always_on_top: true,
1592      set_content_protected: false,
1593      set_size: true,
1594      set_min_size: true,
1595      set_max_size: true,
1596      set_position: true,
1597      set_fullscreen: true,
1598      set_focus: true,
1599      set_icon: true,
1600      set_skip_taskbar: true,
1601      set_cursor_grab: true,
1602      set_cursor_visible: true,
1603      set_cursor_icon: true,
1604      set_cursor_position: true,
1605      set_ignore_cursor_events: true,
1606      start_dragging: true,
1607      print: true,
1608    };
1609    let mut features = allowlist.to_features();
1610    features.push("window-all");
1611    features
1612  }
1613
1614  fn to_features(&self) -> Vec<&'static str> {
1615    if self.all {
1616      vec!["window-all"]
1617    } else {
1618      let mut features = Vec::new();
1619      check_feature!(self, features, create, "window-create");
1620      check_feature!(self, features, center, "window-center");
1621      check_feature!(
1622        self,
1623        features,
1624        request_user_attention,
1625        "window-request-user-attention"
1626      );
1627      check_feature!(self, features, set_resizable, "window-set-resizable");
1628      check_feature!(self, features, set_maximizable, "window-set-maximizable");
1629      check_feature!(self, features, set_minimizable, "window-set-minimizable");
1630      check_feature!(self, features, set_closable, "window-set-closable");
1631      check_feature!(self, features, set_title, "window-set-title");
1632      check_feature!(self, features, maximize, "window-maximize");
1633      check_feature!(self, features, unmaximize, "window-unmaximize");
1634      check_feature!(self, features, minimize, "window-minimize");
1635      check_feature!(self, features, unminimize, "window-unminimize");
1636      check_feature!(self, features, show, "window-show");
1637      check_feature!(self, features, hide, "window-hide");
1638      check_feature!(self, features, close, "window-close");
1639      check_feature!(self, features, set_decorations, "window-set-decorations");
1640      check_feature!(
1641        self,
1642        features,
1643        set_always_on_top,
1644        "window-set-always-on-top"
1645      );
1646      check_feature!(
1647        self,
1648        features,
1649        set_content_protected,
1650        "window-set-content-protected"
1651      );
1652      check_feature!(self, features, set_size, "window-set-size");
1653      check_feature!(self, features, set_min_size, "window-set-min-size");
1654      check_feature!(self, features, set_max_size, "window-set-max-size");
1655      check_feature!(self, features, set_position, "window-set-position");
1656      check_feature!(self, features, set_fullscreen, "window-set-fullscreen");
1657      check_feature!(self, features, set_focus, "window-set-focus");
1658      check_feature!(self, features, set_icon, "window-set-icon");
1659      check_feature!(self, features, set_skip_taskbar, "window-set-skip-taskbar");
1660      check_feature!(self, features, set_cursor_grab, "window-set-cursor-grab");
1661      check_feature!(
1662        self,
1663        features,
1664        set_cursor_visible,
1665        "window-set-cursor-visible"
1666      );
1667      check_feature!(self, features, set_cursor_icon, "window-set-cursor-icon");
1668      check_feature!(
1669        self,
1670        features,
1671        set_cursor_position,
1672        "window-set-cursor-position"
1673      );
1674      check_feature!(
1675        self,
1676        features,
1677        set_ignore_cursor_events,
1678        "window-set-ignore-cursor-events"
1679      );
1680      check_feature!(self, features, start_dragging, "window-start-dragging");
1681      check_feature!(self, features, print, "window-print");
1682      features
1683    }
1684  }
1685}
1686
1687/// A command allowed to be executed by the webview API.
1688#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
1689#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1690pub struct ShellAllowedCommand {
1691  /// The name for this allowed shell command configuration.
1692  ///
1693  /// This name will be used inside of the webview API to call this command along with
1694  /// any specified arguments.
1695  pub name: String,
1696
1697  /// The command name.
1698  /// It can start with a variable that resolves to a system base directory.
1699  /// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
1700  /// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
1701  /// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
1702  /// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
1703  #[serde(rename = "cmd", default)] // use default just so the schema doesn't flag it as required
1704  pub command: PathBuf,
1705
1706  /// The allowed arguments for the command execution.
1707  #[serde(default)]
1708  pub args: ShellAllowedArgs,
1709
1710  /// If this command is a sidecar command.
1711  #[serde(default)]
1712  pub sidecar: bool,
1713}
1714
1715impl<'de> Deserialize<'de> for ShellAllowedCommand {
1716  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1717  where
1718    D: Deserializer<'de>,
1719  {
1720    #[derive(Deserialize)]
1721    struct InnerShellAllowedCommand {
1722      name: String,
1723      #[serde(rename = "cmd")]
1724      command: Option<PathBuf>,
1725      #[serde(default)]
1726      args: ShellAllowedArgs,
1727      #[serde(default)]
1728      sidecar: bool,
1729    }
1730
1731    let config = InnerShellAllowedCommand::deserialize(deserializer)?;
1732
1733    if !config.sidecar && config.command.is_none() {
1734      return Err(DeError::custom(
1735        "The shell scope `command` value is required.",
1736      ));
1737    }
1738
1739    Ok(ShellAllowedCommand {
1740      name: config.name,
1741      command: config.command.unwrap_or_default(),
1742      args: config.args,
1743      sidecar: config.sidecar,
1744    })
1745  }
1746}
1747
1748/// A set of command arguments allowed to be executed by the webview API.
1749///
1750/// A value of `true` will allow any arguments to be passed to the command. `false` will disable all
1751/// arguments. A list of [`ShellAllowedArg`] will set those arguments as the only valid arguments to
1752/// be passed to the attached command configuration.
1753#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1754#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1755#[serde(untagged, deny_unknown_fields)]
1756#[non_exhaustive]
1757pub enum ShellAllowedArgs {
1758  /// Use a simple boolean to allow all or disable all arguments to this command configuration.
1759  Flag(bool),
1760
1761  /// A specific set of [`ShellAllowedArg`] that are valid to call for the command configuration.
1762  List(Vec<ShellAllowedArg>),
1763}
1764
1765impl Default for ShellAllowedArgs {
1766  fn default() -> Self {
1767    Self::Flag(false)
1768  }
1769}
1770
1771/// A command argument allowed to be executed by the webview API.
1772#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1773#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1774#[serde(untagged, deny_unknown_fields)]
1775#[non_exhaustive]
1776pub enum ShellAllowedArg {
1777  /// A non-configurable argument that is passed to the command in the order it was specified.
1778  Fixed(String),
1779
1780  /// A variable that is set while calling the command from the webview API.
1781  ///
1782  Var {
1783    /// [regex] validator to require passed values to conform to an expected input.
1784    ///
1785    /// This will require the argument value passed to this variable to match the `validator` regex
1786    /// before it will be executed.
1787    ///
1788    /// [regex]: https://docs.rs/regex/latest/regex/#syntax
1789    validator: String,
1790  },
1791}
1792
1793/// Shell scope definition.
1794/// It is a list of command names and associated CLI arguments that restrict the API access from the webview.
1795#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1796#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1797pub struct ShellAllowlistScope(pub Vec<ShellAllowedCommand>);
1798
1799/// Defines the `shell > open` api scope.
1800#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1801#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1802#[serde(untagged, deny_unknown_fields)]
1803#[non_exhaustive]
1804pub enum ShellAllowlistOpen {
1805  /// If the shell open API should be enabled.
1806  ///
1807  /// If enabled, the default validation regex (`^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`) is used.
1808  Flag(bool),
1809
1810  /// Enable the shell open API, with a custom regex that the opened path must match against.
1811  ///
1812  /// If using a custom regex to support a non-http(s) schema, care should be used to prevent values
1813  /// that allow flag-like strings to pass validation. e.g. `--enable-debugging`, `-i`, `/R`.
1814  Validate(String),
1815}
1816
1817impl Default for ShellAllowlistOpen {
1818  fn default() -> Self {
1819    Self::Flag(false)
1820  }
1821}
1822
1823/// Allowlist for the shell APIs.
1824///
1825/// See more: https://tauri.app/v1/api/config#shellallowlistconfig
1826#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1827#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1828#[serde(rename_all = "camelCase", deny_unknown_fields)]
1829pub struct ShellAllowlistConfig {
1830  /// Access scope for the binary execution APIs.
1831  /// Sidecars are automatically enabled.
1832  #[serde(default)]
1833  pub scope: ShellAllowlistScope,
1834  /// Use this flag to enable all shell API features.
1835  #[serde(default)]
1836  pub all: bool,
1837  /// Enable binary execution.
1838  #[serde(default)]
1839  pub execute: bool,
1840  /// Enable sidecar execution, allowing the JavaScript layer to spawn a sidecar command,
1841  /// an executable that is shipped with the application.
1842  /// For more information see <https://tauri.app/v1/guides/building/sidecar>.
1843  #[serde(default)]
1844  pub sidecar: bool,
1845  /// Open URL with the user's default application.
1846  #[serde(default)]
1847  pub open: ShellAllowlistOpen,
1848}
1849
1850impl Allowlist for ShellAllowlistConfig {
1851  fn all_features() -> Vec<&'static str> {
1852    let allowlist = Self {
1853      scope: Default::default(),
1854      all: false,
1855      execute: true,
1856      sidecar: true,
1857      open: ShellAllowlistOpen::Flag(true),
1858    };
1859    let mut features = allowlist.to_features();
1860    features.push("shell-all");
1861    features
1862  }
1863
1864  fn to_features(&self) -> Vec<&'static str> {
1865    if self.all {
1866      vec!["shell-all"]
1867    } else {
1868      let mut features = Vec::new();
1869      check_feature!(self, features, execute, "shell-execute");
1870      check_feature!(self, features, sidecar, "shell-sidecar");
1871
1872      if !matches!(self.open, ShellAllowlistOpen::Flag(false)) {
1873        features.push("shell-open")
1874      }
1875
1876      features
1877    }
1878  }
1879}
1880
1881/// Allowlist for the dialog APIs.
1882///
1883/// See more: https://tauri.app/v1/api/config#dialogallowlistconfig
1884#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1885#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1886#[serde(rename_all = "camelCase", deny_unknown_fields)]
1887pub struct DialogAllowlistConfig {
1888  /// Use this flag to enable all dialog API features.
1889  #[serde(default)]
1890  pub all: bool,
1891  /// Allows the API to open a dialog window to pick files.
1892  #[serde(default)]
1893  pub open: bool,
1894  /// Allows the API to open a dialog window to pick where to save files.
1895  #[serde(default)]
1896  pub save: bool,
1897  /// Allows the API to show a message dialog window.
1898  #[serde(default)]
1899  pub message: bool,
1900  /// Allows the API to show a dialog window with Yes/No buttons.
1901  #[serde(default)]
1902  pub ask: bool,
1903  /// Allows the API to show a dialog window with Ok/Cancel buttons.
1904  #[serde(default)]
1905  pub confirm: bool,
1906}
1907
1908impl Allowlist for DialogAllowlistConfig {
1909  fn all_features() -> Vec<&'static str> {
1910    let allowlist = Self {
1911      all: false,
1912      open: true,
1913      save: true,
1914      message: true,
1915      ask: true,
1916      confirm: true,
1917    };
1918    let mut features = allowlist.to_features();
1919    features.push("dialog-all");
1920    features
1921  }
1922
1923  fn to_features(&self) -> Vec<&'static str> {
1924    if self.all {
1925      vec!["dialog-all"]
1926    } else {
1927      let mut features = Vec::new();
1928      check_feature!(self, features, open, "dialog-open");
1929      check_feature!(self, features, save, "dialog-save");
1930      check_feature!(self, features, message, "dialog-message");
1931      check_feature!(self, features, ask, "dialog-ask");
1932      check_feature!(self, features, confirm, "dialog-confirm");
1933      features
1934    }
1935  }
1936}
1937
1938/// HTTP API scope definition.
1939/// It is a list of URLs that can be accessed by the webview when using the HTTP APIs.
1940/// The scoped URL is matched against the request URL using a glob pattern.
1941///
1942/// Examples:
1943/// - "https://*": allows all HTTPS urls
1944/// - "https://*.github.com/tauri-apps/tauri": allows any subdomain of "github.com" with the "tauri-apps/api" path
1945/// - "https://myapi.service.com/users/*": allows access to any URLs that begins with "https://myapi.service.com/users/"
1946#[allow(rustdoc::bare_urls)]
1947#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1948// TODO: in v2, parse into a String or a custom type that perserves the
1949// glob string because Url type will add a trailing slash
1950#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1951pub struct HttpAllowlistScope(pub Vec<Url>);
1952
1953/// Allowlist for the HTTP APIs.
1954///
1955/// See more: https://tauri.app/v1/api/config#httpallowlistconfig
1956#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1957#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1958#[serde(rename_all = "camelCase", deny_unknown_fields)]
1959pub struct HttpAllowlistConfig {
1960  /// The access scope for the HTTP APIs.
1961  #[serde(default)]
1962  pub scope: HttpAllowlistScope,
1963  /// Use this flag to enable all HTTP API features.
1964  #[serde(default)]
1965  pub all: bool,
1966  /// Allows making HTTP requests.
1967  #[serde(default)]
1968  pub request: bool,
1969}
1970
1971impl Allowlist for HttpAllowlistConfig {
1972  fn all_features() -> Vec<&'static str> {
1973    let allowlist = Self {
1974      scope: Default::default(),
1975      all: false,
1976      request: true,
1977    };
1978    let mut features = allowlist.to_features();
1979    features.push("http-all");
1980    features
1981  }
1982
1983  fn to_features(&self) -> Vec<&'static str> {
1984    if self.all {
1985      vec!["http-all"]
1986    } else {
1987      let mut features = Vec::new();
1988      check_feature!(self, features, request, "http-request");
1989      features
1990    }
1991  }
1992}
1993
1994/// Allowlist for the notification APIs.
1995///
1996/// See more: https://tauri.app/v1/api/config#notificationallowlistconfig
1997#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1998#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1999#[serde(rename_all = "camelCase", deny_unknown_fields)]
2000pub struct NotificationAllowlistConfig {
2001  /// Use this flag to enable all notification API features.
2002  #[serde(default)]
2003  pub all: bool,
2004}
2005
2006impl Allowlist for NotificationAllowlistConfig {
2007  fn all_features() -> Vec<&'static str> {
2008    let allowlist = Self { all: false };
2009    let mut features = allowlist.to_features();
2010    features.push("notification-all");
2011    features
2012  }
2013
2014  fn to_features(&self) -> Vec<&'static str> {
2015    if self.all {
2016      vec!["notification-all"]
2017    } else {
2018      vec![]
2019    }
2020  }
2021}
2022
2023/// Allowlist for the global shortcut APIs.
2024///
2025/// See more: https://tauri.app/v1/api/config#globalshortcutallowlistconfig
2026#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2027#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2028#[serde(rename_all = "camelCase", deny_unknown_fields)]
2029pub struct GlobalShortcutAllowlistConfig {
2030  /// Use this flag to enable all global shortcut API features.
2031  #[serde(default)]
2032  pub all: bool,
2033}
2034
2035impl Allowlist for GlobalShortcutAllowlistConfig {
2036  fn all_features() -> Vec<&'static str> {
2037    let allowlist = Self { all: false };
2038    let mut features = allowlist.to_features();
2039    features.push("global-shortcut-all");
2040    features
2041  }
2042
2043  fn to_features(&self) -> Vec<&'static str> {
2044    if self.all {
2045      vec!["global-shortcut-all"]
2046    } else {
2047      vec![]
2048    }
2049  }
2050}
2051
2052/// Allowlist for the OS APIs.
2053///
2054/// See more: https://tauri.app/v1/api/config#osallowlistconfig
2055#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2056#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2057#[serde(rename_all = "camelCase", deny_unknown_fields)]
2058pub struct OsAllowlistConfig {
2059  /// Use this flag to enable all OS API features.
2060  #[serde(default)]
2061  pub all: bool,
2062}
2063
2064impl Allowlist for OsAllowlistConfig {
2065  fn all_features() -> Vec<&'static str> {
2066    let allowlist = Self { all: false };
2067    let mut features = allowlist.to_features();
2068    features.push("os-all");
2069    features
2070  }
2071
2072  fn to_features(&self) -> Vec<&'static str> {
2073    if self.all {
2074      vec!["os-all"]
2075    } else {
2076      vec![]
2077    }
2078  }
2079}
2080
2081/// Allowlist for the path APIs.
2082///
2083/// See more: https://tauri.app/v1/api/config#pathallowlistconfig
2084#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2085#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2086#[serde(rename_all = "camelCase", deny_unknown_fields)]
2087pub struct PathAllowlistConfig {
2088  /// Use this flag to enable all path API features.
2089  #[serde(default)]
2090  pub all: bool,
2091}
2092
2093impl Allowlist for PathAllowlistConfig {
2094  fn all_features() -> Vec<&'static str> {
2095    let allowlist = Self { all: false };
2096    let mut features = allowlist.to_features();
2097    features.push("path-all");
2098    features
2099  }
2100
2101  fn to_features(&self) -> Vec<&'static str> {
2102    if self.all {
2103      vec!["path-all"]
2104    } else {
2105      vec![]
2106    }
2107  }
2108}
2109
2110/// Allowlist for the custom protocols.
2111///
2112/// See more: https://tauri.app/v1/api/config#protocolallowlistconfig
2113#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2114#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2115#[serde(rename_all = "camelCase", deny_unknown_fields)]
2116pub struct ProtocolAllowlistConfig {
2117  /// The access scope for the asset protocol.
2118  #[serde(default, alias = "asset-scope")]
2119  pub asset_scope: FsAllowlistScope,
2120  /// Use this flag to enable all custom protocols.
2121  #[serde(default)]
2122  pub all: bool,
2123  /// Enables the asset protocol.
2124  #[serde(default)]
2125  pub asset: bool,
2126}
2127
2128impl Allowlist for ProtocolAllowlistConfig {
2129  fn all_features() -> Vec<&'static str> {
2130    let allowlist = Self {
2131      asset_scope: Default::default(),
2132      all: false,
2133      asset: true,
2134    };
2135    let mut features = allowlist.to_features();
2136    features.push("protocol-all");
2137    features
2138  }
2139
2140  fn to_features(&self) -> Vec<&'static str> {
2141    if self.all {
2142      vec!["protocol-all"]
2143    } else {
2144      let mut features = Vec::new();
2145      check_feature!(self, features, asset, "protocol-asset");
2146      features
2147    }
2148  }
2149}
2150
2151/// Allowlist for the process APIs.
2152///
2153/// See more: https://tauri.app/v1/api/config#processallowlistconfig
2154#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2155#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2156#[serde(rename_all = "camelCase", deny_unknown_fields)]
2157pub struct ProcessAllowlistConfig {
2158  /// Use this flag to enable all process APIs.
2159  #[serde(default)]
2160  pub all: bool,
2161  /// Enables the relaunch API.
2162  #[serde(default)]
2163  pub relaunch: bool,
2164  /// Dangerous option that allows macOS to relaunch even if the binary contains a symlink.
2165  ///
2166  /// This is due to macOS having less symlink protection. Highly recommended to not set this flag
2167  /// unless you have a very specific reason too, and understand the implications of it.
2168  #[serde(
2169    default,
2170    alias = "relaunchDangerousAllowSymlinkMacOS",
2171    alias = "relaunch-dangerous-allow-symlink-macos"
2172  )]
2173  pub relaunch_dangerous_allow_symlink_macos: bool,
2174  /// Enables the exit API.
2175  #[serde(default)]
2176  pub exit: bool,
2177}
2178
2179impl Allowlist for ProcessAllowlistConfig {
2180  fn all_features() -> Vec<&'static str> {
2181    let allowlist = Self {
2182      all: false,
2183      relaunch: true,
2184      relaunch_dangerous_allow_symlink_macos: false,
2185      exit: true,
2186    };
2187    let mut features = allowlist.to_features();
2188    features.push("process-all");
2189    features
2190  }
2191
2192  fn to_features(&self) -> Vec<&'static str> {
2193    if self.all {
2194      vec!["process-all"]
2195    } else {
2196      let mut features = Vec::new();
2197      check_feature!(self, features, relaunch, "process-relaunch");
2198      check_feature!(
2199        self,
2200        features,
2201        relaunch_dangerous_allow_symlink_macos,
2202        "process-relaunch-dangerous-allow-symlink-macos"
2203      );
2204      check_feature!(self, features, exit, "process-exit");
2205      features
2206    }
2207  }
2208}
2209
2210/// Allowlist for the clipboard APIs.
2211///
2212/// See more: https://tauri.app/v1/api/config#clipboardallowlistconfig
2213#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2214#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2215#[serde(rename_all = "camelCase", deny_unknown_fields)]
2216pub struct ClipboardAllowlistConfig {
2217  /// Use this flag to enable all clipboard APIs.
2218  #[serde(default)]
2219  pub all: bool,
2220  /// Enables the clipboard's `writeText` API.
2221  #[serde(default, alias = "writeText")]
2222  pub write_text: bool,
2223  /// Enables the clipboard's `readText` API.
2224  #[serde(default, alias = "readText")]
2225  pub read_text: bool,
2226}
2227
2228impl Allowlist for ClipboardAllowlistConfig {
2229  fn all_features() -> Vec<&'static str> {
2230    let allowlist = Self {
2231      all: false,
2232      write_text: true,
2233      read_text: true,
2234    };
2235    let mut features = allowlist.to_features();
2236    features.push("clipboard-all");
2237    features
2238  }
2239
2240  fn to_features(&self) -> Vec<&'static str> {
2241    if self.all {
2242      vec!["clipboard-all"]
2243    } else {
2244      let mut features = Vec::new();
2245      check_feature!(self, features, write_text, "clipboard-write-text");
2246      check_feature!(self, features, read_text, "clipboard-read-text");
2247      features
2248    }
2249  }
2250}
2251
2252/// Allowlist for the app APIs.
2253///
2254/// See more: https://tauri.app/v1/api/config#appallowlistconfig
2255#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2256#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2257#[serde(rename_all = "camelCase", deny_unknown_fields)]
2258pub struct AppAllowlistConfig {
2259  /// Use this flag to enable all app APIs.
2260  #[serde(default)]
2261  pub all: bool,
2262  /// Enables the app's `show` API.
2263  #[serde(default)]
2264  pub show: bool,
2265  /// Enables the app's `hide` API.
2266  #[serde(default)]
2267  pub hide: bool,
2268}
2269
2270impl Allowlist for AppAllowlistConfig {
2271  fn all_features() -> Vec<&'static str> {
2272    let allowlist = Self {
2273      all: false,
2274      show: true,
2275      hide: true,
2276    };
2277    let mut features = allowlist.to_features();
2278    features.push("app-all");
2279    features
2280  }
2281
2282  fn to_features(&self) -> Vec<&'static str> {
2283    if self.all {
2284      vec!["app-all"]
2285    } else {
2286      let mut features = Vec::new();
2287      check_feature!(self, features, show, "app-show");
2288      check_feature!(self, features, hide, "app-hide");
2289      features
2290    }
2291  }
2292}
2293
2294/// Allowlist configuration. The allowlist is a translation of the [Cargo allowlist features](https://docs.rs/tauri/latest/tauri/#cargo-allowlist-features).
2295///
2296/// # Notes
2297///
2298/// - Endpoints that don't have their own allowlist option are enabled by default.
2299/// - There is only "opt-in", no "opt-out". Setting an option to `false` has no effect.
2300///
2301/// # Examples
2302///
2303/// - * [`"app-all": true`](https://tauri.app/v1/api/config/#appallowlistconfig.all) will make the [hide](https://tauri.app/v1/api/js/app#hide) endpoint be available regardless of whether `hide` is set to `false` or `true` in the allowlist.
2304#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2305#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2306#[serde(rename_all = "camelCase", deny_unknown_fields)]
2307pub struct AllowlistConfig {
2308  /// Use this flag to enable all API features.
2309  #[serde(default)]
2310  pub all: bool,
2311  /// File system API allowlist.
2312  #[serde(default)]
2313  pub fs: FsAllowlistConfig,
2314  /// Window API allowlist.
2315  #[serde(default)]
2316  pub window: WindowAllowlistConfig,
2317  /// Shell API allowlist.
2318  #[serde(default)]
2319  pub shell: ShellAllowlistConfig,
2320  /// Dialog API allowlist.
2321  #[serde(default)]
2322  pub dialog: DialogAllowlistConfig,
2323  /// HTTP API allowlist.
2324  #[serde(default)]
2325  pub http: HttpAllowlistConfig,
2326  /// Notification API allowlist.
2327  #[serde(default)]
2328  pub notification: NotificationAllowlistConfig,
2329  /// Global shortcut API allowlist.
2330  #[serde(default, alias = "global-shortcut")]
2331  pub global_shortcut: GlobalShortcutAllowlistConfig,
2332  /// OS allowlist.
2333  #[serde(default)]
2334  pub os: OsAllowlistConfig,
2335  /// Path API allowlist.
2336  #[serde(default)]
2337  pub path: PathAllowlistConfig,
2338  /// Custom protocol allowlist.
2339  #[serde(default)]
2340  pub protocol: ProtocolAllowlistConfig,
2341  /// Process API allowlist.
2342  #[serde(default)]
2343  pub process: ProcessAllowlistConfig,
2344  /// Clipboard APIs allowlist.
2345  #[serde(default)]
2346  pub clipboard: ClipboardAllowlistConfig,
2347  /// App APIs allowlist.
2348  #[serde(default)]
2349  pub app: AppAllowlistConfig,
2350}
2351
2352impl Allowlist for AllowlistConfig {
2353  fn all_features() -> Vec<&'static str> {
2354    let mut features = vec!["api-all"];
2355    features.extend(FsAllowlistConfig::all_features());
2356    features.extend(WindowAllowlistConfig::all_features());
2357    features.extend(ShellAllowlistConfig::all_features());
2358    features.extend(DialogAllowlistConfig::all_features());
2359    features.extend(HttpAllowlistConfig::all_features());
2360    features.extend(NotificationAllowlistConfig::all_features());
2361    features.extend(GlobalShortcutAllowlistConfig::all_features());
2362    features.extend(OsAllowlistConfig::all_features());
2363    features.extend(PathAllowlistConfig::all_features());
2364    features.extend(ProtocolAllowlistConfig::all_features());
2365    features.extend(ProcessAllowlistConfig::all_features());
2366    features.extend(ClipboardAllowlistConfig::all_features());
2367    features.extend(AppAllowlistConfig::all_features());
2368    features
2369  }
2370
2371  fn to_features(&self) -> Vec<&'static str> {
2372    if self.all {
2373      vec!["api-all"]
2374    } else {
2375      let mut features = Vec::new();
2376      features.extend(self.fs.to_features());
2377      features.extend(self.window.to_features());
2378      features.extend(self.shell.to_features());
2379      features.extend(self.dialog.to_features());
2380      features.extend(self.http.to_features());
2381      features.extend(self.notification.to_features());
2382      features.extend(self.global_shortcut.to_features());
2383      features.extend(self.os.to_features());
2384      features.extend(self.path.to_features());
2385      features.extend(self.protocol.to_features());
2386      features.extend(self.process.to_features());
2387      features.extend(self.clipboard.to_features());
2388      features.extend(self.app.to_features());
2389      features
2390    }
2391  }
2392}
2393
2394/// The application pattern.
2395#[skip_serializing_none]
2396#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
2397#[serde(rename_all = "lowercase", tag = "use", content = "options")]
2398#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2399pub enum PatternKind {
2400  /// Brownfield pattern.
2401  Brownfield,
2402  /// Isolation pattern. Recommended for security purposes.
2403  Isolation {
2404    /// The dir containing the index.html file that contains the secure isolation application.
2405    dir: PathBuf,
2406  },
2407}
2408
2409impl Default for PatternKind {
2410  fn default() -> Self {
2411    Self::Brownfield
2412  }
2413}
2414
2415/// The Tauri configuration object.
2416///
2417/// See more: https://tauri.app/v1/api/config#tauriconfig
2418#[skip_serializing_none]
2419#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2420#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2421#[serde(rename_all = "camelCase", deny_unknown_fields)]
2422pub struct TauriConfig {
2423  /// The pattern to use.
2424  #[serde(default)]
2425  pub pattern: PatternKind,
2426  /// The windows configuration.
2427  #[serde(default)]
2428  pub windows: Vec<WindowConfig>,
2429  /// The CLI configuration.
2430  pub cli: Option<CliConfig>,
2431  /// The bundler configuration.
2432  #[serde(default)]
2433  pub bundle: BundleConfig,
2434  /// The allowlist configuration.
2435  #[serde(default)]
2436  pub allowlist: AllowlistConfig,
2437  /// Security configuration.
2438  #[serde(default)]
2439  pub security: SecurityConfig,
2440  /// The updater configuration.
2441  #[serde(default)]
2442  pub updater: UpdaterConfig,
2443  /// Configuration for app system tray.
2444  #[serde(alias = "system-tray")]
2445  pub system_tray: Option<SystemTrayConfig>,
2446  /// MacOS private API configuration. Enables the transparent background API and sets the `fullScreenEnabled` preference to `true`.
2447  #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
2448  pub macos_private_api: bool,
2449}
2450
2451/// A URL to an updater server.
2452///
2453/// The URL must use the `https` scheme on production.
2454#[skip_serializing_none]
2455#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
2456#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2457pub struct UpdaterEndpoint(pub Url);
2458
2459impl std::fmt::Display for UpdaterEndpoint {
2460  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2461    write!(f, "{}", self.0)
2462  }
2463}
2464
2465impl<'de> Deserialize<'de> for UpdaterEndpoint {
2466  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2467  where
2468    D: Deserializer<'de>,
2469  {
2470    let url = Url::deserialize(deserializer)?;
2471    #[cfg(all(not(debug_assertions), not(feature = "schema")))]
2472    {
2473      if url.scheme() != "https" {
2474        return Err(serde::de::Error::custom(
2475          "The configured updater endpoint must use the `https` protocol.",
2476        ));
2477      }
2478    }
2479    Ok(Self(url))
2480  }
2481}
2482
2483/// Install modes for the Windows update.
2484#[derive(Debug, PartialEq, Eq, Clone)]
2485#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2486#[cfg_attr(feature = "schema", schemars(rename_all = "camelCase"))]
2487pub enum WindowsUpdateInstallMode {
2488  /// Specifies there's a basic UI during the installation process, including a final dialog box at the end.
2489  BasicUi,
2490  /// The quiet mode means there's no user interaction required.
2491  /// Requires admin privileges if the installer does.
2492  Quiet,
2493  /// Specifies unattended mode, which means the installation only shows a progress bar.
2494  Passive,
2495  // to add more modes, we need to check if the updater relaunch makes sense
2496  // i.e. for a full UI mode, the user can also mark the installer to start the app
2497}
2498
2499impl Display for WindowsUpdateInstallMode {
2500  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2501    write!(
2502      f,
2503      "{}",
2504      match self {
2505        Self::BasicUi => "basicUI",
2506        Self::Quiet => "quiet",
2507        Self::Passive => "passive",
2508      }
2509    )
2510  }
2511}
2512
2513impl Default for WindowsUpdateInstallMode {
2514  fn default() -> Self {
2515    Self::Passive
2516  }
2517}
2518
2519impl Serialize for WindowsUpdateInstallMode {
2520  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
2521  where
2522    S: Serializer,
2523  {
2524    serializer.serialize_str(self.to_string().as_ref())
2525  }
2526}
2527
2528impl<'de> Deserialize<'de> for WindowsUpdateInstallMode {
2529  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
2530  where
2531    D: Deserializer<'de>,
2532  {
2533    let s = String::deserialize(deserializer)?;
2534    match s.to_lowercase().as_str() {
2535      "basicui" => Ok(Self::BasicUi),
2536      "quiet" => Ok(Self::Quiet),
2537      "passive" => Ok(Self::Passive),
2538      _ => Err(DeError::custom(format!(
2539        "unknown update install mode '{s}'"
2540      ))),
2541    }
2542  }
2543}
2544
2545/// The updater configuration for Windows.
2546///
2547/// See more: https://tauri.app/v1/api/config#updaterwindowsconfig
2548#[skip_serializing_none]
2549#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
2550#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2551#[serde(rename_all = "camelCase", deny_unknown_fields)]
2552pub struct UpdaterWindowsConfig {
2553  /// Additional arguments given to the NSIS or WiX installer.
2554  #[serde(default, alias = "installer-args")]
2555  pub installer_args: Vec<String>,
2556  /// The installation mode for the update on Windows. Defaults to `passive`.
2557  #[serde(default, alias = "install-mode")]
2558  pub install_mode: WindowsUpdateInstallMode,
2559}
2560
2561/// The Updater configuration object.
2562///
2563/// See more: https://tauri.app/v1/api/config#updaterconfig
2564#[skip_serializing_none]
2565#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
2566#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2567#[serde(rename_all = "camelCase", deny_unknown_fields)]
2568pub struct UpdaterConfig {
2569  /// Whether the updater is active or not.
2570  #[serde(default)]
2571  pub active: bool,
2572  /// Display built-in dialog or use event system if disabled.
2573  #[serde(default = "default_true")]
2574  pub dialog: bool,
2575  /// The updater endpoints. TLS is enforced on production.
2576  ///
2577  /// The updater URL can contain the following variables:
2578  /// - {{current_version}}: The version of the app that is requesting the update
2579  /// - {{target}}: The operating system name (one of `linux`, `windows` or `darwin`).
2580  /// - {{arch}}: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`).
2581  ///
2582  /// # Examples
2583  /// - "https://my.cdn.com/latest.json": a raw JSON endpoint that returns the latest version and download links for each platform.
2584  /// - "https://updates.app.dev/{{target}}?version={{current_version}}&arch={{arch}}": a dedicated API with positional and query string arguments.
2585  #[allow(rustdoc::bare_urls)]
2586  pub endpoints: Option<Vec<UpdaterEndpoint>>,
2587  /// Signature public key.
2588  #[serde(default)] // use default just so the schema doesn't flag it as required
2589  pub pubkey: String,
2590  /// The Windows configuration for the updater.
2591  #[serde(default)]
2592  pub windows: UpdaterWindowsConfig,
2593}
2594
2595impl<'de> Deserialize<'de> for UpdaterConfig {
2596  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2597  where
2598    D: Deserializer<'de>,
2599  {
2600    #[derive(Deserialize)]
2601    struct InnerUpdaterConfig {
2602      #[serde(default)]
2603      active: bool,
2604      #[serde(default = "default_true")]
2605      dialog: bool,
2606      endpoints: Option<Vec<UpdaterEndpoint>>,
2607      pubkey: Option<String>,
2608      #[serde(default)]
2609      windows: UpdaterWindowsConfig,
2610    }
2611
2612    let config = InnerUpdaterConfig::deserialize(deserializer)?;
2613
2614    if config.active && config.pubkey.is_none() {
2615      return Err(DeError::custom(
2616        "The updater `pubkey` configuration is required.",
2617      ));
2618    }
2619
2620    Ok(UpdaterConfig {
2621      active: config.active,
2622      dialog: config.dialog,
2623      endpoints: config.endpoints,
2624      pubkey: config.pubkey.unwrap_or_default(),
2625      windows: config.windows,
2626    })
2627  }
2628}
2629
2630impl Default for UpdaterConfig {
2631  fn default() -> Self {
2632    Self {
2633      active: false,
2634      dialog: true,
2635      endpoints: None,
2636      pubkey: "".into(),
2637      windows: Default::default(),
2638    }
2639  }
2640}
2641
2642/// Configuration for application system tray icon.
2643///
2644/// See more: https://tauri.app/v1/api/config#systemtrayconfig
2645#[skip_serializing_none]
2646#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2647#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2648#[serde(rename_all = "camelCase", deny_unknown_fields)]
2649pub struct SystemTrayConfig {
2650  /// Path to the default icon to use on the system tray.
2651  #[serde(alias = "icon-path")]
2652  pub icon_path: PathBuf,
2653  /// 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.
2654  #[serde(default, alias = "icon-as-template")]
2655  pub icon_as_template: bool,
2656  /// A Boolean value that determines whether the menu should appear when the tray icon receives a left click on macOS.
2657  #[serde(default = "default_true", alias = "menu-on-left-click")]
2658  pub menu_on_left_click: bool,
2659  /// Title for MacOS tray
2660  pub title: Option<String>,
2661}
2662
2663/// Defines the URL or assets to embed in the application.
2664#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2665#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2666#[serde(untagged, deny_unknown_fields)]
2667#[non_exhaustive]
2668pub enum AppUrl {
2669  /// The app's external URL, or the path to the directory containing the app assets.
2670  Url(WindowUrl),
2671  /// An array of files to embed on the app.
2672  Files(Vec<PathBuf>),
2673}
2674
2675impl std::fmt::Display for AppUrl {
2676  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2677    match self {
2678      Self::Url(url) => write!(f, "{url}"),
2679      Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
2680    }
2681  }
2682}
2683
2684/// Describes the shell command to run before `tauri dev`.
2685#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2686#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2687#[serde(rename_all = "camelCase", untagged)]
2688pub enum BeforeDevCommand {
2689  /// Run the given script with the default options.
2690  Script(String),
2691  /// Run the given script with custom options.
2692  ScriptWithOptions {
2693    /// The script to execute.
2694    script: String,
2695    /// The current working directory.
2696    cwd: Option<String>,
2697    /// Whether `tauri dev` should wait for the command to finish or not. Defaults to `false`.
2698    #[serde(default)]
2699    wait: bool,
2700  },
2701}
2702
2703/// Describes a shell command to be executed when a CLI hook is triggered.
2704#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2705#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2706#[serde(rename_all = "camelCase", untagged)]
2707pub enum HookCommand {
2708  /// Run the given script with the default options.
2709  Script(String),
2710  /// Run the given script with custom options.
2711  ScriptWithOptions {
2712    /// The script to execute.
2713    script: String,
2714    /// The current working directory.
2715    cwd: Option<String>,
2716  },
2717}
2718
2719/// The Build configuration object.
2720///
2721/// See more: https://tauri.app/v1/api/config#buildconfig
2722#[skip_serializing_none]
2723#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2724#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2725#[serde(rename_all = "camelCase", deny_unknown_fields)]
2726pub struct BuildConfig {
2727  /// The binary used to build and run the application.
2728  pub runner: Option<String>,
2729  /// The path to the application assets or URL to load in development.
2730  ///
2731  /// This is usually an URL to a dev server, which serves your application assets
2732  /// with live reloading. Most modern JavaScript bundlers provides a way to start a dev server by default.
2733  ///
2734  /// See [vite](https://vitejs.dev/guide/), [Webpack DevServer](https://webpack.js.org/configuration/dev-server/) and [sirv](https://github.com/lukeed/sirv)
2735  /// for examples on how to set up a dev server.
2736  #[serde(default = "default_dev_path", alias = "dev-path")]
2737  pub dev_path: AppUrl,
2738  /// The path to the application assets or URL to load in production.
2739  ///
2740  /// When a path relative to the configuration file is provided,
2741  /// it is read recursively and all files are embedded in the application binary.
2742  /// Tauri then looks for an `index.html` file unless you provide a custom window URL.
2743  ///
2744  /// You can also provide a list of paths to be embedded, which allows granular control over what files are added to the binary.
2745  /// In this case, all files are added to the root and you must reference it that way in your HTML files.
2746  ///
2747  /// When an URL is provided, the application won't have bundled assets
2748  /// and the application will load that URL by default.
2749  #[serde(default = "default_dist_dir", alias = "dist-dir")]
2750  pub dist_dir: AppUrl,
2751  /// A shell command to run before `tauri dev` kicks in.
2752  ///
2753  /// The TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG environment variables are set if you perform conditional compilation.
2754  #[serde(alias = "before-dev-command")]
2755  pub before_dev_command: Option<BeforeDevCommand>,
2756  /// A shell command to run before `tauri build` kicks in.
2757  ///
2758  /// The TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG environment variables are set if you perform conditional compilation.
2759  #[serde(alias = "before-build-command")]
2760  pub before_build_command: Option<HookCommand>,
2761  /// A shell command to run before the bundling phase in `tauri build` kicks in.
2762  ///
2763  /// The TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG environment variables are set if you perform conditional compilation.
2764  #[serde(alias = "before-bundle-command")]
2765  pub before_bundle_command: Option<HookCommand>,
2766  /// Features passed to `cargo` commands.
2767  pub features: Option<Vec<String>>,
2768  /// Whether we should inject the Tauri API on `window.__TAURI__` or not.
2769  #[serde(default, alias = "with-global-tauri")]
2770  pub with_global_tauri: bool,
2771}
2772
2773impl Default for BuildConfig {
2774  fn default() -> Self {
2775    Self {
2776      runner: None,
2777      dev_path: default_dev_path(),
2778      dist_dir: default_dist_dir(),
2779      before_dev_command: None,
2780      before_build_command: None,
2781      before_bundle_command: None,
2782      features: None,
2783      with_global_tauri: false,
2784    }
2785  }
2786}
2787
2788fn default_dev_path() -> AppUrl {
2789  AppUrl::Url(WindowUrl::External(
2790    Url::parse("http://localhost:8080").unwrap(),
2791  ))
2792}
2793
2794fn default_dist_dir() -> AppUrl {
2795  AppUrl::Url(WindowUrl::App("../dist".into()))
2796}
2797
2798#[derive(Debug, PartialEq, Eq)]
2799struct PackageVersion(String);
2800
2801impl<'d> serde::Deserialize<'d> for PackageVersion {
2802  fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<PackageVersion, D::Error> {
2803    struct PackageVersionVisitor;
2804
2805    impl<'d> Visitor<'d> for PackageVersionVisitor {
2806      type Value = PackageVersion;
2807
2808      fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2809        write!(
2810          formatter,
2811          "a semver string or a path to a package.json file"
2812        )
2813      }
2814
2815      fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
2816        let path = PathBuf::from(value);
2817        if path.exists() {
2818          let json_str = read_to_string(&path)
2819            .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2820          let package_json: serde_json::Value = serde_json::from_str(&json_str)
2821            .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2822          if let Some(obj) = package_json.as_object() {
2823            let version = obj
2824              .get("version")
2825              .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
2826              .as_str()
2827              .ok_or_else(|| {
2828                DeError::custom(format!("`{} > version` must be a string", path.display()))
2829              })?;
2830            Ok(PackageVersion(
2831              Version::from_str(version)
2832                .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2833                .to_string(),
2834            ))
2835          } else {
2836            Err(DeError::custom(
2837              "`package > version` value is not a path to a JSON object",
2838            ))
2839          }
2840        } else {
2841          Ok(PackageVersion(
2842            Version::from_str(value)
2843              .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2844              .to_string(),
2845          ))
2846        }
2847      }
2848    }
2849
2850    deserializer.deserialize_string(PackageVersionVisitor {})
2851  }
2852}
2853
2854/// The package configuration.
2855///
2856/// See more: https://tauri.app/v1/api/config#packageconfig
2857#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
2858#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2859#[serde(rename_all = "camelCase", deny_unknown_fields)]
2860pub struct PackageConfig {
2861  /// App name.
2862  #[serde(alias = "product-name")]
2863  #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
2864  pub product_name: Option<String>,
2865  /// 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.
2866  #[serde(deserialize_with = "version_deserializer", default)]
2867  pub version: Option<String>,
2868}
2869
2870fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
2871where
2872  D: Deserializer<'de>,
2873{
2874  Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
2875}
2876
2877/// The Tauri configuration object.
2878/// It is read from a file where you can define your frontend assets,
2879/// configure the bundler, enable the app updater, define a system tray,
2880/// enable APIs via the allowlist and more.
2881///
2882/// The configuration file is generated by the
2883/// [`tauri init`](https://tauri.app/v1/api/cli#init) command that lives in
2884/// your Tauri application source directory (src-tauri).
2885///
2886/// Once generated, you may modify it at will to customize your Tauri application.
2887///
2888/// ## File Formats
2889///
2890/// By default, the configuration is defined as a JSON file named `tauri.conf.json`.
2891///
2892/// Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.
2893/// The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.
2894/// The TOML file name is `Tauri.toml`.
2895///
2896/// ## Platform-Specific Configuration
2897///
2898/// In addition to the default configuration file, Tauri can
2899/// read a platform-specific configuration from `tauri.linux.conf.json`,
2900/// `tauri.windows.conf.json`, and `tauri.macos.conf.json`
2901/// (or `Tauri.linux.toml`, `Tauri.windows.toml` and `Tauri.macos.toml` if the `Tauri.toml` format is used),
2902/// which gets merged with the main configuration object.
2903///
2904/// ## Configuration Structure
2905///
2906/// The configuration is composed of the following objects:
2907///
2908/// - [`package`](#packageconfig): Package settings
2909/// - [`tauri`](#tauriconfig): The Tauri config
2910/// - [`build`](#buildconfig): The build configuration
2911/// - [`plugins`](#pluginconfig): The plugins config
2912///
2913/// ```json title="Example tauri.config.json file"
2914/// {
2915///   "build": {
2916///     "beforeBuildCommand": "",
2917///     "beforeDevCommand": "",
2918///     "devPath": "../dist",
2919///     "distDir": "../dist"
2920///   },
2921///   "package": {
2922///     "productName": "tauri-app",
2923///     "version": "0.1.0"
2924///   },
2925///   "tauri": {
2926///     "allowlist": {
2927///       "all": true
2928///     },
2929///     "bundle": {},
2930///     "security": {
2931///       "csp": null
2932///     },
2933///     "updater": {
2934///       "active": false
2935///     },
2936///     "windows": [
2937///       {
2938///         "fullscreen": false,
2939///         "height": 600,
2940///         "resizable": true,
2941///         "title": "Tauri App",
2942///         "width": 800
2943///       }
2944///     ]
2945///   }
2946/// }
2947/// ```
2948#[allow(rustdoc::invalid_codeblock_attributes)]
2949#[skip_serializing_none]
2950#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2951#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2952#[serde(rename_all = "camelCase", deny_unknown_fields)]
2953pub struct Config {
2954  /// The JSON schema for the Tauri config.
2955  #[serde(rename = "$schema")]
2956  pub schema: Option<String>,
2957  /// Package settings.
2958  #[serde(default)]
2959  pub package: PackageConfig,
2960  /// The Tauri configuration.
2961  #[serde(default)]
2962  pub tauri: TauriConfig,
2963  /// The build configuration.
2964  #[serde(default = "default_build")]
2965  pub build: BuildConfig,
2966  /// The plugins config.
2967  #[serde(default)]
2968  pub plugins: PluginConfig,
2969}
2970
2971/// The plugin configs holds a HashMap mapping a plugin name to its configuration object.
2972///
2973/// See more: https://tauri.app/v1/api/config#pluginconfig
2974#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
2975#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2976pub struct PluginConfig(pub HashMap<String, JsonValue>);
2977
2978fn default_build() -> BuildConfig {
2979  BuildConfig {
2980    runner: None,
2981    dev_path: default_dev_path(),
2982    dist_dir: default_dist_dir(),
2983    before_dev_command: None,
2984    before_build_command: None,
2985    before_bundle_command: None,
2986    features: None,
2987    with_global_tauri: false,
2988  }
2989}
2990
2991/// How the window title bar should be displayed on macOS.
2992#[derive(Debug, Clone, PartialEq, Eq)]
2993#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2994pub enum TitleBarStyle {
2995  /// A normal title bar.
2996  Visible,
2997  /// Makes the title bar transparent, so the window background color is shown instead.
2998  ///
2999  /// Useful if you don't need to have actual HTML under the title bar. This lets you avoid the caveats of using `TitleBarStyle::Overlay`. Will be more useful when Tauri lets you set a custom window background color.
3000  Transparent,
3001  /// Shows the title bar as a transparent overlay over the window's content.
3002  ///
3003  /// Keep in mind:
3004  /// - The height of the title bar is different on different OS versions, which can lead to window the controls and title not being where you don't expect.
3005  /// - You need to define a custom drag region to make your window draggable, however due to a limitation you can't drag the window when it's not in focus <https://github.com/tauri-apps/tauri/issues/4316>.
3006  /// - The color of the window title depends on the system theme.
3007  Overlay,
3008}
3009
3010impl Default for TitleBarStyle {
3011  fn default() -> Self {
3012    Self::Visible
3013  }
3014}
3015
3016impl Serialize for TitleBarStyle {
3017  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3018  where
3019    S: Serializer,
3020  {
3021    serializer.serialize_str(self.to_string().as_ref())
3022  }
3023}
3024
3025impl<'de> Deserialize<'de> for TitleBarStyle {
3026  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
3027  where
3028    D: Deserializer<'de>,
3029  {
3030    let s = String::deserialize(deserializer)?;
3031    Ok(match s.to_lowercase().as_str() {
3032      "transparent" => Self::Transparent,
3033      "overlay" => Self::Overlay,
3034      _ => Self::Visible,
3035    })
3036  }
3037}
3038
3039impl Display for TitleBarStyle {
3040  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3041    write!(
3042      f,
3043      "{}",
3044      match self {
3045        Self::Visible => "Visible",
3046        Self::Transparent => "Transparent",
3047        Self::Overlay => "Overlay",
3048      }
3049    )
3050  }
3051}
3052
3053/// System theme.
3054#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3055#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
3056#[non_exhaustive]
3057pub enum Theme {
3058  /// Light theme.
3059  Light,
3060  /// Dark theme.
3061  Dark,
3062}
3063
3064impl Serialize for Theme {
3065  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3066  where
3067    S: Serializer,
3068  {
3069    serializer.serialize_str(self.to_string().as_ref())
3070  }
3071}
3072
3073impl<'de> Deserialize<'de> for Theme {
3074  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
3075  where
3076    D: Deserializer<'de>,
3077  {
3078    let s = String::deserialize(deserializer)?;
3079    Ok(match s.to_lowercase().as_str() {
3080      "dark" => Self::Dark,
3081      _ => Self::Light,
3082    })
3083  }
3084}
3085
3086impl Display for Theme {
3087  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3088    write!(
3089      f,
3090      "{}",
3091      match self {
3092        Self::Light => "light",
3093        Self::Dark => "dark",
3094      }
3095    )
3096  }
3097}
3098
3099#[cfg(test)]
3100mod test {
3101  use super::*;
3102
3103  // TODO: create a test that compares a config to a json config
3104
3105  #[test]
3106  // test all of the default functions
3107  fn test_defaults() {
3108    // get default tauri config
3109    let t_config = TauriConfig::default();
3110    // get default build config
3111    let b_config = BuildConfig::default();
3112    // get default dev path
3113    let d_path = default_dev_path();
3114    // get default window
3115    let d_windows: Vec<WindowConfig> = vec![];
3116    // get default bundle
3117    let d_bundle = BundleConfig::default();
3118    // get default updater
3119    let d_updater = UpdaterConfig::default();
3120
3121    // create a tauri config.
3122    let tauri = TauriConfig {
3123      pattern: Default::default(),
3124      windows: vec![],
3125      bundle: BundleConfig {
3126        active: false,
3127        targets: Default::default(),
3128        identifier: String::from(""),
3129        publisher: None,
3130        icon: Vec::new(),
3131        resources: None,
3132        copyright: None,
3133        category: None,
3134        short_description: None,
3135        long_description: None,
3136        appimage: Default::default(),
3137        deb: Default::default(),
3138        macos: Default::default(),
3139        external_bin: None,
3140        windows: Default::default(),
3141      },
3142      cli: None,
3143      updater: UpdaterConfig {
3144        active: false,
3145        dialog: true,
3146        pubkey: "".into(),
3147        endpoints: None,
3148        windows: Default::default(),
3149      },
3150      security: SecurityConfig {
3151        csp: None,
3152        dev_csp: None,
3153        freeze_prototype: false,
3154        dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
3155        dangerous_remote_domain_ipc_access: Vec::new(),
3156        dangerous_use_http_scheme: false,
3157      },
3158      allowlist: AllowlistConfig::default(),
3159      system_tray: None,
3160      macos_private_api: false,
3161    };
3162
3163    // create a build config
3164    let build = BuildConfig {
3165      runner: None,
3166      dev_path: AppUrl::Url(WindowUrl::External(
3167        Url::parse("http://localhost:8080").unwrap(),
3168      )),
3169      dist_dir: AppUrl::Url(WindowUrl::App("../dist".into())),
3170      before_dev_command: None,
3171      before_build_command: None,
3172      before_bundle_command: None,
3173      features: None,
3174      with_global_tauri: false,
3175    };
3176
3177    // test the configs
3178    assert_eq!(t_config, tauri);
3179    assert_eq!(b_config, build);
3180    assert_eq!(d_bundle, tauri.bundle);
3181    assert_eq!(d_updater, tauri.updater);
3182    assert_eq!(
3183      d_path,
3184      AppUrl::Url(WindowUrl::External(
3185        Url::parse("http://localhost:8080").unwrap()
3186      ))
3187    );
3188    assert_eq!(d_windows, tauri.windows);
3189  }
3190}