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