Files
tauri/core/tauri-utils/src/config.rs
2023-03-28 16:04:44 +02:00

3905 lines
130 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! The Tauri configuration used at runtime.
//!
//! It is pulled from a `tauri.conf.json` file and the [`Config`] struct is generated at compile time.
//!
//! # Stability
//! This is a core functionality that is not considered part of the stable API.
//! If you use it, note that it may include breaking changes in the future.
#[cfg(target_os = "linux")]
use heck::ToKebabCase;
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use semver::Version;
use serde::{
de::{Deserializer, Error as DeError, Visitor},
Deserialize, Serialize, Serializer,
};
use serde_json::Value as JsonValue;
use serde_with::skip_serializing_none;
use url::Url;
use std::{
collections::HashMap,
fmt::{self, Display},
fs::read_to_string,
path::PathBuf,
str::FromStr,
};
/// Items to help with parsing content into a [`Config`].
pub mod parse;
use crate::TitleBarStyle;
pub use self::parse::parse;
/// An URL to open on a Tauri webview window.
#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
#[non_exhaustive]
pub enum WindowUrl {
/// An external URL.
External(Url),
/// The path portion of an app URL.
/// For instance, to load `tauri://localhost/users/john`,
/// you can simply provide `users/john` in this configuration.
App(PathBuf),
}
impl fmt::Display for WindowUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::External(url) => write!(f, "{url}"),
Self::App(path) => write!(f, "{}", path.display()),
}
}
}
impl Default for WindowUrl {
fn default() -> Self {
Self::App("index.html".into())
}
}
/// A bundle referenced by tauri-bundler.
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))]
pub enum BundleType {
/// The debian bundle (.deb).
Deb,
/// The AppImage bundle (.appimage).
AppImage,
/// The Microsoft Installer bundle (.msi).
Msi,
/// The NSIS bundle (.exe).
Nsis,
/// The macOS application bundle (.app).
App,
/// The Apple Disk Image bundle (.dmg).
Dmg,
/// The Tauri updater bundle.
Updater,
}
impl Display for BundleType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Deb => "deb",
Self::AppImage => "appimage",
Self::Msi => "msi",
Self::Nsis => "nsis",
Self::App => "app",
Self::Dmg => "dmg",
Self::Updater => "updater",
}
)
}
}
impl Serialize for BundleType {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
impl<'de> Deserialize<'de> for BundleType {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
"deb" => Ok(Self::Deb),
"appimage" => Ok(Self::AppImage),
"msi" => Ok(Self::Msi),
"nsis" => Ok(Self::Nsis),
"app" => Ok(Self::App),
"dmg" => Ok(Self::Dmg),
"updater" => Ok(Self::Updater),
_ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
}
}
}
/// Targets to bundle. Each value is case insensitive.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BundleTarget {
/// Bundle all targets.
All,
/// A list of bundle targets.
List(Vec<BundleType>),
/// A single bundle target.
One(BundleType),
}
#[cfg(feature = "schema")]
impl schemars::JsonSchema for BundleTarget {
fn schema_name() -> std::string::String {
"BundleTarget".to_owned()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
let any_of = vec![
schemars::schema::SchemaObject {
enum_values: Some(vec!["all".into()]),
metadata: Some(Box::new(schemars::schema::Metadata {
description: Some("Bundle all targets.".to_owned()),
..Default::default()
})),
..Default::default()
}
.into(),
schemars::_private::apply_metadata(
gen.subschema_for::<Vec<BundleType>>(),
schemars::schema::Metadata {
description: Some("A list of bundle targets.".to_owned()),
..Default::default()
},
),
schemars::_private::apply_metadata(
gen.subschema_for::<BundleType>(),
schemars::schema::Metadata {
description: Some("A single bundle target.".to_owned()),
..Default::default()
},
),
];
schemars::schema::SchemaObject {
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
any_of: Some(any_of),
..Default::default()
})),
metadata: Some(Box::new(schemars::schema::Metadata {
description: Some("Targets to bundle. Each value is case insensitive.".to_owned()),
..Default::default()
})),
..Default::default()
}
.into()
}
}
impl Default for BundleTarget {
fn default() -> Self {
Self::All
}
}
impl Serialize for BundleTarget {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::All => serializer.serialize_str("all"),
Self::List(l) => l.serialize(serializer),
Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
}
}
}
impl<'de> Deserialize<'de> for BundleTarget {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize, Serialize)]
#[serde(untagged)]
pub enum BundleTargetInner {
List(Vec<BundleType>),
One(BundleType),
All(String),
}
match BundleTargetInner::deserialize(deserializer)? {
BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {t}"))),
BundleTargetInner::List(l) => Ok(Self::List(l)),
BundleTargetInner::One(t) => Ok(Self::One(t)),
}
}
}
impl BundleTarget {
/// Gets the bundle targets as a [`Vec`]. The vector is empty when set to [`BundleTarget::All`].
#[allow(dead_code)]
pub fn to_vec(&self) -> Vec<BundleType> {
match self {
Self::All => vec![],
Self::List(list) => list.clone(),
Self::One(i) => vec![i.clone()],
}
}
}
/// Configuration for AppImage bundles.
///
/// See more: https://tauri.app/v1/api/config#appimageconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AppImageConfig {
/// Include additional gstreamer dependencies needed for audio and video playback.
/// This increases the bundle size by ~15-35MB depending on your build system.
#[serde(default, alias = "bundle-media-framework")]
pub bundle_media_framework: bool,
}
/// Configuration for Debian (.deb) bundles.
///
/// See more: https://tauri.app/v1/api/config#debconfig
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DebConfig {
/// The list of deb dependencies your application relies on.
pub depends: Option<Vec<String>>,
/// The files to include on the package.
#[serde(default)]
pub files: HashMap<PathBuf, PathBuf>,
}
fn de_minimum_system_version<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let version = Option::<String>::deserialize(deserializer)?;
match version {
Some(v) if v.is_empty() => Ok(minimum_system_version()),
e => Ok(e),
}
}
/// Configuration for the macOS bundles.
///
/// See more: https://tauri.app/v1/api/config#macconfig
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct MacConfig {
/// A list of strings indicating any macOS X frameworks that need to be bundled with the application.
///
/// 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.
pub frameworks: Option<Vec<String>>,
/// A version string indicating the minimum macOS X version that the bundled application supports. Defaults to `10.13`.
///
/// Setting it to `null` completely removes the `LSMinimumSystemVersion` field on the bundle's `Info.plist`
/// and the `MACOSX_DEPLOYMENT_TARGET` environment variable.
///
/// An empty string is considered an invalid value so the default value is used.
#[serde(
deserialize_with = "de_minimum_system_version",
default = "minimum_system_version",
alias = "minimum-system-version"
)]
pub minimum_system_version: Option<String>,
/// Allows your application to communicate with the outside world.
/// It should be a lowercase, without port and protocol domain name.
#[serde(alias = "exception-domain")]
pub exception_domain: Option<String>,
/// The path to the license file to add to the DMG bundle.
pub license: Option<String>,
/// Identity to use for code signing.
#[serde(alias = "signing-identity")]
pub signing_identity: Option<String>,
/// Provider short name for notarization.
#[serde(alias = "provider-short-name")]
pub provider_short_name: Option<String>,
/// Path to the entitlements file.
pub entitlements: Option<String>,
}
impl Default for MacConfig {
fn default() -> Self {
Self {
frameworks: None,
minimum_system_version: minimum_system_version(),
exception_domain: None,
license: None,
signing_identity: None,
provider_short_name: None,
entitlements: None,
}
}
}
fn minimum_system_version() -> Option<String> {
Some("10.13".into())
}
/// Configuration for a target language for the WiX build.
///
/// See more: https://tauri.app/v1/api/config#wixlanguageconfig
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WixLanguageConfig {
/// The path to a locale (`.wxl`) file. See <https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html>.
#[serde(alias = "locale-path")]
pub locale_path: Option<String>,
}
/// The languages to build using WiX.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum WixLanguage {
/// A single language to build, without configuration.
One(String),
/// A list of languages to build, without configuration.
List(Vec<String>),
/// A map of languages and its configuration.
Localized(HashMap<String, WixLanguageConfig>),
}
impl Default for WixLanguage {
fn default() -> Self {
Self::One("en-US".into())
}
}
/// Configuration for the MSI bundle using WiX.
///
/// See more: https://tauri.app/v1/api/config#wixconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WixConfig {
/// The installer languages to build. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.
#[serde(default)]
pub language: WixLanguage,
/// A custom .wxs template to use.
pub template: Option<PathBuf>,
/// A list of paths to .wxs files with WiX fragments to use.
#[serde(default, alias = "fragment-paths")]
pub fragment_paths: Vec<PathBuf>,
/// The ComponentGroup element ids you want to reference from the fragments.
#[serde(default, alias = "component-group-refs")]
pub component_group_refs: Vec<String>,
/// The Component element ids you want to reference from the fragments.
#[serde(default, alias = "component-refs")]
pub component_refs: Vec<String>,
/// The FeatureGroup element ids you want to reference from the fragments.
#[serde(default, alias = "feature-group-refs")]
pub feature_group_refs: Vec<String>,
/// The Feature element ids you want to reference from the fragments.
#[serde(default, alias = "feature-refs")]
pub feature_refs: Vec<String>,
/// The Merge element ids you want to reference from the fragments.
#[serde(default, alias = "merge-refs")]
pub merge_refs: Vec<String>,
/// Disables the Webview2 runtime installation after app install.
///
/// Will be removed in v2, prefer the [`WindowsConfig::webview_install_mode`] option.
#[serde(default, alias = "skip-webview-install")]
pub skip_webview_install: bool,
/// The path to the license file to render on the installer.
///
/// Must be an RTF file, so if a different extension is provided, we convert it to the RTF format.
pub license: Option<PathBuf>,
/// Create an elevated update task within Windows Task Scheduler.
#[serde(default, alias = "enable-elevated-update-task")]
pub enable_elevated_update_task: bool,
/// Path to a bitmap file to use as the installation user interface banner.
/// This bitmap will appear at the top of all but the first page of the installer.
///
/// The required dimensions are 493px × 58px.
#[serde(alias = "banner-path")]
pub banner_path: Option<PathBuf>,
/// Path to a bitmap file to use on the installation user interface dialogs.
/// It is used on the welcome and completion dialogs.
/// The required dimensions are 493px × 312px.
#[serde(alias = "dialog-image-path")]
pub dialog_image_path: Option<PathBuf>,
}
/// Configuration for the Installer bundle using NSIS.
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct NsisConfig {
/// The path to the license file to render on the installer.
pub license: Option<PathBuf>,
/// The path to a bitmap file to display on the header of installers pages.
///
/// The recommended dimensions are 150px x 57px.
#[serde(alias = "header-image")]
pub header_image: Option<PathBuf>,
/// The path to a bitmap file for the Welcome page and the Finish page.
///
/// The recommended dimensions are 164px x 314px.
#[serde(alias = "sidebar-image")]
pub sidebar_image: Option<PathBuf>,
/// The path to an icon file used as the installer icon.
#[serde(alias = "install-icon")]
pub installer_icon: Option<PathBuf>,
/// Whether the installation will be for all users or just the current user.
#[serde(default, alias = "install-mode")]
pub install_mode: NSISInstallerMode,
/// A list of installer languages.
/// By default the OS language is used. If the OS language is not in the list of languages, the first language will be used.
/// To allow the user to select the language, set `display_language_selector` to `true`.
///
/// See <https://github.com/kichik/nsis/tree/9465c08046f00ccb6eda985abbdbf52c275c6c4d/Contrib/Language%20files> for the complete list of languages.
pub languages: Option<Vec<String>>,
/// Whether to display a language selector dialog before the installer and uninstaller windows are rendered or not.
/// By default the OS language is selected, with a fallback to the first language in the `languages` array.
#[serde(default, alias = "display-language-selector")]
pub display_language_selector: bool,
}
/// Install Modes for the NSIS installer.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum NSISInstallerMode {
/// Default mode for the installer.
///
/// Install the app by default in a directory that doesn't require Administrator access.
///
/// Installer metadata will be saved under the `HKCU` registry path.
CurrentUser,
/// Install the app by default in the `Program Files` folder directory requires Administrator
/// access for the installation.
///
/// Installer metadata will be saved under the `HKLM` registry path.
PerMachine,
/// Combines both modes and allows the user to choose at install time
/// whether to install for the current user or per machine. Note that this mode
/// will require Administrator access even if the user wants to install it for the current user only.
///
/// Installer metadata will be saved under the `HKLM` or `HKCU` registry path based on the user's choice.
Both,
}
impl Default for NSISInstallerMode {
fn default() -> Self {
Self::CurrentUser
}
}
/// Install modes for the Webview2 runtime.
/// Note that for the updater bundle [`Self::DownloadBootstrapper`] is used.
///
/// For more information see <https://tauri.app/v1/guides/building/windows>.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum WebviewInstallMode {
/// Do not install the Webview2 as part of the Windows Installer.
Skip,
/// Download the bootstrapper and run it.
/// Requires an internet connection.
/// Results in a smaller installer size, but is not recommended on Windows 7.
DownloadBootstrapper {
/// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
#[serde(default = "default_webview_install_silent")]
silent: bool,
},
/// Embed the bootstrapper and run it.
/// Requires an internet connection.
/// Increases the installer size by around 1.8MB, but offers better support on Windows 7.
EmbedBootstrapper {
/// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
#[serde(default = "default_webview_install_silent")]
silent: bool,
},
/// Embed the offline installer and run it.
/// Does not require an internet connection.
/// Increases the installer size by around 127MB.
OfflineInstaller {
/// Instructs the installer to run the installer in silent mode. Defaults to `true`.
#[serde(default = "default_webview_install_silent")]
silent: bool,
},
/// Embed a fixed webview2 version and use it at runtime.
/// Increases the installer size by around 180MB.
FixedRuntime {
/// The path to the fixed runtime to use.
///
/// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
/// The `.cab` file must be extracted to a folder and this folder path must be defined on this field.
path: PathBuf,
},
}
fn default_webview_install_silent() -> bool {
true
}
impl Default for WebviewInstallMode {
fn default() -> Self {
Self::DownloadBootstrapper {
silent: default_webview_install_silent(),
}
}
}
/// Windows bundler configuration.
///
/// See more: https://tauri.app/v1/api/config#windowsconfig
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowsConfig {
/// Specifies the file digest algorithm to use for creating file signatures.
/// Required for code signing. SHA-256 is recommended.
#[serde(alias = "digest-algorithm")]
pub digest_algorithm: Option<String>,
/// Specifies the SHA1 hash of the signing certificate.
#[serde(alias = "certificate-thumbprint")]
pub certificate_thumbprint: Option<String>,
/// Server to use during timestamping.
#[serde(alias = "timestamp-url")]
pub timestamp_url: Option<String>,
/// Whether to use Time-Stamp Protocol (TSP, a.k.a. RFC 3161) for the timestamp server. Your code signing provider may
/// use a TSP timestamp server, like e.g. SSL.com does. If so, enable TSP by setting to true.
#[serde(default)]
pub tsp: bool,
/// The installation mode for the Webview2 runtime.
#[serde(default, alias = "webview-install-mode")]
pub webview_install_mode: WebviewInstallMode,
/// Path to the webview fixed runtime to use. Overwrites [`Self::webview_install_mode`] if set.
///
/// Will be removed in v2, prefer the [`Self::webview_install_mode`] option.
///
/// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
/// The `.cab` file must be extracted to a folder and this folder path must be defined on this field.
#[serde(alias = "webview-fixed-runtime-path")]
pub webview_fixed_runtime_path: Option<PathBuf>,
/// Validates a second app installation, blocking the user from installing an older version if set to `false`.
///
/// 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`.
///
/// The default value of this flag is `true`.
#[serde(default = "default_allow_downgrades", alias = "allow-downgrades")]
pub allow_downgrades: bool,
/// Configuration for the MSI generated with WiX.
pub wix: Option<WixConfig>,
/// Configuration for the installer generated with NSIS.
pub nsis: Option<NsisConfig>,
}
impl Default for WindowsConfig {
fn default() -> Self {
Self {
digest_algorithm: None,
certificate_thumbprint: None,
timestamp_url: None,
tsp: false,
webview_install_mode: Default::default(),
webview_fixed_runtime_path: None,
allow_downgrades: default_allow_downgrades(),
wix: None,
nsis: None,
}
}
}
fn default_allow_downgrades() -> bool {
true
}
/// Configuration for tauri-bundler.
///
/// See more: https://tauri.app/v1/api/config#bundleconfig
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BundleConfig {
/// Whether Tauri should bundle your application or just output the executable.
#[serde(default)]
pub active: bool,
/// The bundle targets, currently supports ["deb", "appimage", "nsis", "msi", "app", "dmg", "updater"] or "all".
#[serde(default)]
pub targets: BundleTarget,
/// The application identifier in reverse domain name notation (e.g. `com.tauri.example`).
/// This string must be unique across applications since it is used in system configurations like
/// the bundle ID and path to the webview data directory.
/// This string must contain only alphanumeric characters (AZ, az, and 09), hyphens (-),
/// and periods (.).
pub identifier: String,
/// The application's publisher. Defaults to the second element in the identifier string.
/// Currently maps to the Manufacturer property of the Windows Installer.
pub publisher: Option<String>,
/// The app's icons
#[serde(default)]
pub icon: Vec<String>,
/// App resources to bundle.
/// Each resource is a path to a file or directory.
/// Glob patterns are supported.
pub resources: Option<Vec<String>>,
/// A copyright string associated with your application.
pub copyright: Option<String>,
/// The application kind.
///
/// Should be one of the following:
/// 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.
pub category: Option<String>,
/// A short description of your application.
#[serde(alias = "short-description")]
pub short_description: Option<String>,
/// A longer, multi-line description of the application.
#[serde(alias = "long-description")]
pub long_description: Option<String>,
/// Configuration for the AppImage bundle.
#[serde(default)]
pub appimage: AppImageConfig,
/// Configuration for the Debian bundle.
#[serde(default)]
pub deb: DebConfig,
/// Configuration for the macOS bundles.
#[serde(rename = "macOS", default)]
pub macos: MacConfig,
/// A list of—either absolute or relative—paths to binaries to embed with your application.
///
/// Note that Tauri will look for system-specific binaries following the pattern "binary-name{-target-triple}{.system-extension}".
///
/// E.g. for the external binary "my-binary", Tauri looks for:
///
/// - "my-binary-x86_64-pc-windows-msvc.exe" for Windows
/// - "my-binary-x86_64-apple-darwin" for macOS
/// - "my-binary-x86_64-unknown-linux-gnu" for Linux
///
/// so don't forget to provide binaries for all targeted platforms.
#[serde(alias = "external-bin")]
pub external_bin: Option<Vec<String>>,
/// Configuration for the Windows bundle.
#[serde(default)]
pub windows: WindowsConfig,
}
/// A CLI argument definition.
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CliArg {
/// The short version of the argument, without the preceding -.
///
/// NOTE: Any leading `-` characters will be stripped, and only the first non-character will be used as the short version.
pub short: Option<char>,
/// The unique argument name
pub name: String,
/// The argument description which will be shown on the help information.
/// Typically, this is a short (one line) description of the arg.
pub description: Option<String>,
/// The argument long description which will be shown on the help information.
/// Typically this a more detailed (multi-line) message that describes the argument.
#[serde(alias = "long-description")]
pub long_description: Option<String>,
/// Specifies that the argument takes a value at run time.
///
/// NOTE: values for arguments may be specified in any of the following methods
/// - Using a space such as -o value or --option value
/// - Using an equals and no space such as -o=value or --option=value
/// - Use a short and no space such as -ovalue
#[serde(default, alias = "takes-value")]
pub takes_value: bool,
/// Specifies that the argument may have an unknown number of multiple values. Without any other settings, this argument may appear only once.
///
/// For example, --opt val1 val2 is allowed, but --opt val1 val2 --opt val3 is not.
///
/// NOTE: Setting this requires `takes_value` to be set to true.
#[serde(default)]
pub multiple: bool,
/// Specifies that the argument may appear more than once.
/// 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.
/// 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)
///
/// For example, --opt val1 --opt val2 is allowed, but --opt val1 val2 is not.
#[serde(default, alias = "multiple-occurrences")]
pub multiple_occurrences: bool,
/// Specifies how many values are required to satisfy this argument. For example, if you had a
/// `-f <file>` argument where you wanted exactly 3 'files' you would set
/// `number_of_values = 3`, and this argument wouldn't be satisfied unless the user provided
/// 3 and only 3 values.
///
/// **NOTE:** Does *not* require `multiple_occurrences = true` to be set. Setting
/// `multiple_occurrences = true` would allow `-f <file> <file> <file> -f <file> <file> <file>` where
/// as *not* setting it would only allow one occurrence of this argument.
///
/// **NOTE:** implicitly sets `takes_value = true` and `multiple_values = true`.
#[serde(alias = "number-of-values")]
pub number_of_values: Option<usize>,
/// Specifies a list of possible values for this argument.
/// At runtime, the CLI verifies that only one of the specified values was used, or fails with an error message.
#[serde(alias = "possible-values")]
pub possible_values: Option<Vec<String>>,
/// Specifies the minimum number of values for this argument.
/// For example, if you had a -f `<file>` argument where you wanted at least 2 'files',
/// you would set `minValues: 2`, and this argument would be satisfied if the user provided, 2 or more values.
#[serde(alias = "min-values")]
pub min_values: Option<usize>,
/// Specifies the maximum number of values are for this argument.
/// For example, if you had a -f `<file>` argument where you wanted up to 3 'files',
/// you would set .max_values(3), and this argument would be satisfied if the user provided, 1, 2, or 3 values.
#[serde(alias = "max-values")]
pub max_values: Option<usize>,
/// Sets whether or not the argument is required by default.
///
/// - Required by default means it is required, when no other conflicting rules have been evaluated
/// - Conflicting rules take precedence over being required.
#[serde(default)]
pub required: bool,
/// Sets an arg that override this arg's required setting
/// i.e. this arg will be required unless this other argument is present.
#[serde(alias = "required-unless-present")]
pub required_unless_present: Option<String>,
/// Sets args that override this arg's required setting
/// i.e. this arg will be required unless all these other arguments are present.
#[serde(alias = "required-unless-present-all")]
pub required_unless_present_all: Option<Vec<String>>,
/// Sets args that override this arg's required setting
/// i.e. this arg will be required unless at least one of these other arguments are present.
#[serde(alias = "required-unless-present-any")]
pub required_unless_present_any: Option<Vec<String>>,
/// Sets a conflicting argument by name
/// i.e. when using this argument, the following argument can't be present and vice versa.
#[serde(alias = "conflicts-with")]
pub conflicts_with: Option<String>,
/// The same as conflictsWith but allows specifying multiple two-way conflicts per argument.
#[serde(alias = "conflicts-with-all")]
pub conflicts_with_all: Option<Vec<String>>,
/// Tets an argument by name that is required when this one is present
/// i.e. when using this argument, the following argument must be present.
pub requires: Option<String>,
/// Sts multiple arguments by names that are required when this one is present
/// i.e. when using this argument, the following arguments must be present.
#[serde(alias = "requires-all")]
pub requires_all: Option<Vec<String>>,
/// Allows a conditional requirement with the signature [arg, value]
/// the requirement will only become valid if `arg`'s value equals `${value}`.
#[serde(alias = "requires-if")]
pub requires_if: Option<Vec<String>>,
/// Allows specifying that an argument is required conditionally with the signature [arg, value]
/// the requirement will only become valid if the `arg`'s value equals `${value}`.
#[serde(alias = "requires-if-eq")]
pub required_if_eq: Option<Vec<String>>,
/// Requires that options use the --option=val syntax
/// i.e. an equals between the option and associated value.
#[serde(alias = "requires-equals")]
pub require_equals: Option<bool>,
/// The positional argument index, starting at 1.
///
/// The index refers to position according to other positional argument.
/// It does not define position in the argument list as a whole. When utilized with multiple=true,
/// only the last positional argument may be defined as multiple (i.e. the one with the highest index).
#[cfg_attr(feature = "schema", validate(range(min = 1)))]
pub index: Option<usize>,
}
/// describes a CLI configuration
///
/// See more: https://tauri.app/v1/api/config#cliconfig
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CliConfig {
/// Command description which will be shown on the help information.
pub description: Option<String>,
/// Command long description which will be shown on the help information.
#[serde(alias = "long-description")]
pub long_description: Option<String>,
/// Adds additional help information to be displayed in addition to auto-generated help.
/// This information is displayed before the auto-generated help information.
/// This is often used for header information.
#[serde(alias = "before-help")]
pub before_help: Option<String>,
/// Adds additional help information to be displayed in addition to auto-generated help.
/// This information is displayed after the auto-generated help information.
/// This is often used to describe how to use the arguments, or caveats to be noted.
#[serde(alias = "after-help")]
pub after_help: Option<String>,
/// List of arguments for the command
pub args: Option<Vec<CliArg>>,
/// List of subcommands of this command
pub subcommands: Option<HashMap<String, CliConfig>>,
}
impl CliConfig {
/// List of arguments for the command
pub fn args(&self) -> Option<&Vec<CliArg>> {
self.args.as_ref()
}
/// List of subcommands of this command
pub fn subcommands(&self) -> Option<&HashMap<String, CliConfig>> {
self.subcommands.as_ref()
}
/// Command description which will be shown on the help information.
pub fn description(&self) -> Option<&String> {
self.description.as_ref()
}
/// Command long description which will be shown on the help information.
pub fn long_description(&self) -> Option<&String> {
self.description.as_ref()
}
/// Adds additional help information to be displayed in addition to auto-generated help.
/// This information is displayed before the auto-generated help information.
/// This is often used for header information.
pub fn before_help(&self) -> Option<&String> {
self.before_help.as_ref()
}
/// Adds additional help information to be displayed in addition to auto-generated help.
/// This information is displayed after the auto-generated help information.
/// This is often used to describe how to use the arguments, or caveats to be noted.
pub fn after_help(&self) -> Option<&String> {
self.after_help.as_ref()
}
}
/// The window configuration object.
///
/// See more: https://tauri.app/v1/api/config#windowconfig
#[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowConfig {
/// The window identifier. It must be alphanumeric.
#[serde(default = "default_window_label")]
pub label: String,
/// The window webview URL.
#[serde(default)]
pub url: WindowUrl,
/// The user agent for the webview
#[serde(alias = "user-agent")]
pub user_agent: Option<String>,
/// Whether the file drop is enabled or not on the webview. By default it is enabled.
///
/// Disabling it is required to use drag and drop on the frontend on Windows.
#[serde(default = "default_file_drop_enabled", alias = "file-drop-enabled")]
pub file_drop_enabled: bool,
/// Whether or not the window starts centered or not.
#[serde(default)]
pub center: bool,
/// The horizontal position of the window's top left corner
pub x: Option<f64>,
/// The vertical position of the window's top left corner
pub y: Option<f64>,
/// The window width.
#[serde(default = "default_width")]
pub width: f64,
/// The window height.
#[serde(default = "default_height")]
pub height: f64,
/// The min window width.
#[serde(alias = "min-width")]
pub min_width: Option<f64>,
/// The min window height.
#[serde(alias = "min-height")]
pub min_height: Option<f64>,
/// The max window width.
#[serde(alias = "max-width")]
pub max_width: Option<f64>,
/// The max window height.
#[serde(alias = "max-height")]
pub max_height: Option<f64>,
/// Whether the window is resizable or not.
#[serde(default = "default_resizable")]
pub resizable: bool,
/// The window title.
#[serde(default = "default_title")]
pub title: String,
/// Whether the window starts as fullscreen or not.
#[serde(default)]
pub fullscreen: bool,
/// Whether the window will be initially focused or not.
#[serde(default = "default_focus")]
pub focus: bool,
/// Whether the window is transparent or not.
///
/// Note that on `macOS` this requires the `macos-private-api` feature flag, enabled under `tauri > macOSPrivateApi`.
/// WARNING: Using private APIs on `macOS` prevents your application from being accepted to the `App Store`.
#[serde(default)]
pub transparent: bool,
/// Whether the window is maximized or not.
#[serde(default)]
pub maximized: bool,
/// Whether the window is visible or not.
#[serde(default = "default_visible")]
pub visible: bool,
/// Whether the window should have borders and bars.
#[serde(default = "default_decorations")]
pub decorations: bool,
/// Whether the window should always be on top of other windows.
#[serde(default, alias = "always-on-top")]
pub always_on_top: bool,
/// Prevents the window contents from being captured by other apps.
#[serde(default, alias = "content-protected")]
pub content_protected: bool,
/// If `true`, hides the window icon from the taskbar on Windows and Linux.
#[serde(default, alias = "skip-taskbar")]
pub skip_taskbar: bool,
/// The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.
pub theme: Option<crate::Theme>,
/// The style of the macOS title bar.
#[serde(default, alias = "title-bar-style")]
pub title_bar_style: TitleBarStyle,
/// If `true`, sets the window title to be hidden on macOS.
#[serde(default, alias = "hidden-title")]
pub hidden_title: bool,
/// Whether clicking an inactive window also clicks through to the webview on macOS.
#[serde(default, alias = "accept-first-mouse")]
pub accept_first_mouse: bool,
/// Defines the window [tabbing identifier] for macOS.
///
/// Windows with matching tabbing identifiers will be grouped together.
/// If the tabbing identifier is not set, automatic tabbing will be disabled.
///
/// [tabbing identifier]: <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
#[serde(default, alias = "tabbing-identifier")]
pub tabbing_identifier: Option<String>,
/// Defines additional browser arguments on Windows. By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`
/// so if you use this method, you also need to disable these components by yourself if you want.
#[serde(default, alias = "additional-browser-args")]
pub additional_browser_args: Option<String>,
}
impl Default for WindowConfig {
fn default() -> Self {
Self {
label: default_window_label(),
url: WindowUrl::default(),
user_agent: None,
file_drop_enabled: default_file_drop_enabled(),
center: false,
x: None,
y: None,
width: default_width(),
height: default_height(),
min_width: None,
min_height: None,
max_width: None,
max_height: None,
resizable: default_resizable(),
title: default_title(),
fullscreen: false,
focus: false,
transparent: false,
maximized: false,
visible: default_visible(),
decorations: default_decorations(),
always_on_top: false,
content_protected: false,
skip_taskbar: false,
theme: None,
title_bar_style: Default::default(),
hidden_title: false,
accept_first_mouse: false,
tabbing_identifier: None,
additional_browser_args: None,
}
}
}
fn default_window_label() -> String {
"main".to_string()
}
fn default_width() -> f64 {
800f64
}
fn default_height() -> f64 {
600f64
}
fn default_resizable() -> bool {
true
}
fn default_title() -> String {
"Tauri App".to_string()
}
fn default_focus() -> bool {
true
}
fn default_visible() -> bool {
true
}
fn default_decorations() -> bool {
true
}
fn default_file_drop_enabled() -> bool {
true
}
/// A Content-Security-Policy directive source list.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources>.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum CspDirectiveSources {
/// An inline list of CSP sources. Same as [`Self::List`], but concatenated with a space separator.
Inline(String),
/// A list of CSP sources. The collection will be concatenated with a space separator for the CSP string.
List(Vec<String>),
}
impl Default for CspDirectiveSources {
fn default() -> Self {
Self::List(Vec::new())
}
}
impl From<CspDirectiveSources> for Vec<String> {
fn from(sources: CspDirectiveSources) -> Self {
match sources {
CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
CspDirectiveSources::List(l) => l,
}
}
}
impl CspDirectiveSources {
/// Whether the given source is configured on this directive or not.
pub fn contains(&self, source: &str) -> bool {
match self {
Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
Self::List(l) => l.contains(&source.into()),
}
}
/// Appends the given source to this directive.
pub fn push<S: AsRef<str>>(&mut self, source: S) {
match self {
Self::Inline(s) => {
s.push(' ');
s.push_str(source.as_ref());
}
Self::List(l) => {
l.push(source.as_ref().to_string());
}
}
}
/// Extends this CSP directive source list with the given array of sources.
pub fn extend(&mut self, sources: Vec<String>) {
for s in sources {
self.push(s);
}
}
}
/// A Content-Security-Policy definition.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum Csp {
/// The entire CSP policy in a single text string.
Policy(String),
/// An object mapping a directive with its sources values as a list of strings.
DirectiveMap(HashMap<String, CspDirectiveSources>),
}
impl From<HashMap<String, CspDirectiveSources>> for Csp {
fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
Self::DirectiveMap(map)
}
}
impl From<Csp> for HashMap<String, CspDirectiveSources> {
fn from(csp: Csp) -> Self {
match csp {
Csp::Policy(policy) => {
let mut map = HashMap::new();
for directive in policy.split(';') {
let mut tokens = directive.trim().split(' ');
if let Some(directive) = tokens.next() {
let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
map.insert(directive.to_string(), CspDirectiveSources::List(sources));
}
}
map
}
Csp::DirectiveMap(m) => m,
}
}
}
impl Display for Csp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Policy(s) => write!(f, "{s}"),
Self::DirectiveMap(m) => {
let len = m.len();
let mut i = 0;
for (directive, sources) in m {
let sources: Vec<String> = sources.clone().into();
write!(f, "{} {}", directive, sources.join(" "))?;
i += 1;
if i != len {
write!(f, "; ")?;
}
}
Ok(())
}
}
}
}
/// The possible values for the `dangerous_disable_asset_csp_modification` config option.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum DisabledCspModificationKind {
/// If `true`, disables all CSP modification.
/// `false` is the default value and it configures Tauri to control the CSP.
Flag(bool),
/// Disables the given list of CSP directives modifications.
List(Vec<String>),
}
impl DisabledCspModificationKind {
/// Determines whether the given CSP directive can be modified or not.
pub fn can_modify(&self, directive: &str) -> bool {
match self {
Self::Flag(f) => !f,
Self::List(l) => !l.contains(&directive.into()),
}
}
}
impl Default for DisabledCspModificationKind {
fn default() -> Self {
Self::Flag(false)
}
}
/// Security configuration.
///
/// See more: https://tauri.app/v1/api/config#securityconfig
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SecurityConfig {
/// The Content Security Policy that will be injected on all HTML files on the built application.
/// If [`dev_csp`](#SecurityConfig.devCsp) is not specified, this value is also injected on dev.
///
/// This is a really important part of the configuration since it helps you ensure your WebView is secured.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
pub csp: Option<Csp>,
/// The Content Security Policy that will be injected on all HTML files on development.
///
/// This is a really important part of the configuration since it helps you ensure your WebView is secured.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
#[serde(alias = "dev-csp")]
pub dev_csp: Option<Csp>,
/// Freeze the `Object.prototype` when using the custom protocol.
#[serde(default, alias = "freeze-prototype")]
pub freeze_prototype: bool,
/// Disables the Tauri-injected CSP sources.
///
/// At compile time, Tauri parses all the frontend assets and changes the Content-Security-Policy
/// to only allow loading of your own scripts and styles by injecting nonce and hash sources.
/// This stricts your CSP, which may introduce issues when using along with other flexing sources.
///
/// This configuration option allows both a boolean and a list of strings as value.
/// A boolean instructs Tauri to disable the injection for all CSP injections,
/// and a list of strings indicates the CSP directives that Tauri cannot inject.
///
/// **WARNING:** Only disable this if you know what you are doing and have properly configured the CSP.
/// Your application might be vulnerable to XSS attacks without this Tauri protection.
#[serde(default, alias = "dangerous-disable-asset-csp-modification")]
pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
}
/// Defines an allowlist type.
pub trait Allowlist {
/// Returns all features associated with the allowlist struct.
fn all_features() -> Vec<&'static str>;
/// Returns the tauri features enabled on this allowlist.
fn to_features(&self) -> Vec<&'static str>;
}
macro_rules! check_feature {
($self:ident, $features:ident, $flag:ident, $feature_name: expr) => {
if $self.$flag {
$features.push($feature_name)
}
};
}
/// Filesystem scope definition.
/// It is a list of glob patterns that restrict the API access from the webview.
///
/// Each pattern can start with a variable that resolves to a system base directory.
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum FsAllowlistScope {
/// A list of paths that are allowed by this scope.
AllowedPaths(Vec<PathBuf>),
/// A complete scope configuration.
Scope {
/// A list of paths that are allowed by this scope.
#[serde(default)]
allow: Vec<PathBuf>,
/// A list of paths that are not allowed by this scope.
/// This gets precedence over the [`Self::Scope::allow`] list.
#[serde(default)]
deny: Vec<PathBuf>,
},
}
impl Default for FsAllowlistScope {
fn default() -> Self {
Self::AllowedPaths(Vec::new())
}
}
impl FsAllowlistScope {
/// The list of allowed paths.
pub fn allowed_paths(&self) -> &Vec<PathBuf> {
match self {
Self::AllowedPaths(p) => p,
Self::Scope { allow, .. } => allow,
}
}
/// The list of forbidden paths.
pub fn forbidden_paths(&self) -> Option<&Vec<PathBuf>> {
match self {
Self::AllowedPaths(_) => None,
Self::Scope { deny, .. } => Some(deny),
}
}
}
/// Allowlist for the file system APIs.
///
/// See more: https://tauri.app/v1/api/config#fsallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct FsAllowlistConfig {
/// The access scope for the filesystem APIs.
#[serde(default)]
pub scope: FsAllowlistScope,
/// Use this flag to enable all file system API features.
#[serde(default)]
pub all: bool,
/// Read file from local filesystem.
#[serde(default, alias = "read-file")]
pub read_file: bool,
/// Write file to local filesystem.
#[serde(default, alias = "write-file")]
pub write_file: bool,
/// Read directory from local filesystem.
#[serde(default, alias = "read-dir")]
pub read_dir: bool,
/// Copy file from local filesystem.
#[serde(default, alias = "copy-file")]
pub copy_file: bool,
/// Create directory from local filesystem.
#[serde(default, alias = "create-dir")]
pub create_dir: bool,
/// Remove directory from local filesystem.
#[serde(default, alias = "remove-dir")]
pub remove_dir: bool,
/// Remove file from local filesystem.
#[serde(default, alias = "remove-file")]
pub remove_file: bool,
/// Rename file from local filesystem.
#[serde(default, alias = "rename-file")]
pub rename_file: bool,
/// Check if path exists on the local filesystem.
#[serde(default)]
pub exists: bool,
}
impl Allowlist for FsAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
scope: Default::default(),
all: false,
read_file: true,
write_file: true,
read_dir: true,
copy_file: true,
create_dir: true,
remove_dir: true,
remove_file: true,
rename_file: true,
exists: true,
};
let mut features = allowlist.to_features();
features.push("fs-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["fs-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, read_file, "fs-read-file");
check_feature!(self, features, write_file, "fs-write-file");
check_feature!(self, features, read_dir, "fs-read-dir");
check_feature!(self, features, copy_file, "fs-copy-file");
check_feature!(self, features, create_dir, "fs-create-dir");
check_feature!(self, features, remove_dir, "fs-remove-dir");
check_feature!(self, features, remove_file, "fs-remove-file");
check_feature!(self, features, rename_file, "fs-rename-file");
check_feature!(self, features, exists, "fs-exists");
features
}
}
}
/// Allowlist for the window APIs.
///
/// See more: https://tauri.app/v1/api/config#windowallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowAllowlistConfig {
/// Use this flag to enable all window API features.
#[serde(default)]
pub all: bool,
/// Allows dynamic window creation.
#[serde(default)]
pub create: bool,
/// Allows centering the window.
#[serde(default)]
pub center: bool,
/// Allows requesting user attention on the window.
#[serde(default, alias = "request-user-attention")]
pub request_user_attention: bool,
/// Allows setting the resizable flag of the window.
#[serde(default, alias = "set-resizable")]
pub set_resizable: bool,
/// Allows changing the window title.
#[serde(default, alias = "set-title")]
pub set_title: bool,
/// Allows maximizing the window.
#[serde(default)]
pub maximize: bool,
/// Allows unmaximizing the window.
#[serde(default)]
pub unmaximize: bool,
/// Allows minimizing the window.
#[serde(default)]
pub minimize: bool,
/// Allows unminimizing the window.
#[serde(default)]
pub unminimize: bool,
/// Allows showing the window.
#[serde(default)]
pub show: bool,
/// Allows hiding the window.
#[serde(default)]
pub hide: bool,
/// Allows closing the window.
#[serde(default)]
pub close: bool,
/// Allows setting the decorations flag of the window.
#[serde(default, alias = "set-decorations")]
pub set_decorations: bool,
/// Allows setting the always_on_top flag of the window.
#[serde(default, alias = "set-always-on-top")]
pub set_always_on_top: bool,
/// Allows preventing the window contents from being captured by other apps.
#[serde(default, alias = "set-content-protected")]
pub set_content_protected: bool,
/// Allows setting the window size.
#[serde(default, alias = "set-size")]
pub set_size: bool,
/// Allows setting the window minimum size.
#[serde(default, alias = "set-min-size")]
pub set_min_size: bool,
/// Allows setting the window maximum size.
#[serde(default, alias = "set-max-size")]
pub set_max_size: bool,
/// Allows changing the position of the window.
#[serde(default, alias = "set-position")]
pub set_position: bool,
/// Allows setting the fullscreen flag of the window.
#[serde(default, alias = "set-fullscreen")]
pub set_fullscreen: bool,
/// Allows focusing the window.
#[serde(default, alias = "set-focus")]
pub set_focus: bool,
/// Allows changing the window icon.
#[serde(default, alias = "set-icon")]
pub set_icon: bool,
/// Allows setting the skip_taskbar flag of the window.
#[serde(default, alias = "set-skip-taskbar")]
pub set_skip_taskbar: bool,
/// Allows grabbing the cursor.
#[serde(default, alias = "set-cursor-grab")]
pub set_cursor_grab: bool,
/// Allows setting the cursor visibility.
#[serde(default, alias = "set-cursor-visible")]
pub set_cursor_visible: bool,
/// Allows changing the cursor icon.
#[serde(default, alias = "set-cursor-icon")]
pub set_cursor_icon: bool,
/// Allows setting the cursor position.
#[serde(default, alias = "set-cursor-position")]
pub set_cursor_position: bool,
/// Allows ignoring cursor events.
#[serde(default, alias = "set-ignore-cursor-events")]
pub set_ignore_cursor_events: bool,
/// Allows start dragging on the window.
#[serde(default, alias = "start-dragging")]
pub start_dragging: bool,
/// Allows opening the system dialog to print the window content.
#[serde(default)]
pub print: bool,
}
impl Allowlist for WindowAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
create: true,
center: true,
request_user_attention: true,
set_resizable: true,
set_title: true,
maximize: true,
unmaximize: true,
minimize: true,
unminimize: true,
show: true,
hide: true,
close: true,
set_decorations: true,
set_always_on_top: true,
set_content_protected: false,
set_size: true,
set_min_size: true,
set_max_size: true,
set_position: true,
set_fullscreen: true,
set_focus: true,
set_icon: true,
set_skip_taskbar: true,
set_cursor_grab: true,
set_cursor_visible: true,
set_cursor_icon: true,
set_cursor_position: true,
set_ignore_cursor_events: true,
start_dragging: true,
print: true,
};
let mut features = allowlist.to_features();
features.push("window-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["window-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, create, "window-create");
check_feature!(self, features, center, "window-center");
check_feature!(
self,
features,
request_user_attention,
"window-request-user-attention"
);
check_feature!(self, features, set_resizable, "window-set-resizable");
check_feature!(self, features, set_title, "window-set-title");
check_feature!(self, features, maximize, "window-maximize");
check_feature!(self, features, unmaximize, "window-unmaximize");
check_feature!(self, features, minimize, "window-minimize");
check_feature!(self, features, unminimize, "window-unminimize");
check_feature!(self, features, show, "window-show");
check_feature!(self, features, hide, "window-hide");
check_feature!(self, features, close, "window-close");
check_feature!(self, features, set_decorations, "window-set-decorations");
check_feature!(
self,
features,
set_always_on_top,
"window-set-always-on-top"
);
check_feature!(
self,
features,
set_content_protected,
"window-set-content-protected"
);
check_feature!(self, features, set_size, "window-set-size");
check_feature!(self, features, set_min_size, "window-set-min-size");
check_feature!(self, features, set_max_size, "window-set-max-size");
check_feature!(self, features, set_position, "window-set-position");
check_feature!(self, features, set_fullscreen, "window-set-fullscreen");
check_feature!(self, features, set_focus, "window-set-focus");
check_feature!(self, features, set_icon, "window-set-icon");
check_feature!(self, features, set_skip_taskbar, "window-set-skip-taskbar");
check_feature!(self, features, set_cursor_grab, "window-set-cursor-grab");
check_feature!(
self,
features,
set_cursor_visible,
"window-set-cursor-visible"
);
check_feature!(self, features, set_cursor_icon, "window-set-cursor-icon");
check_feature!(
self,
features,
set_cursor_position,
"window-set-cursor-position"
);
check_feature!(
self,
features,
set_ignore_cursor_events,
"window-set-ignore-cursor-events"
);
check_feature!(self, features, start_dragging, "window-start-dragging");
check_feature!(self, features, print, "window-print");
features
}
}
}
/// A command allowed to be executed by the webview API.
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct ShellAllowedCommand {
/// The name for this allowed shell command configuration.
///
/// This name will be used inside of the webview API to call this command along with
/// any specified arguments.
pub name: String,
/// The command name.
/// It can start with a variable that resolves to a system base directory.
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
#[serde(rename = "cmd", default)] // use default just so the schema doesn't flag it as required
pub command: PathBuf,
/// The allowed arguments for the command execution.
#[serde(default)]
pub args: ShellAllowedArgs,
/// If this command is a sidecar command.
#[serde(default)]
pub sidecar: bool,
}
impl<'de> Deserialize<'de> for ShellAllowedCommand {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct InnerShellAllowedCommand {
name: String,
#[serde(rename = "cmd")]
command: Option<PathBuf>,
#[serde(default)]
args: ShellAllowedArgs,
#[serde(default)]
sidecar: bool,
}
let config = InnerShellAllowedCommand::deserialize(deserializer)?;
if !config.sidecar && config.command.is_none() {
return Err(DeError::custom(
"The shell scope `command` value is required.",
));
}
Ok(ShellAllowedCommand {
name: config.name,
command: config.command.unwrap_or_default(),
args: config.args,
sidecar: config.sidecar,
})
}
}
/// A set of command arguments allowed to be executed by the webview API.
///
/// A value of `true` will allow any arguments to be passed to the command. `false` will disable all
/// arguments. A list of [`ShellAllowedArg`] will set those arguments as the only valid arguments to
/// be passed to the attached command configuration.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellAllowedArgs {
/// Use a simple boolean to allow all or disable all arguments to this command configuration.
Flag(bool),
/// A specific set of [`ShellAllowedArg`] that are valid to call for the command configuration.
List(Vec<ShellAllowedArg>),
}
impl Default for ShellAllowedArgs {
fn default() -> Self {
Self::Flag(false)
}
}
/// A command argument allowed to be executed by the webview API.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellAllowedArg {
/// A non-configurable argument that is passed to the command in the order it was specified.
Fixed(String),
/// A variable that is set while calling the command from the webview API.
///
Var {
/// [regex] validator to require passed values to conform to an expected input.
///
/// This will require the argument value passed to this variable to match the `validator` regex
/// before it will be executed.
///
/// [regex]: https://docs.rs/regex/latest/regex/#syntax
validator: String,
},
}
/// Shell scope definition.
/// It is a list of command names and associated CLI arguments that restrict the API access from the webview.
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct ShellAllowlistScope(pub Vec<ShellAllowedCommand>);
/// Defines the `shell > open` api scope.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellAllowlistOpen {
/// If the shell open API should be enabled.
///
/// If enabled, the default validation regex (`^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`) is used.
Flag(bool),
/// Enable the shell open API, with a custom regex that the opened path must match against.
///
/// If using a custom regex to support a non-http(s) schema, care should be used to prevent values
/// that allow flag-like strings to pass validation. e.g. `--enable-debugging`, `-i`, `/R`.
Validate(String),
}
impl Default for ShellAllowlistOpen {
fn default() -> Self {
Self::Flag(false)
}
}
/// Allowlist for the shell APIs.
///
/// See more: https://tauri.app/v1/api/config#shellallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ShellAllowlistConfig {
/// Access scope for the binary execution APIs.
/// Sidecars are automatically enabled.
#[serde(default)]
pub scope: ShellAllowlistScope,
/// Use this flag to enable all shell API features.
#[serde(default)]
pub all: bool,
/// Enable binary execution.
#[serde(default)]
pub execute: bool,
/// Enable sidecar execution, allowing the JavaScript layer to spawn a sidecar command,
/// an executable that is shipped with the application.
/// For more information see <https://tauri.app/v1/guides/building/sidecar>.
#[serde(default)]
pub sidecar: bool,
/// Open URL with the user's default application.
#[serde(default)]
pub open: ShellAllowlistOpen,
}
impl Allowlist for ShellAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
scope: Default::default(),
all: false,
execute: true,
sidecar: true,
open: ShellAllowlistOpen::Flag(true),
};
let mut features = allowlist.to_features();
features.push("shell-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["shell-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, execute, "shell-execute");
check_feature!(self, features, sidecar, "shell-sidecar");
if !matches!(self.open, ShellAllowlistOpen::Flag(false)) {
features.push("shell-open")
}
features
}
}
}
/// Allowlist for the dialog APIs.
///
/// See more: https://tauri.app/v1/api/config#dialogallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DialogAllowlistConfig {
/// Use this flag to enable all dialog API features.
#[serde(default)]
pub all: bool,
/// Allows the API to open a dialog window to pick files.
#[serde(default)]
pub open: bool,
/// Allows the API to open a dialog window to pick where to save files.
#[serde(default)]
pub save: bool,
/// Allows the API to show a message dialog window.
#[serde(default)]
pub message: bool,
/// Allows the API to show a dialog window with Yes/No buttons.
#[serde(default)]
pub ask: bool,
/// Allows the API to show a dialog window with Ok/Cancel buttons.
#[serde(default)]
pub confirm: bool,
}
impl Allowlist for DialogAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
open: true,
save: true,
message: true,
ask: true,
confirm: true,
};
let mut features = allowlist.to_features();
features.push("dialog-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["dialog-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, open, "dialog-open");
check_feature!(self, features, save, "dialog-save");
check_feature!(self, features, message, "dialog-message");
check_feature!(self, features, ask, "dialog-ask");
check_feature!(self, features, confirm, "dialog-confirm");
features
}
}
}
/// HTTP API scope definition.
/// It is a list of URLs that can be accessed by the webview when using the HTTP APIs.
/// The scoped URL is matched against the request URL using a glob pattern.
///
/// Examples:
/// - "https://**": allows all HTTPS urls
/// - "https://*.github.com/tauri-apps/tauri": allows any subdomain of "github.com" with the "tauri-apps/api" path
/// - "https://myapi.service.com/users/*": allows access to any URLs that begins with "https://myapi.service.com/users/"
#[allow(rustdoc::bare_urls)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct HttpAllowlistScope(pub Vec<Url>);
/// Allowlist for the HTTP APIs.
///
/// See more: https://tauri.app/v1/api/config#httpallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct HttpAllowlistConfig {
/// The access scope for the HTTP APIs.
#[serde(default)]
pub scope: HttpAllowlistScope,
/// Use this flag to enable all HTTP API features.
#[serde(default)]
pub all: bool,
/// Allows making HTTP requests.
#[serde(default)]
pub request: bool,
}
impl Allowlist for HttpAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
scope: Default::default(),
all: false,
request: true,
};
let mut features = allowlist.to_features();
features.push("http-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["http-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, request, "http-request");
features
}
}
}
/// Allowlist for the notification APIs.
///
/// See more: https://tauri.app/v1/api/config#notificationallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct NotificationAllowlistConfig {
/// Use this flag to enable all notification API features.
#[serde(default)]
pub all: bool,
}
impl Allowlist for NotificationAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("notification-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["notification-all"]
} else {
vec![]
}
}
}
/// Allowlist for the global shortcut APIs.
///
/// See more: https://tauri.app/v1/api/config#globalshortcutallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GlobalShortcutAllowlistConfig {
/// Use this flag to enable all global shortcut API features.
#[serde(default)]
pub all: bool,
}
impl Allowlist for GlobalShortcutAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("global-shortcut-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["global-shortcut-all"]
} else {
vec![]
}
}
}
/// Allowlist for the OS APIs.
///
/// See more: https://tauri.app/v1/api/config#osallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct OsAllowlistConfig {
/// Use this flag to enable all OS API features.
#[serde(default)]
pub all: bool,
}
impl Allowlist for OsAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("os-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["os-all"]
} else {
vec![]
}
}
}
/// Allowlist for the path APIs.
///
/// See more: https://tauri.app/v1/api/config#pathallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PathAllowlistConfig {
/// Use this flag to enable all path API features.
#[serde(default)]
pub all: bool,
}
impl Allowlist for PathAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("path-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["path-all"]
} else {
vec![]
}
}
}
/// Allowlist for the custom protocols.
///
/// See more: https://tauri.app/v1/api/config#protocolallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProtocolAllowlistConfig {
/// The access scope for the asset protocol.
#[serde(default, alias = "asset-scope")]
pub asset_scope: FsAllowlistScope,
/// Use this flag to enable all custom protocols.
#[serde(default)]
pub all: bool,
/// Enables the asset protocol.
#[serde(default)]
pub asset: bool,
}
impl Allowlist for ProtocolAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
asset_scope: Default::default(),
all: false,
asset: true,
};
let mut features = allowlist.to_features();
features.push("protocol-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["protocol-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, asset, "protocol-asset");
features
}
}
}
/// Allowlist for the process APIs.
///
/// See more: https://tauri.app/v1/api/config#processallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProcessAllowlistConfig {
/// Use this flag to enable all process APIs.
#[serde(default)]
pub all: bool,
/// Enables the relaunch API.
#[serde(default)]
pub relaunch: bool,
/// Dangerous option that allows macOS to relaunch even if the binary contains a symlink.
///
/// This is due to macOS having less symlink protection. Highly recommended to not set this flag
/// unless you have a very specific reason too, and understand the implications of it.
#[serde(
default,
alias = "relaunchDangerousAllowSymlinkMacOS",
alias = "relaunch-dangerous-allow-symlink-macos"
)]
pub relaunch_dangerous_allow_symlink_macos: bool,
/// Enables the exit API.
#[serde(default)]
pub exit: bool,
}
impl Allowlist for ProcessAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
relaunch: true,
relaunch_dangerous_allow_symlink_macos: false,
exit: true,
};
let mut features = allowlist.to_features();
features.push("process-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["process-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, relaunch, "process-relaunch");
check_feature!(
self,
features,
relaunch_dangerous_allow_symlink_macos,
"process-relaunch-dangerous-allow-symlink-macos"
);
check_feature!(self, features, exit, "process-exit");
features
}
}
}
/// Allowlist for the clipboard APIs.
///
/// See more: https://tauri.app/v1/api/config#clipboardallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ClipboardAllowlistConfig {
/// Use this flag to enable all clipboard APIs.
#[serde(default)]
pub all: bool,
/// Enables the clipboard's `writeText` API.
#[serde(default, alias = "writeText")]
pub write_text: bool,
/// Enables the clipboard's `readText` API.
#[serde(default, alias = "readText")]
pub read_text: bool,
}
impl Allowlist for ClipboardAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
write_text: true,
read_text: true,
};
let mut features = allowlist.to_features();
features.push("clipboard-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["clipboard-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, write_text, "clipboard-write-text");
check_feature!(self, features, read_text, "clipboard-read-text");
features
}
}
}
/// Allowlist for the app APIs.
///
/// See more: https://tauri.app/v1/api/config#appallowlistconfig
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AppAllowlistConfig {
/// Use this flag to enable all app APIs.
#[serde(default)]
pub all: bool,
/// Enables the app's `show` API.
#[serde(default)]
pub show: bool,
/// Enables the app's `hide` API.
#[serde(default)]
pub hide: bool,
}
impl Allowlist for AppAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
show: true,
hide: true,
};
let mut features = allowlist.to_features();
features.push("app-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["app-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, show, "app-show");
check_feature!(self, features, hide, "app-hide");
features
}
}
}
/// Allowlist configuration. The allowlist is a translation of the [Cargo allowlist features](https://docs.rs/tauri/latest/tauri/#cargo-allowlist-features).
///
/// # Notes
///
/// - Endpoints that don't have their own allowlist option are enabled by default.
/// - There is only "opt-in", no "opt-out". Setting an option to `false` has no effect.
///
/// # Examples
///
/// - * [`"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.
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AllowlistConfig {
/// Use this flag to enable all API features.
#[serde(default)]
pub all: bool,
/// File system API allowlist.
#[serde(default)]
pub fs: FsAllowlistConfig,
/// Window API allowlist.
#[serde(default)]
pub window: WindowAllowlistConfig,
/// Shell API allowlist.
#[serde(default)]
pub shell: ShellAllowlistConfig,
/// Dialog API allowlist.
#[serde(default)]
pub dialog: DialogAllowlistConfig,
/// HTTP API allowlist.
#[serde(default)]
pub http: HttpAllowlistConfig,
/// Notification API allowlist.
#[serde(default)]
pub notification: NotificationAllowlistConfig,
/// Global shortcut API allowlist.
#[serde(default, alias = "global-shortcut")]
pub global_shortcut: GlobalShortcutAllowlistConfig,
/// OS allowlist.
#[serde(default)]
pub os: OsAllowlistConfig,
/// Path API allowlist.
#[serde(default)]
pub path: PathAllowlistConfig,
/// Custom protocol allowlist.
#[serde(default)]
pub protocol: ProtocolAllowlistConfig,
/// Process API allowlist.
#[serde(default)]
pub process: ProcessAllowlistConfig,
/// Clipboard APIs allowlist.
#[serde(default)]
pub clipboard: ClipboardAllowlistConfig,
/// App APIs allowlist.
#[serde(default)]
pub app: AppAllowlistConfig,
}
impl Allowlist for AllowlistConfig {
fn all_features() -> Vec<&'static str> {
let mut features = vec!["api-all"];
features.extend(FsAllowlistConfig::all_features());
features.extend(WindowAllowlistConfig::all_features());
features.extend(ShellAllowlistConfig::all_features());
features.extend(DialogAllowlistConfig::all_features());
features.extend(HttpAllowlistConfig::all_features());
features.extend(NotificationAllowlistConfig::all_features());
features.extend(GlobalShortcutAllowlistConfig::all_features());
features.extend(OsAllowlistConfig::all_features());
features.extend(PathAllowlistConfig::all_features());
features.extend(ProtocolAllowlistConfig::all_features());
features.extend(ProcessAllowlistConfig::all_features());
features.extend(ClipboardAllowlistConfig::all_features());
features.extend(AppAllowlistConfig::all_features());
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["api-all"]
} else {
let mut features = Vec::new();
features.extend(self.fs.to_features());
features.extend(self.window.to_features());
features.extend(self.shell.to_features());
features.extend(self.dialog.to_features());
features.extend(self.http.to_features());
features.extend(self.notification.to_features());
features.extend(self.global_shortcut.to_features());
features.extend(self.os.to_features());
features.extend(self.path.to_features());
features.extend(self.protocol.to_features());
features.extend(self.process.to_features());
features.extend(self.clipboard.to_features());
features.extend(self.app.to_features());
features
}
}
}
/// The application pattern.
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase", tag = "use", content = "options")]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum PatternKind {
/// Brownfield pattern.
Brownfield,
/// Isolation pattern. Recommended for security purposes.
Isolation {
/// The dir containing the index.html file that contains the secure isolation application.
dir: PathBuf,
},
}
impl Default for PatternKind {
fn default() -> Self {
Self::Brownfield
}
}
/// The Tauri configuration object.
///
/// See more: https://tauri.app/v1/api/config#tauriconfig
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TauriConfig {
/// The pattern to use.
#[serde(default)]
pub pattern: PatternKind,
/// The windows configuration.
#[serde(default)]
pub windows: Vec<WindowConfig>,
/// The CLI configuration.
pub cli: Option<CliConfig>,
/// The bundler configuration.
#[serde(default)]
pub bundle: BundleConfig,
/// The allowlist configuration.
#[serde(default)]
pub allowlist: AllowlistConfig,
/// Security configuration.
#[serde(default)]
pub security: SecurityConfig,
/// The updater configuration.
#[serde(default)]
pub updater: UpdaterConfig,
/// Configuration for app system tray.
#[serde(alias = "system-tray")]
pub system_tray: Option<SystemTrayConfig>,
/// MacOS private API configuration. Enables the transparent background API and sets the `fullScreenEnabled` preference to `true`.
#[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
pub macos_private_api: bool,
}
impl TauriConfig {
/// Returns all Cargo features.
#[allow(dead_code)]
pub fn all_features() -> Vec<&'static str> {
let mut features = AllowlistConfig::all_features();
features.extend(vec![
"cli",
"updater",
"system-tray",
"macos-private-api",
"isolation",
]);
features
}
/// Returns the enabled Cargo features.
#[allow(dead_code)]
pub fn features(&self) -> Vec<&str> {
let mut features = self.allowlist.to_features();
if self.cli.is_some() {
features.push("cli");
}
if self.updater.active {
features.push("updater");
}
if self.system_tray.is_some() {
features.push("system-tray");
}
if self.macos_private_api {
features.push("macos-private-api");
}
if let PatternKind::Isolation { .. } = self.pattern {
features.push("isolation");
}
features.sort_unstable();
features
}
}
/// A URL to an updater server.
///
/// The URL must use the `https` scheme on production.
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct UpdaterEndpoint(pub Url);
impl std::fmt::Display for UpdaterEndpoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'de> Deserialize<'de> for UpdaterEndpoint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let url = Url::deserialize(deserializer)?;
#[cfg(all(not(debug_assertions), not(feature = "schema")))]
{
if url.scheme() != "https" {
return Err(serde::de::Error::custom(
"The configured updater endpoint must use the `https` protocol.",
));
}
}
Ok(Self(url))
}
}
/// Install modes for the Windows update.
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(rename_all = "camelCase"))]
pub enum WindowsUpdateInstallMode {
/// Specifies there's a basic UI during the installation process, including a final dialog box at the end.
BasicUi,
/// The quiet mode means there's no user interaction required.
/// Requires admin privileges if the installer does (WiX).
Quiet,
/// Specifies unattended mode, which means the installation only shows a progress bar.
Passive,
// to add more modes, we need to check if the updater relaunch makes sense
// i.e. for a full UI mode, the user can also mark the installer to start the app
}
impl WindowsUpdateInstallMode {
/// Returns the associated `msiexec.exe` arguments.
pub fn msiexec_args(&self) -> &'static [&'static str] {
match self {
Self::BasicUi => &["/qb+"],
Self::Quiet => &["/quiet"],
Self::Passive => &["/passive"],
}
}
}
impl Display for WindowsUpdateInstallMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::BasicUi => "basicUI",
Self::Quiet => "quiet",
Self::Passive => "passive",
}
)
}
}
impl Default for WindowsUpdateInstallMode {
fn default() -> Self {
Self::Passive
}
}
impl Serialize for WindowsUpdateInstallMode {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
impl<'de> Deserialize<'de> for WindowsUpdateInstallMode {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
"basicui" => Ok(Self::BasicUi),
"quiet" => Ok(Self::Quiet),
"passive" => Ok(Self::Passive),
_ => Err(DeError::custom(format!(
"unknown update install mode '{s}'"
))),
}
}
}
/// The updater configuration for Windows.
///
/// See more: https://tauri.app/v1/api/config#updaterwindowsconfig
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UpdaterWindowsConfig {
/// Additional arguments given to the NSIS or WiX installer.
#[serde(default, alias = "installer-args")]
pub installer_args: Vec<String>,
/// The installation mode for the update on Windows. Defaults to `passive`.
#[serde(default, alias = "install-mode")]
pub install_mode: WindowsUpdateInstallMode,
}
/// The Updater configuration object.
///
/// See more: https://tauri.app/v1/api/config#updaterconfig
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UpdaterConfig {
/// Whether the updater is active or not.
#[serde(default)]
pub active: bool,
/// Display built-in dialog or use event system if disabled.
#[serde(default = "default_dialog")]
pub dialog: bool,
/// The updater endpoints. TLS is enforced on production.
///
/// The updater URL can contain the following variables:
/// - {{current_version}}: The version of the app that is requesting the update
/// - {{target}}: The operating system name (one of `linux`, `windows` or `darwin`).
/// - {{arch}}: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`).
///
/// # Examples
/// - "https://my.cdn.com/latest.json": a raw JSON endpoint that returns the latest version and download links for each platform.
/// - "https://updates.app.dev/{{target}}?version={{current_version}}&arch={{arch}}": a dedicated API with positional and query string arguments.
#[allow(rustdoc::bare_urls)]
pub endpoints: Option<Vec<UpdaterEndpoint>>,
/// Signature public key.
#[serde(default)] // use default just so the schema doesn't flag it as required
pub pubkey: String,
/// The Windows configuration for the updater.
#[serde(default)]
pub windows: UpdaterWindowsConfig,
}
impl<'de> Deserialize<'de> for UpdaterConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct InnerUpdaterConfig {
#[serde(default)]
active: bool,
#[serde(default = "default_dialog")]
dialog: bool,
endpoints: Option<Vec<UpdaterEndpoint>>,
pubkey: Option<String>,
#[serde(default)]
windows: UpdaterWindowsConfig,
}
let config = InnerUpdaterConfig::deserialize(deserializer)?;
if config.active && config.pubkey.is_none() {
return Err(DeError::custom(
"The updater `pubkey` configuration is required.",
));
}
Ok(UpdaterConfig {
active: config.active,
dialog: config.dialog,
endpoints: config.endpoints,
pubkey: config.pubkey.unwrap_or_default(),
windows: config.windows,
})
}
}
impl Default for UpdaterConfig {
fn default() -> Self {
Self {
active: false,
dialog: default_dialog(),
endpoints: None,
pubkey: "".into(),
windows: Default::default(),
}
}
}
/// Configuration for application system tray icon.
///
/// See more: https://tauri.app/v1/api/config#systemtrayconfig
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SystemTrayConfig {
/// Path to the default icon to use on the system tray.
#[serde(alias = "icon-path")]
pub icon_path: PathBuf,
/// 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.
#[serde(default, alias = "icon-as-template")]
pub icon_as_template: bool,
/// A Boolean value that determines whether the menu should appear when the tray icon receives a left click on macOS.
#[serde(
default = "default_tray_menu_on_left_click",
alias = "menu-on-left-click"
)]
pub menu_on_left_click: bool,
/// Title for MacOS tray
pub title: Option<String>,
}
fn default_tray_menu_on_left_click() -> bool {
true
}
// We enable the unnecessary_wraps because we need
// to use an Option for dialog otherwise the CLI schema will mark
// the dialog as a required field which is not as we default it to true.
fn default_dialog() -> bool {
true
}
/// Defines the URL or assets to embed in the application.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum AppUrl {
/// The app's external URL, or the path to the directory containing the app assets.
Url(WindowUrl),
/// An array of files to embed on the app.
Files(Vec<PathBuf>),
}
impl std::fmt::Display for AppUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Url(url) => write!(f, "{url}"),
Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
}
}
}
/// Describes the shell command to run before `tauri dev`.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum BeforeDevCommand {
/// Run the given script with the default options.
Script(String),
/// Run the given script with custom options.
ScriptWithOptions {
/// The script to execute.
script: String,
/// The current working directory.
cwd: Option<String>,
/// Whether `tauri dev` should wait for the command to finish or not. Defaults to `false`.
#[serde(default)]
wait: bool,
},
}
/// Describes a shell command to be executed when a CLI hook is triggered.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum HookCommand {
/// Run the given script with the default options.
Script(String),
/// Run the given script with custom options.
ScriptWithOptions {
/// The script to execute.
script: String,
/// The current working directory.
cwd: Option<String>,
},
}
/// The Build configuration object.
///
/// See more: https://tauri.app/v1/api/config#buildconfig
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BuildConfig {
/// The binary used to build and run the application.
pub runner: Option<String>,
/// The path to the application assets or URL to load in development.
///
/// This is usually an URL to a dev server, which serves your application assets
/// with live reloading. Most modern JavaScript bundlers provides a way to start a dev server by default.
///
/// See [vite](https://vitejs.dev/guide/), [Webpack DevServer](https://webpack.js.org/configuration/dev-server/) and [sirv](https://github.com/lukeed/sirv)
/// for examples on how to set up a dev server.
#[serde(default = "default_dev_path", alias = "dev-path")]
pub dev_path: AppUrl,
/// The path to the application assets or URL to load in production.
///
/// When a path relative to the configuration file is provided,
/// it is read recursively and all files are embedded in the application binary.
/// Tauri then looks for an `index.html` file unless you provide a custom window URL.
///
/// You can also provide a list of paths to be embedded, which allows granular control over what files are added to the binary.
/// In this case, all files are added to the root and you must reference it that way in your HTML files.
///
/// When an URL is provided, the application won't have bundled assets
/// and the application will load that URL by default.
#[serde(default = "default_dist_dir", alias = "dist-dir")]
pub dist_dir: AppUrl,
/// A shell command to run before `tauri dev` kicks in.
///
/// 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.
#[serde(alias = "before-dev-command")]
pub before_dev_command: Option<BeforeDevCommand>,
/// A shell command to run before `tauri build` kicks in.
///
/// 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.
#[serde(alias = "before-build-command")]
pub before_build_command: Option<HookCommand>,
/// A shell command to run before the bundling phase in `tauri build` kicks in.
///
/// 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.
#[serde(alias = "before-bundle-command")]
pub before_bundle_command: Option<HookCommand>,
/// Features passed to `cargo` commands.
pub features: Option<Vec<String>>,
/// Whether we should inject the Tauri API on `window.__TAURI__` or not.
#[serde(default, alias = "with-global-tauri")]
pub with_global_tauri: bool,
}
impl Default for BuildConfig {
fn default() -> Self {
Self {
runner: None,
dev_path: default_dev_path(),
dist_dir: default_dist_dir(),
before_dev_command: None,
before_build_command: None,
before_bundle_command: None,
features: None,
with_global_tauri: false,
}
}
}
fn default_dev_path() -> AppUrl {
AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap(),
))
}
fn default_dist_dir() -> AppUrl {
AppUrl::Url(WindowUrl::App("../dist".into()))
}
#[derive(Debug, PartialEq, Eq)]
struct PackageVersion(String);
impl<'d> serde::Deserialize<'d> for PackageVersion {
fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<PackageVersion, D::Error> {
struct PackageVersionVisitor;
impl<'d> Visitor<'d> for PackageVersionVisitor {
type Value = PackageVersion;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"a semver string or a path to a package.json file"
)
}
fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
let path = PathBuf::from(value);
if path.exists() {
let json_str = read_to_string(&path)
.map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
let package_json: serde_json::Value = serde_json::from_str(&json_str)
.map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
if let Some(obj) = package_json.as_object() {
let version = obj
.get("version")
.ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
.as_str()
.ok_or_else(|| {
DeError::custom(format!("`{} > version` must be a string", path.display()))
})?;
Ok(PackageVersion(
Version::from_str(version)
.map_err(|_| DeError::custom("`package > version` must be a semver string"))?
.to_string(),
))
} else {
Err(DeError::custom(
"`package > version` value is not a path to a JSON object",
))
}
} else {
Ok(PackageVersion(
Version::from_str(value)
.map_err(|_| DeError::custom("`package > version` must be a semver string"))?
.to_string(),
))
}
}
}
deserializer.deserialize_string(PackageVersionVisitor {})
}
}
/// The package configuration.
///
/// See more: https://tauri.app/v1/api/config#packageconfig
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PackageConfig {
/// App name.
#[serde(alias = "product-name")]
#[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
pub product_name: Option<String>,
/// App version. It is a semver version number or a path to a `package.json` file containing the `version` field.
#[serde(deserialize_with = "version_deserializer", default)]
pub version: Option<String>,
}
fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
}
impl PackageConfig {
/// The binary name.
#[allow(dead_code)]
pub fn binary_name(&self) -> Option<String> {
#[cfg(target_os = "linux")]
{
self.product_name.as_ref().map(|n| n.to_kebab_case())
}
#[cfg(not(target_os = "linux"))]
{
self.product_name.clone()
}
}
}
/// The Tauri configuration object.
/// It is read from a file where you can define your frontend assets,
/// configure the bundler, enable the app updater, define a system tray,
/// enable APIs via the allowlist and more.
///
/// The configuration file is generated by the
/// [`tauri init`](https://tauri.app/v1/api/cli#init) command that lives in
/// your Tauri application source directory (src-tauri).
///
/// Once generated, you may modify it at will to customize your Tauri application.
///
/// ## File Formats
///
/// By default, the configuration is defined as a JSON file named `tauri.conf.json`.
///
/// Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.
/// The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.
/// The TOML file name is `Tauri.toml`.
///
/// ## Platform-Specific Configuration
///
/// In addition to the default configuration file, Tauri can
/// read a platform-specific configuration from `tauri.linux.conf.json`,
/// `tauri.windows.conf.json`, and `tauri.macos.conf.json`
/// (or `Tauri.linux.toml`, `Tauri.windows.toml` and `Tauri.macos.toml` if the `Tauri.toml` format is used),
/// which gets merged with the main configuration object.
///
/// ## Configuration Structure
///
/// The configuration is composed of the following objects:
///
/// - [`package`](#packageconfig): Package settings
/// - [`tauri`](#tauriconfig): The Tauri config
/// - [`build`](#buildconfig): The build configuration
/// - [`plugins`](#pluginconfig): The plugins config
///
/// ```json title="Example tauri.config.json file"
/// {
/// "build": {
/// "beforeBuildCommand": "",
/// "beforeDevCommand": "",
/// "devPath": "../dist",
/// "distDir": "../dist"
/// },
/// "package": {
/// "productName": "tauri-app",
/// "version": "0.1.0"
/// },
/// "tauri": {
/// "allowlist": {
/// "all": true
/// },
/// "bundle": {},
/// "security": {
/// "csp": null
/// },
/// "updater": {
/// "active": false
/// },
/// "windows": [
/// {
/// "fullscreen": false,
/// "height": 600,
/// "resizable": true,
/// "title": "Tauri App",
/// "width": 800
/// }
/// ]
/// }
/// }
/// ```
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Config {
/// The JSON schema for the Tauri config.
#[serde(rename = "$schema")]
pub schema: Option<String>,
/// Package settings.
#[serde(default)]
pub package: PackageConfig,
/// The Tauri configuration.
#[serde(default)]
pub tauri: TauriConfig,
/// The build configuration.
#[serde(default = "default_build")]
pub build: BuildConfig,
/// The plugins config.
#[serde(default)]
pub plugins: PluginConfig,
}
/// The plugin configs holds a HashMap mapping a plugin name to its configuration object.
///
/// See more: https://tauri.app/v1/api/config#pluginconfig
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct PluginConfig(pub HashMap<String, JsonValue>);
fn default_build() -> BuildConfig {
BuildConfig {
runner: None,
dev_path: default_dev_path(),
dist_dir: default_dist_dir(),
before_dev_command: None,
before_build_command: None,
before_bundle_command: None,
features: None,
with_global_tauri: false,
}
}
/// Implement `ToTokens` for all config structs, allowing a literal `Config` to be built.
///
/// This allows for a build script to output the values in a `Config` to a `TokenStream`, which can
/// then be consumed by another crate. Useful for passing a config to both the build script and the
/// application using tauri while only parsing it once (in the build script).
#[cfg(feature = "build")]
mod build {
use std::{convert::identity, path::Path};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens, TokenStreamExt};
use super::*;
use serde_json::Value as JsonValue;
/// Create a `String` constructor `TokenStream`.
///
/// e.g. `"Hello World" -> String::from("Hello World").
/// This takes a `&String` to reduce casting all the `&String` -> `&str` manually.
fn str_lit(s: impl AsRef<str>) -> TokenStream {
let s = s.as_ref();
quote! { #s.into() }
}
/// Create an `Option` constructor `TokenStream`.
fn opt_lit(item: Option<&impl ToTokens>) -> TokenStream {
match item {
None => quote! { ::core::option::Option::None },
Some(item) => quote! { ::core::option::Option::Some(#item) },
}
}
/// Helper function to combine an `opt_lit` with `str_lit`.
fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
opt_lit(item.map(str_lit).as_ref())
}
/// Helper function to combine an `opt_lit` with a list of `str_lit`
fn opt_vec_str_lit(item: Option<impl IntoIterator<Item = impl AsRef<str>>>) -> TokenStream {
opt_lit(item.map(|list| vec_lit(list, str_lit)).as_ref())
}
/// Create a `Vec` constructor, mapping items with a function that spits out `TokenStream`s.
fn vec_lit<Raw, Tokens>(
list: impl IntoIterator<Item = Raw>,
map: impl Fn(Raw) -> Tokens,
) -> TokenStream
where
Tokens: ToTokens,
{
let items = list.into_iter().map(map);
quote! { vec![#(#items),*] }
}
/// Create a `PathBuf` constructor `TokenStream`.
///
/// e.g. `"Hello World" -> String::from("Hello World").
fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
let s = s.as_ref().to_string_lossy().into_owned();
quote! { ::std::path::PathBuf::from(#s) }
}
/// Creates a `Url` constructor `TokenStream`.
fn url_lit(url: &Url) -> TokenStream {
let url = url.as_str();
quote! { #url.parse().unwrap() }
}
/// Create a map constructor, mapping keys and values with other `TokenStream`s.
///
/// This function is pretty generic because the types of keys AND values get transformed.
fn map_lit<Map, Key, Value, TokenStreamKey, TokenStreamValue, FuncKey, FuncValue>(
map_type: TokenStream,
map: Map,
map_key: FuncKey,
map_value: FuncValue,
) -> TokenStream
where
<Map as IntoIterator>::IntoIter: ExactSizeIterator,
Map: IntoIterator<Item = (Key, Value)>,
TokenStreamKey: ToTokens,
TokenStreamValue: ToTokens,
FuncKey: Fn(Key) -> TokenStreamKey,
FuncValue: Fn(Value) -> TokenStreamValue,
{
let ident = quote::format_ident!("map");
let map = map.into_iter();
if map.len() > 0 {
let items = map.map(|(key, value)| {
let key = map_key(key);
let value = map_value(value);
quote! { #ident.insert(#key, #value); }
});
quote! {{
let mut #ident = #map_type::new();
#(#items)*
#ident
}}
} else {
quote! { #map_type::new() }
}
}
/// Create a `serde_json::Value` variant `TokenStream` for a number
fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
// See https://docs.rs/serde_json/1/serde_json/struct.Number.html for guarantees
let prefix = quote! { ::serde_json::Value };
if num.is_u64() {
// guaranteed u64
let num = num.as_u64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else if num.is_i64() {
// guaranteed i64
let num = num.as_i64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else if num.is_f64() {
// guaranteed f64
let num = num.as_f64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else {
// invalid number
quote! { #prefix::Null }
}
}
/// Create a `serde_json::Value` constructor `TokenStream`
fn json_value_lit(jv: &JsonValue) -> TokenStream {
let prefix = quote! { ::serde_json::Value };
match jv {
JsonValue::Null => quote! { #prefix::Null },
JsonValue::Bool(bool) => quote! { #prefix::Bool(#bool) },
JsonValue::Number(number) => json_value_number_lit(number),
JsonValue::String(str) => {
let s = str_lit(str);
quote! { #prefix::String(#s) }
}
JsonValue::Array(vec) => {
let items = vec.iter().map(json_value_lit);
quote! { #prefix::Array(vec![#(#items),*]) }
}
JsonValue::Object(map) => {
let map = map_lit(quote! { ::serde_json::Map }, map, str_lit, json_value_lit);
quote! { #prefix::Object(#map) }
}
}
}
/// Write a `TokenStream` of the `$struct`'s fields to the `$tokens`.
///
/// All fields must represent a binding of the same name that implements `ToTokens`.
macro_rules! literal_struct {
($tokens:ident, $struct:ident, $($field:ident),+) => {
$tokens.append_all(quote! {
::tauri::utils::config::$struct {
$($field: #$field),+
}
});
};
}
impl ToTokens for WindowUrl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::WindowUrl };
tokens.append_all(match self {
Self::App(path) => {
let path = path_buf_lit(path);
quote! { #prefix::App(#path) }
}
Self::External(url) => {
let url = url_lit(url);
quote! { #prefix::External(#url) }
}
})
}
}
impl ToTokens for crate::Theme {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::Theme };
tokens.append_all(match self {
Self::Light => quote! { #prefix::Light },
Self::Dark => quote! { #prefix::Dark },
})
}
}
impl ToTokens for crate::TitleBarStyle {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::TitleBarStyle };
tokens.append_all(match self {
Self::Visible => quote! { #prefix::Visible },
Self::Transparent => quote! { #prefix::Transparent },
Self::Overlay => quote! { #prefix::Overlay },
})
}
}
impl ToTokens for WindowConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let label = str_lit(&self.label);
let url = &self.url;
let user_agent = opt_str_lit(self.user_agent.as_ref());
let file_drop_enabled = self.file_drop_enabled;
let center = self.center;
let x = opt_lit(self.x.as_ref());
let y = opt_lit(self.y.as_ref());
let width = self.width;
let height = self.height;
let min_width = opt_lit(self.min_width.as_ref());
let min_height = opt_lit(self.min_height.as_ref());
let max_width = opt_lit(self.max_width.as_ref());
let max_height = opt_lit(self.max_height.as_ref());
let resizable = self.resizable;
let title = str_lit(&self.title);
let fullscreen = self.fullscreen;
let focus = self.focus;
let transparent = self.transparent;
let maximized = self.maximized;
let visible = self.visible;
let decorations = self.decorations;
let always_on_top = self.always_on_top;
let content_protected = self.content_protected;
let skip_taskbar = self.skip_taskbar;
let theme = opt_lit(self.theme.as_ref());
let title_bar_style = &self.title_bar_style;
let hidden_title = self.hidden_title;
let accept_first_mouse = self.accept_first_mouse;
let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
literal_struct!(
tokens,
WindowConfig,
label,
url,
user_agent,
file_drop_enabled,
center,
x,
y,
width,
height,
min_width,
min_height,
max_width,
max_height,
resizable,
title,
fullscreen,
focus,
transparent,
maximized,
visible,
decorations,
always_on_top,
content_protected,
skip_taskbar,
theme,
title_bar_style,
hidden_title,
accept_first_mouse,
tabbing_identifier,
additional_browser_args
);
}
}
impl ToTokens for CliArg {
fn to_tokens(&self, tokens: &mut TokenStream) {
let short = opt_lit(self.short.as_ref());
let name = str_lit(&self.name);
let description = opt_str_lit(self.description.as_ref());
let long_description = opt_str_lit(self.long_description.as_ref());
let takes_value = self.takes_value;
let multiple = self.multiple;
let multiple_occurrences = self.multiple_occurrences;
let number_of_values = opt_lit(self.number_of_values.as_ref());
let possible_values = opt_vec_str_lit(self.possible_values.as_ref());
let min_values = opt_lit(self.min_values.as_ref());
let max_values = opt_lit(self.max_values.as_ref());
let required = self.required;
let required_unless_present = opt_str_lit(self.required_unless_present.as_ref());
let required_unless_present_all = opt_vec_str_lit(self.required_unless_present_all.as_ref());
let required_unless_present_any = opt_vec_str_lit(self.required_unless_present_any.as_ref());
let conflicts_with = opt_str_lit(self.conflicts_with.as_ref());
let conflicts_with_all = opt_vec_str_lit(self.conflicts_with_all.as_ref());
let requires = opt_str_lit(self.requires.as_ref());
let requires_all = opt_vec_str_lit(self.requires_all.as_ref());
let requires_if = opt_vec_str_lit(self.requires_if.as_ref());
let required_if_eq = opt_vec_str_lit(self.required_if_eq.as_ref());
let require_equals = opt_lit(self.require_equals.as_ref());
let index = opt_lit(self.index.as_ref());
literal_struct!(
tokens,
CliArg,
short,
name,
description,
long_description,
takes_value,
multiple,
multiple_occurrences,
number_of_values,
possible_values,
min_values,
max_values,
required,
required_unless_present,
required_unless_present_all,
required_unless_present_any,
conflicts_with,
conflicts_with_all,
requires,
requires_all,
requires_if,
required_if_eq,
require_equals,
index
);
}
}
impl ToTokens for CliConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let description = opt_str_lit(self.description.as_ref());
let long_description = opt_str_lit(self.long_description.as_ref());
let before_help = opt_str_lit(self.before_help.as_ref());
let after_help = opt_str_lit(self.after_help.as_ref());
let args = {
let args = self.args.as_ref().map(|args| {
let arg = args.iter().map(|a| quote! { #a });
quote! { vec![#(#arg),*] }
});
opt_lit(args.as_ref())
};
let subcommands = opt_lit(
self
.subcommands
.as_ref()
.map(|map| {
map_lit(
quote! { ::std::collections::HashMap },
map,
str_lit,
identity,
)
})
.as_ref(),
);
literal_struct!(
tokens,
CliConfig,
description,
long_description,
before_help,
after_help,
args,
subcommands
);
}
}
impl ToTokens for PatternKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::PatternKind };
tokens.append_all(match self {
Self::Brownfield => quote! { #prefix::Brownfield },
#[cfg(not(feature = "isolation"))]
Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
#[cfg(feature = "isolation")]
Self::Isolation { dir } => {
let dir = path_buf_lit(dir);
quote! { #prefix::Isolation { dir: #dir } }
}
})
}
}
impl ToTokens for WebviewInstallMode {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
tokens.append_all(match self {
Self::Skip => quote! { #prefix::Skip },
Self::DownloadBootstrapper { silent } => {
quote! { #prefix::DownloadBootstrapper { silent: #silent } }
}
Self::EmbedBootstrapper { silent } => {
quote! { #prefix::EmbedBootstrapper { silent: #silent } }
}
Self::OfflineInstaller { silent } => {
quote! { #prefix::OfflineInstaller { silent: #silent } }
}
Self::FixedRuntime { path } => {
let path = path_buf_lit(path);
quote! { #prefix::FixedRuntime { path: #path } }
}
})
}
}
impl ToTokens for WindowsConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let webview_install_mode = if let Some(fixed_runtime_path) = &self.webview_fixed_runtime_path
{
WebviewInstallMode::FixedRuntime {
path: fixed_runtime_path.clone(),
}
} else {
self.webview_install_mode.clone()
};
tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
webview_install_mode: #webview_install_mode,
..Default::default()
}})
}
}
impl ToTokens for BundleConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let identifier = str_lit(&self.identifier);
let publisher = quote!(None);
let icon = vec_lit(&self.icon, str_lit);
let active = self.active;
let targets = quote!(Default::default());
let resources = quote!(None);
let copyright = quote!(None);
let category = quote!(None);
let short_description = quote!(None);
let long_description = quote!(None);
let appimage = quote!(Default::default());
let deb = quote!(Default::default());
let macos = quote!(Default::default());
let external_bin = opt_vec_str_lit(self.external_bin.as_ref());
let windows = &self.windows;
literal_struct!(
tokens,
BundleConfig,
active,
identifier,
publisher,
icon,
targets,
resources,
copyright,
category,
short_description,
long_description,
appimage,
deb,
macos,
external_bin,
windows
);
}
}
impl ToTokens for AppUrl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::AppUrl };
tokens.append_all(match self {
Self::Url(url) => {
quote! { #prefix::Url(#url) }
}
Self::Files(files) => {
let files = vec_lit(files, path_buf_lit);
quote! { #prefix::Files(#files) }
}
})
}
}
impl ToTokens for BuildConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let dev_path = &self.dev_path;
let dist_dir = &self.dist_dir;
let with_global_tauri = self.with_global_tauri;
let runner = quote!(None);
let before_dev_command = quote!(None);
let before_build_command = quote!(None);
let before_bundle_command = quote!(None);
let features = quote!(None);
literal_struct!(
tokens,
BuildConfig,
runner,
dev_path,
dist_dir,
with_global_tauri,
before_dev_command,
before_build_command,
before_bundle_command,
features
);
}
}
impl ToTokens for WindowsUpdateInstallMode {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::WindowsUpdateInstallMode };
tokens.append_all(match self {
Self::BasicUi => quote! { #prefix::BasicUi },
Self::Quiet => quote! { #prefix::Quiet },
Self::Passive => quote! { #prefix::Passive },
})
}
}
impl ToTokens for UpdaterWindowsConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let install_mode = &self.install_mode;
let installer_args = vec_lit(&self.installer_args, str_lit);
literal_struct!(tokens, UpdaterWindowsConfig, install_mode, installer_args);
}
}
impl ToTokens for UpdaterConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let active = self.active;
let dialog = self.dialog;
let pubkey = str_lit(&self.pubkey);
let endpoints = opt_lit(
self
.endpoints
.as_ref()
.map(|list| {
vec_lit(list, |url| {
let url = url.0.as_str();
quote! { ::tauri::utils::config::UpdaterEndpoint(#url.parse().unwrap()) }
})
})
.as_ref(),
);
let windows = &self.windows;
literal_struct!(
tokens,
UpdaterConfig,
active,
dialog,
pubkey,
endpoints,
windows
);
}
}
impl ToTokens for CspDirectiveSources {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
tokens.append_all(match self {
Self::Inline(sources) => {
let sources = sources.as_str();
quote!(#prefix::Inline(#sources.into()))
}
Self::List(list) => {
let list = vec_lit(list, str_lit);
quote!(#prefix::List(#list))
}
})
}
}
impl ToTokens for Csp {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::Csp };
tokens.append_all(match self {
Self::Policy(policy) => {
let policy = policy.as_str();
quote!(#prefix::Policy(#policy.into()))
}
Self::DirectiveMap(list) => {
let map = map_lit(
quote! { ::std::collections::HashMap },
list,
str_lit,
identity,
);
quote!(#prefix::DirectiveMap(#map))
}
})
}
}
impl ToTokens for DisabledCspModificationKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
tokens.append_all(match self {
Self::Flag(flag) => {
quote! { #prefix::Flag(#flag) }
}
Self::List(directives) => {
let directives = vec_lit(directives, str_lit);
quote! { #prefix::List(#directives) }
}
});
}
}
impl ToTokens for SecurityConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let csp = opt_lit(self.csp.as_ref());
let dev_csp = opt_lit(self.dev_csp.as_ref());
let freeze_prototype = self.freeze_prototype;
let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
literal_struct!(
tokens,
SecurityConfig,
csp,
dev_csp,
freeze_prototype,
dangerous_disable_asset_csp_modification
);
}
}
impl ToTokens for SystemTrayConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let icon_as_template = self.icon_as_template;
let menu_on_left_click = self.menu_on_left_click;
let icon_path = path_buf_lit(&self.icon_path);
let title = opt_str_lit(self.title.as_ref());
literal_struct!(
tokens,
SystemTrayConfig,
icon_path,
icon_as_template,
menu_on_left_click,
title
);
}
}
impl ToTokens for FsAllowlistScope {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::FsAllowlistScope };
tokens.append_all(match self {
Self::AllowedPaths(allow) => {
let allowed_paths = vec_lit(allow, path_buf_lit);
quote! { #prefix::AllowedPaths(#allowed_paths) }
}
Self::Scope { allow, deny } => {
let allow = vec_lit(allow, path_buf_lit);
let deny = vec_lit(deny, path_buf_lit);
quote! { #prefix::Scope { allow: #allow, deny: #deny } }
}
});
}
}
impl ToTokens for FsAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scope = &self.scope;
tokens.append_all(quote! { ::tauri::utils::config::FsAllowlistConfig { scope: #scope, ..Default::default() } })
}
}
impl ToTokens for ProtocolAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let asset_scope = &self.asset_scope;
tokens.append_all(quote! { ::tauri::utils::config::ProtocolAllowlistConfig { asset_scope: #asset_scope, ..Default::default() } })
}
}
impl ToTokens for HttpAllowlistScope {
fn to_tokens(&self, tokens: &mut TokenStream) {
let allowed_urls = vec_lit(&self.0, url_lit);
tokens.append_all(quote! { ::tauri::utils::config::HttpAllowlistScope(#allowed_urls) })
}
}
impl ToTokens for HttpAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scope = &self.scope;
tokens.append_all(quote! { ::tauri::utils::config::HttpAllowlistConfig { scope: #scope, ..Default::default() } })
}
}
impl ToTokens for ShellAllowedCommand {
fn to_tokens(&self, tokens: &mut TokenStream) {
let name = str_lit(&self.name);
let command = path_buf_lit(&self.command);
let args = &self.args;
let sidecar = &self.sidecar;
literal_struct!(tokens, ShellAllowedCommand, name, command, args, sidecar);
}
}
impl ToTokens for ShellAllowedArgs {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::ShellAllowedArgs };
tokens.append_all(match self {
Self::Flag(flag) => quote!(#prefix::Flag(#flag)),
Self::List(list) => {
let list = vec_lit(list, identity);
quote!(#prefix::List(#list))
}
})
}
}
impl ToTokens for ShellAllowedArg {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::ShellAllowedArg };
tokens.append_all(match self {
Self::Fixed(fixed) => {
let fixed = str_lit(fixed);
quote!(#prefix::Fixed(#fixed))
}
Self::Var { validator } => {
let validator = str_lit(validator);
quote!(#prefix::Var { validator: #validator })
}
})
}
}
impl ToTokens for ShellAllowlistOpen {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::ShellAllowlistOpen };
tokens.append_all(match self {
Self::Flag(flag) => quote!(#prefix::Flag(#flag)),
Self::Validate(regex) => quote!(#prefix::Validate(#regex)),
})
}
}
impl ToTokens for ShellAllowlistScope {
fn to_tokens(&self, tokens: &mut TokenStream) {
let allowed_commands = vec_lit(&self.0, identity);
tokens.append_all(quote! { ::tauri::utils::config::ShellAllowlistScope(#allowed_commands) })
}
}
impl ToTokens for ShellAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scope = &self.scope;
tokens.append_all(quote! { ::tauri::utils::config::ShellAllowlistConfig { scope: #scope, ..Default::default() } })
}
}
impl ToTokens for AllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let fs = &self.fs;
let protocol = &self.protocol;
let http = &self.http;
let shell = &self.shell;
tokens.append_all(
quote! { ::tauri::utils::config::AllowlistConfig { fs: #fs, protocol: #protocol, http: #http, shell: #shell, ..Default::default() } },
)
}
}
impl ToTokens for TauriConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let pattern = &self.pattern;
let windows = vec_lit(&self.windows, identity);
let cli = opt_lit(self.cli.as_ref());
let bundle = &self.bundle;
let updater = &self.updater;
let security = &self.security;
let system_tray = opt_lit(self.system_tray.as_ref());
let allowlist = &self.allowlist;
let macos_private_api = self.macos_private_api;
literal_struct!(
tokens,
TauriConfig,
pattern,
windows,
cli,
bundle,
updater,
security,
system_tray,
allowlist,
macos_private_api
);
}
}
impl ToTokens for PluginConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let config = map_lit(
quote! { ::std::collections::HashMap },
&self.0,
str_lit,
json_value_lit,
);
tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
}
}
impl ToTokens for PackageConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let product_name = opt_str_lit(self.product_name.as_ref());
let version = opt_str_lit(self.version.as_ref());
literal_struct!(tokens, PackageConfig, product_name, version);
}
}
impl ToTokens for Config {
fn to_tokens(&self, tokens: &mut TokenStream) {
let schema = quote!(None);
let package = &self.package;
let tauri = &self.tauri;
let build = &self.build;
let plugins = &self.plugins;
literal_struct!(tokens, Config, schema, package, tauri, build, plugins);
}
}
}
#[cfg(test)]
mod test {
use super::*;
// TODO: create a test that compares a config to a json config
#[test]
// test all of the default functions
fn test_defaults() {
// get default tauri config
let t_config = TauriConfig::default();
// get default build config
let b_config = BuildConfig::default();
// get default dev path
let d_path = default_dev_path();
// get default window
let d_windows: Vec<WindowConfig> = vec![];
// get default bundle
let d_bundle = BundleConfig::default();
// get default updater
let d_updater = UpdaterConfig::default();
// create a tauri config.
let tauri = TauriConfig {
pattern: Default::default(),
windows: vec![],
bundle: BundleConfig {
active: false,
targets: Default::default(),
identifier: String::from(""),
publisher: None,
icon: Vec::new(),
resources: None,
copyright: None,
category: None,
short_description: None,
long_description: None,
appimage: Default::default(),
deb: Default::default(),
macos: Default::default(),
external_bin: None,
windows: Default::default(),
},
cli: None,
updater: UpdaterConfig {
active: false,
dialog: true,
pubkey: "".into(),
endpoints: None,
windows: Default::default(),
},
security: SecurityConfig {
csp: None,
dev_csp: None,
freeze_prototype: false,
dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
},
allowlist: AllowlistConfig::default(),
system_tray: None,
macos_private_api: false,
};
// create a build config
let build = BuildConfig {
runner: None,
dev_path: AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap(),
)),
dist_dir: AppUrl::Url(WindowUrl::App("../dist".into())),
before_dev_command: None,
before_build_command: None,
before_bundle_command: None,
features: None,
with_global_tauri: false,
};
// test the configs
assert_eq!(t_config, tauri);
assert_eq!(b_config, build);
assert_eq!(d_bundle, tauri.bundle);
assert_eq!(d_updater, tauri.updater);
assert_eq!(
d_path,
AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap()
))
);
assert_eq!(d_windows, tauri.windows);
}
}