Selaa lähdekoodia

feat: add support to TOML config file `Tauri.toml`, closes #4806 (#4813)

Lucas Fernandes Nogueira 3 vuotta sitten
vanhempi
sitoutus
ae83d008f9

+ 11 - 0
.changes/config-toml.md

@@ -0,0 +1,11 @@
+---
+"tauri": minor
+"tauri-utils": minor
+"tauri-macros": minor
+"tauri-codegen": minor
+"tauri-build": minor
+"cli.rs": minor
+"cli.js": minor
+---
+
+Added support to configuration files in TOML format (Tauri.toml file).

+ 5 - 0
.changes/utils-parse-refactor.md

@@ -0,0 +1,5 @@
+---
+"tauri-utils": minor
+---
+
+Refactored the `config::parse` module.

+ 1 - 0
core/tauri-build/Cargo.toml

@@ -34,3 +34,4 @@ semver = "1"
 codegen = [ "tauri-codegen", "quote" ]
 isolation = [ "tauri-codegen/isolation", "tauri-utils/isolation" ]
 config-json5 = [ "tauri-utils/config-json5" ]
+config-toml = [ "tauri-utils/config-toml" ]

+ 2 - 0
core/tauri-build/src/lib.rs

@@ -202,6 +202,8 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
   println!("cargo:rerun-if-changed=tauri.conf.json");
   #[cfg(feature = "config-json5")]
   println!("cargo:rerun-if-changed=tauri.conf.json5");
+  #[cfg(feature = "config-toml")]
+  println!("cargo:rerun-if-changed=Tauri.toml");
 
   let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
   let mobile = target_os == "ios" || target_os == "android";

+ 1 - 0
core/tauri-codegen/Cargo.toml

@@ -40,3 +40,4 @@ compression = [ "brotli", "tauri-utils/compression" ]
 isolation = [ "tauri-utils/isolation" ]
 shell-scope = [ "regex" ]
 config-json5 = [ "tauri-utils/config-json5" ]
+config-toml = [ "tauri-utils/config-toml" ]

+ 1 - 0
core/tauri-macros/Cargo.toml

@@ -29,3 +29,4 @@ compression = [ "tauri-codegen/compression" ]
 isolation = [ "tauri-codegen/isolation" ]
 shell-scope = [ "tauri-codegen/shell-scope" ]
 config-json5 = [ "tauri-codegen/config-json5", "tauri-utils/config-json5" ]
+config-toml = [ "tauri-codegen/config-toml", "tauri-utils/config-toml" ]

+ 2 - 2
core/tauri-macros/src/context.rs

@@ -11,7 +11,7 @@ use syn::{
   LitStr, PathArguments, PathSegment, Token,
 };
 use tauri_codegen::{context_codegen, get_config, ContextData};
-use tauri_utils::config::parse::does_supported_extension_exist;
+use tauri_utils::config::parse::does_supported_file_name_exist;
 
 pub(crate) struct ContextItems {
   config_file: PathBuf,
@@ -36,7 +36,7 @@ impl Parse for ContextItems {
       VarError::NotUnicode(_) => "CARGO_MANIFEST_DIR env var contained invalid utf8".into(),
     })
     .and_then(|path| {
-      if does_supported_extension_exist(&path) {
+      if does_supported_file_name_exist(&path) {
         Ok(path)
       } else {
         Err(format!(

+ 2 - 0
core/tauri-utils/Cargo.toml

@@ -29,6 +29,7 @@ getrandom = { version = "0.2", optional = true, features = [ "std" ] }
 serialize-to-javascript = { version = "=0.1.1", optional = true }
 ctor = "0.1"
 json5 = { version = "0.4", optional = true }
+toml = { version = "0.5", optional = true }
 json-patch = "0.2"
 glob = { version = "0.3.0", optional = true }
 walkdir = { version = "2", optional = true }
@@ -56,4 +57,5 @@ schema = [ "schemars" ]
 isolation = [ "aes-gcm", "getrandom", "serialize-to-javascript" ]
 process-relaunch-dangerous-allow-symlink-macos = [ ]
 config-json5 = [ "json5" ]
+config-toml = [ "toml" ]
 resources = [ "glob", "walkdir" ]

+ 121 - 59
core/tauri-utils/src/config.rs

@@ -247,7 +247,7 @@ impl BundleTarget {
 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)]
+  #[serde(default, alias = "bundle-media-framework")]
   pub bundle_media_framework: bool,
 }
 
@@ -293,17 +293,21 @@ pub struct MacConfig {
   /// 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"
+    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>,
@@ -333,6 +337,7 @@ fn minimum_system_version() -> Option<String> {
 #[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>,
 }
 
@@ -366,44 +371,46 @@ pub struct WixConfig {
   /// A custom .wxs template to use.
   pub template: Option<PathBuf>,
   /// A list of paths to .wxs files with WiX fragments to use.
-  #[serde(default)]
+  #[serde(default, alias = "fragment-paths")]
   pub fragment_paths: Vec<PathBuf>,
   /// The ComponentGroup element ids you want to reference from the fragments.
-  #[serde(default)]
+  #[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)]
+  #[serde(default, alias = "component-refs")]
   pub component_refs: Vec<String>,
   /// The FeatureGroup element ids you want to reference from the fragments.
-  #[serde(default)]
+  #[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)]
+  #[serde(default, alias = "feature-refs")]
   pub feature_refs: Vec<String>,
   /// The Merge element ids you want to reference from the fragments.
-  #[serde(default)]
+  #[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)]
+  #[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)]
+  #[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>,
 }
 
@@ -471,17 +478,20 @@ impl Default for WebviewInstallMode {
 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)]
+  #[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.
   ///
@@ -489,13 +499,14 @@ pub struct WindowsConfig {
   ///
   /// 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")]
+  #[serde(default = "default_allow_downgrades", alias = "allow-downgrades")]
   pub allow_downgrades: bool,
   /// Configuration for the MSI generated with WiX.
   pub wix: Option<WixConfig>,
@@ -553,8 +564,10 @@ pub struct BundleConfig {
   /// 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)]
@@ -576,6 +589,7 @@ pub struct BundleConfig {
   /// - "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)]
@@ -599,6 +613,7 @@ pub struct CliArg {
   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.
   ///
@@ -606,7 +621,7 @@ pub struct CliArg {
   /// - 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)]
+  #[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.
   ///
@@ -620,7 +635,7 @@ pub struct CliArg {
   /// 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)]
+  #[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
@@ -632,17 +647,21 @@ pub struct CliArg {
   /// 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.
   ///
@@ -652,32 +671,41 @@ pub struct CliArg {
   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 = "requred-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.
   ///
@@ -697,14 +725,17 @@ 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>>,
@@ -763,7 +794,7 @@ pub struct WindowConfig {
   /// 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")]
+  #[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)]
@@ -779,12 +810,16 @@ pub struct WindowConfig {
   #[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")]
@@ -800,7 +835,7 @@ pub struct WindowConfig {
   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.conf.json > tauri > macOSPrivateApi`.
+  /// 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,
@@ -814,10 +849,10 @@ pub struct WindowConfig {
   #[serde(default = "default_decorations")]
   pub decorations: bool,
   /// Whether the window should always be on top of other windows.
-  #[serde(default)]
+  #[serde(default, alias = "always-on-top")]
   pub always_on_top: bool,
   /// Whether or not the window icon should be added to the taskbar.
-  #[serde(default)]
+  #[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>,
@@ -1048,9 +1083,10 @@ pub struct SecurityConfig {
   ///
   /// 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)]
+  #[serde(default, alias = "freeze-prototype")]
   pub freeze_prototype: bool,
   /// Disables the Tauri-injected CSP sources.
   ///
@@ -1064,7 +1100,7 @@ pub struct SecurityConfig {
   ///
   /// **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)]
+  #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
   pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
 }
 
@@ -1145,28 +1181,28 @@ pub struct FsAllowlistConfig {
   #[serde(default)]
   pub all: bool,
   /// Read file from local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "read-file")]
   pub read_file: bool,
   /// Write file to local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "write-file")]
   pub write_file: bool,
   /// Read directory from local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "read-dir")]
   pub read_dir: bool,
   /// Copy file from local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "copy-file")]
   pub copy_file: bool,
   /// Create directory from local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "create-dir")]
   pub create_dir: bool,
   /// Remove directory from local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "remove-dir")]
   pub remove_dir: bool,
   /// Remove file from local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "remove-file")]
   pub remove_file: bool,
   /// Rename file from local filesystem.
-  #[serde(default)]
+  #[serde(default, alias = "rename-file")]
   pub rename_file: bool,
 }
 
@@ -1222,13 +1258,13 @@ pub struct WindowAllowlistConfig {
   #[serde(default)]
   pub center: bool,
   /// Allows requesting user attention on the window.
-  #[serde(default)]
+  #[serde(default, alias = "request-user-attention")]
   pub request_user_attention: bool,
   /// Allows setting the resizable flag of the window.
-  #[serde(default)]
+  #[serde(default, alias = "set-resizable")]
   pub set_resizable: bool,
   /// Allows changing the window title.
-  #[serde(default)]
+  #[serde(default, alias = "set-title")]
   pub set_title: bool,
   /// Allows maximizing the window.
   #[serde(default)]
@@ -1252,37 +1288,37 @@ pub struct WindowAllowlistConfig {
   #[serde(default)]
   pub close: bool,
   /// Allows setting the decorations flag of the window.
-  #[serde(default)]
+  #[serde(default, alias = "set-decorations")]
   pub set_decorations: bool,
   /// Allows setting the always_on_top flag of the window.
-  #[serde(default)]
+  #[serde(default, alias = "set-always-on-top")]
   pub set_always_on_top: bool,
   /// Allows setting the window size.
-  #[serde(default)]
+  #[serde(default, alias = "set-size")]
   pub set_size: bool,
   /// Allows setting the window minimum size.
-  #[serde(default)]
+  #[serde(default, alias = "set-min-size")]
   pub set_min_size: bool,
   /// Allows setting the window maximum size.
-  #[serde(default)]
+  #[serde(default, alias = "set-max-size")]
   pub set_max_size: bool,
   /// Allows changing the position of the window.
-  #[serde(default)]
+  #[serde(default, alias = "set-position")]
   pub set_position: bool,
   /// Allows setting the fullscreen flag of the window.
-  #[serde(default)]
+  #[serde(default, alias = "set-fullscreen")]
   pub set_fullscreen: bool,
   /// Allows focusing the window.
-  #[serde(default)]
+  #[serde(default, alias = "set-focus")]
   pub set_focus: bool,
   /// Allows changing the window icon.
-  #[serde(default)]
+  #[serde(default, alias = "set-icon")]
   pub set_icon: bool,
   /// Allows setting the skip_taskbar flag of the window.
-  #[serde(default)]
+  #[serde(default, alias = "set-skip-taskbar")]
   pub set_skip_taskbar: bool,
   /// Allows start dragging on the window.
-  #[serde(default)]
+  #[serde(default, alias = "start-dragging")]
   pub start_dragging: bool,
   /// Allows opening the system dialog to print the window content.
   #[serde(default)]
@@ -1779,7 +1815,7 @@ impl Allowlist for PathAllowlistConfig {
 #[serde(rename_all = "camelCase", deny_unknown_fields)]
 pub struct ProtocolAllowlistConfig {
   /// The access scope for the asset protocol.
-  #[serde(default)]
+  #[serde(default, alias = "asset-scope")]
   pub asset_scope: FsAllowlistScope,
   /// Use this flag to enable all custom protocols.
   #[serde(default)]
@@ -1827,7 +1863,11 @@ pub struct ProcessAllowlistConfig {
   ///
   /// 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)]
+  #[serde(
+    default,
+    alias = "relaunchDangerousAllowSymlinkMacOS",
+    alias = "relaunch-dangerous-allow-symlink-macos"
+  )]
   pub relaunch_dangerous_allow_symlink_macos: bool,
   /// Enables the exit API.
   #[serde(default)]
@@ -1874,10 +1914,10 @@ pub struct ClipboardAllowlistConfig {
   #[serde(default)]
   pub all: bool,
   /// Enables the clipboard's `writeText` API.
-  #[serde(default)]
+  #[serde(default, alias = "writeText")]
   pub write_text: bool,
   /// Enables the clipboard's `readText` API.
-  #[serde(default)]
+  #[serde(default, alias = "readText")]
   pub read_text: bool,
 }
 
@@ -1932,7 +1972,7 @@ pub struct AllowlistConfig {
   #[serde(default)]
   pub notification: NotificationAllowlistConfig,
   /// Global shortcut API allowlist.
-  #[serde(default)]
+  #[serde(default, alias = "global-shortcut")]
   pub global_shortcut: GlobalShortcutAllowlistConfig,
   /// OS allowlist.
   #[serde(default)]
@@ -2040,9 +2080,10 @@ pub struct TauriConfig {
   #[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", default)]
+  #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
   pub macos_private_api: bool,
 }
 
@@ -2199,7 +2240,7 @@ impl<'de> Deserialize<'de> for WindowsUpdateInstallMode {
 #[serde(rename_all = "camelCase", deny_unknown_fields)]
 pub struct UpdaterWindowsConfig {
   /// The installation mode for the update on Windows. Defaults to `passive`.
-  #[serde(default)]
+  #[serde(default, alias = "install-mode")]
   pub install_mode: WindowsUpdateInstallMode,
 }
 
@@ -2291,12 +2332,16 @@ pub struct SystemTrayConfig {
   /// Path to the icon to use on the system tray.
   ///
   /// It is forced to be a `.png` file on Linux and macOS, and a `.ico` file on Windows.
+  #[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)]
+  #[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")]
+  #[serde(
+    default = "default_tray_menu_on_left_click",
+    alias = "menu-on-left-click"
+  )]
   pub menu_on_left_click: bool,
 }
 
@@ -2347,7 +2392,7 @@ pub struct BuildConfig {
   ///
   /// 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")]
+  #[serde(default = "default_dev_path", alias = "dev-path")]
   pub dev_path: AppUrl,
   /// The path to the application assets or URL to load in production.
   ///
@@ -2360,20 +2405,22 @@ pub struct BuildConfig {
   ///
   /// 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")]
+  #[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<String>,
   /// 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<String>,
   /// Features passed to `cargo` commands.
   pub features: Option<Vec<String>>,
   /// Whether we should inject the Tauri API on `window.__TAURI__` or not.
-  #[serde(default)]
+  #[serde(default, alias = "with-global-tauri")]
   pub with_global_tauri: bool,
 }
 
@@ -2463,6 +2510,7 @@ impl<'d> serde::Deserialize<'d> for PackageVersion {
 #[serde(rename_all = "camelCase", deny_unknown_fields)]
 pub struct PackageConfig {
   /// App name.
+  #[serde(alias = "product-name")]
   pub product_name: Option<String>,
   /// App version. It is a semver version number or a path to a `package.json` file contaning the `version` field.
   #[serde(deserialize_with = "version_deserializer", default)]
@@ -2491,22 +2539,36 @@ impl PackageConfig {
   }
 }
 
-/// The tauri.conf.json is a file generated by the
+/// 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 JSON defined on the `tauri.conf.json` file, Tauri can
+/// 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` and merges it with
-/// the main `tauri.conf.json` configuration.
+/// `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
 ///
-/// `tauri.conf.json` is composed of the following objects:
+/// The configuration is composed of the following objects:
 ///
 /// - [`package`](#packageconfig): Package settings
 /// - [`tauri`](#tauriconfig): The Tauri config

+ 145 - 34
core/tauri-utils/src/config/parse.rs

@@ -11,15 +11,75 @@ use std::path::{Path, PathBuf};
 use thiserror::Error;
 
 /// All extensions that are possibly supported, but perhaps not enabled.
-pub const EXTENSIONS_SUPPORTED: &[&str] = &["json", "json5"];
+pub const EXTENSIONS_SUPPORTED: &[&str] = &["json", "json5", "toml"];
 
-/// All extensions that are currently enabled.
-pub const EXTENSIONS_ENABLED: &[&str] = &[
-  "json",
+/// All configuration formats that are possibly supported, but perhaps not enabled.
+pub const SUPPORTED_FORMATS: &[ConfigFormat] =
+  &[ConfigFormat::Json, ConfigFormat::Json5, ConfigFormat::Toml];
+
+/// All configuration formats that are currently enabled.
+pub const ENABLED_FORMATS: &[ConfigFormat] = &[
+  ConfigFormat::Json,
   #[cfg(feature = "config-json5")]
-  "json5",
+  ConfigFormat::Json5,
+  #[cfg(feature = "config-toml")]
+  ConfigFormat::Toml,
 ];
 
+/// The available configuration formats.
+#[derive(Debug, Copy, Clone)]
+pub enum ConfigFormat {
+  /// The default JSON (tauri.conf.json) format.
+  Json,
+  /// The JSON5 (tauri.conf.json5) format.
+  Json5,
+  /// The TOML (Tauri.toml file) format.
+  Toml,
+}
+
+impl ConfigFormat {
+  /// Maps the config format to its file name.
+  pub fn into_file_name(self) -> &'static str {
+    match self {
+      Self::Json => "tauri.conf.json",
+      Self::Json5 => "tauri.conf.json5",
+      Self::Toml => "Tauri.toml",
+    }
+  }
+
+  fn into_platform_file_name(self) -> &'static str {
+    match self {
+      Self::Json => {
+        if cfg!(target_os = "macos") {
+          "tauri.macos.conf.json"
+        } else if cfg!(windows) {
+          "tauri.windows.conf.json"
+        } else {
+          "tauri.linux.conf.json"
+        }
+      }
+      Self::Json5 => {
+        if cfg!(target_os = "macos") {
+          "tauri.macos.conf.json5"
+        } else if cfg!(windows) {
+          "tauri.windows.conf.json5"
+        } else {
+          "tauri.linux.conf.json5"
+        }
+      }
+      Self::Toml => {
+        if cfg!(target_os = "macos") {
+          "Tauri.macos.toml"
+        } else if cfg!(windows) {
+          "Tauri.windows.toml"
+        } else {
+          "Tauri.linux.toml"
+        }
+      }
+    }
+  }
+}
+
 /// Represents all the errors that can happen while reading the config.
 #[derive(Debug, Error)]
 #[non_exhaustive]
@@ -45,7 +105,18 @@ pub enum ConfigError {
     error: ::json5::Error,
   },
 
-  /// Unknown file extension encountered.
+  /// Failed to parse from TOML.
+  #[cfg(feature = "config-toml")]
+  #[error("unable to parse toml Tauri config file at {path} because {error}")]
+  FormatToml {
+    /// The path that failed to parse into TOML.
+    path: PathBuf,
+
+    /// The parsing [`toml::Error`].
+    error: ::toml::de::Error,
+  },
+
+  /// Unknown config file name encountered.
   #[error("unsupported format encountered {0}")]
   UnsupportedFormat(String),
 
@@ -81,32 +152,21 @@ pub enum ConfigError {
 ///
 /// [JSON Merge Patch (RFC 7396)]: https://datatracker.ietf.org/doc/html/rfc7396.
 pub fn read_from(root_dir: PathBuf) -> Result<Value, ConfigError> {
-  let mut config: Value = parse_value(root_dir.join("tauri.conf.json"))?;
-  if let Some(platform_config) = read_platform(root_dir)? {
+  let mut config: Value = parse_value(root_dir.join("tauri.conf.json"))?.0;
+  if let Some((platform_config, _)) = read_platform(root_dir)? {
     merge(&mut config, &platform_config);
   }
   Ok(config)
 }
 
-/// Gets the platform configuration file name.
-pub fn get_platform_config_filename() -> &'static str {
-  if cfg!(target_os = "macos") {
-    "tauri.macos.conf.json"
-  } else if cfg!(windows) {
-    "tauri.windows.conf.json"
-  } else {
-    "tauri.linux.conf.json"
-  }
-}
-
 /// Reads the platform-specific configuration file from the given root directory if it exists.
 ///
 /// Check [`read_from`] for more information.
-pub fn read_platform(root_dir: PathBuf) -> Result<Option<Value>, ConfigError> {
-  let platform_config_path = root_dir.join(get_platform_config_filename());
-  if does_supported_extension_exist(&platform_config_path) {
-    let platform_config: Value = parse_value(platform_config_path)?;
-    Ok(Some(platform_config))
+pub fn read_platform(root_dir: PathBuf) -> Result<Option<(Value, PathBuf)>, ConfigError> {
+  let platform_config_path = root_dir.join(ConfigFormat::Json.into_platform_file_name());
+  if does_supported_file_name_exist(&platform_config_path) {
+    let (platform_config, path): (Value, PathBuf) = parse_value(platform_config_path)?;
+    Ok(Some((platform_config, path)))
   } else {
     Ok(None)
   }
@@ -116,11 +176,21 @@ pub fn read_platform(root_dir: PathBuf) -> Result<Option<Value>, ConfigError> {
 ///
 /// The passed path is expected to be the path to the "default" configuration format, in this case
 /// JSON with `.json`.
-pub fn does_supported_extension_exist(path: impl Into<PathBuf>) -> bool {
+pub fn does_supported_file_name_exist(path: impl Into<PathBuf>) -> bool {
   let path = path.into();
-  EXTENSIONS_ENABLED
+  let source_file_name = path.file_name().unwrap().to_str().unwrap();
+  let lookup_platform_config = ENABLED_FORMATS
     .iter()
-    .any(|ext| path.with_extension(ext).exists())
+    .any(|format| source_file_name == format.into_platform_file_name());
+  ENABLED_FORMATS.iter().any(|format| {
+    path
+      .with_file_name(if lookup_platform_config {
+        format.into_platform_file_name()
+      } else {
+        format.into_file_name()
+      })
+      .exists()
+  })
 }
 
 /// Parse the config from path, including alternative formats.
@@ -133,18 +203,39 @@ pub fn does_supported_extension_exist(path: impl Into<PathBuf>) -> bool {
 /// 2. Check if `tauri.conf.json5` exists
 ///   a. Parse it with `json5`
 ///   b. Return error if all above steps failed
-/// 3. Return error if all above steps failed
-pub fn parse(path: impl Into<PathBuf>) -> Result<Config, ConfigError> {
+/// 3. Check if `Tauri.json` exists
+///   a. Parse it with `toml`
+///   b. Return error if all above steps failed
+/// 4. Return error if all above steps failed
+pub fn parse(path: impl Into<PathBuf>) -> Result<(Config, PathBuf), ConfigError> {
   do_parse(path.into())
 }
 
 /// See [`parse`] for specifics, returns a JSON [`Value`] instead of [`Config`].
-pub fn parse_value(path: impl Into<PathBuf>) -> Result<Value, ConfigError> {
+pub fn parse_value(path: impl Into<PathBuf>) -> Result<(Value, PathBuf), ConfigError> {
   do_parse(path.into())
 }
 
-fn do_parse<D: DeserializeOwned>(path: PathBuf) -> Result<D, ConfigError> {
-  let json5 = path.with_extension("json5");
+fn do_parse<D: DeserializeOwned>(path: PathBuf) -> Result<(D, PathBuf), ConfigError> {
+  let file_name = path
+    .file_name()
+    .map(OsStr::to_string_lossy)
+    .unwrap_or_default();
+  let lookup_platform_config = ENABLED_FORMATS
+    .iter()
+    .any(|format| file_name == format.into_platform_file_name());
+
+  let json5 = path.with_file_name(if lookup_platform_config {
+    ConfigFormat::Json5.into_platform_file_name()
+  } else {
+    ConfigFormat::Json5.into_file_name()
+  });
+  let toml = path.with_file_name(if lookup_platform_config {
+    ConfigFormat::Toml.into_platform_file_name()
+  } else {
+    ConfigFormat::Toml.into_file_name()
+  });
+
   let path_ext = path
     .extension()
     .map(OsStr::to_string_lossy)
@@ -171,12 +262,12 @@ fn do_parse<D: DeserializeOwned>(path: PathBuf) -> Result<D, ConfigError> {
       }
     };
 
-    json
+    json.map(|j| (j, path))
   } else if json5.exists() {
     #[cfg(feature = "config-json5")]
     {
       let raw = read_to_string(&json5)?;
-      do_parse_json5(&raw, &path)
+      do_parse_json5(&raw, &path).map(|config| (config, json5))
     }
 
     #[cfg(not(feature = "config-json5"))]
@@ -184,6 +275,18 @@ fn do_parse<D: DeserializeOwned>(path: PathBuf) -> Result<D, ConfigError> {
       extension: ".json5".into(),
       feature: "config-json5".into(),
     })
+  } else if toml.exists() {
+    #[cfg(feature = "config-toml")]
+    {
+      let raw = read_to_string(&toml)?;
+      do_parse_toml(&raw, &path).map(|config| (config, toml))
+    }
+
+    #[cfg(not(feature = "config-toml"))]
+    Err(ConfigError::DisabledFormat {
+      extension: ".toml".into(),
+      feature: "config-toml".into(),
+    })
   } else if !EXTENSIONS_SUPPORTED.contains(&path_ext.as_ref()) {
     Err(ConfigError::UnsupportedFormat(path_ext.to_string()))
   } else {
@@ -241,6 +344,14 @@ fn do_parse_json5<D: DeserializeOwned>(raw: &str, path: &Path) -> Result<D, Conf
   })
 }
 
+#[cfg(feature = "config-toml")]
+fn do_parse_toml<D: DeserializeOwned>(raw: &str, path: &Path) -> Result<D, ConfigError> {
+  ::toml::from_str(raw).map_err(|error| ConfigError::FormatToml {
+    path: path.into(),
+    error,
+  })
+}
+
 /// Helper function to wrap IO errors from [`std::fs::read_to_string`] into a [`ConfigError`].
 fn read_to_string(path: &Path) -> Result<String, ConfigError> {
   std::fs::read_to_string(path).map_err(|error| ConfigError::Io {

+ 1 - 0
core/tauri/Cargo.toml

@@ -285,6 +285,7 @@ window-set-cursor-position = [ ]
 window-start-dragging = [ ]
 window-print = [ ]
 config-json5 = [ "tauri-macros/config-json5" ]
+config-toml = [ "tauri-macros/config-toml" ]
 icon-ico = [ "infer", "ico" ]
 icon-png = [ "infer", "png" ]
 

+ 1 - 0
core/tauri/src/lib.rs

@@ -36,6 +36,7 @@
 //! - **window-data-url**: Enables usage of data URLs on the webview.
 //! - **compression** *(enabled by default): Enables asset compression. You should only disable this if you want faster compile times in release builds - it produces larger binaries.
 //! - **config-json5**: Adds support to JSON5 format for `tauri.conf.json`.
+//! - **config-toml**: Adds support to TOML format for the configuration `Tauri.toml`.
 //! - **icon-ico**: Adds support to set `.ico` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants.
 //! - **icon-png**: Adds support to set `.png` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants.
 //!

+ 1 - 0
tooling/cli/Cargo.lock

@@ -2933,6 +2933,7 @@ dependencies = [
  "serde_with 1.14.0",
  "serialize-to-javascript",
  "thiserror",
+ "toml",
  "url",
  "walkdir",
  "windows",

+ 1 - 1
tooling/cli/Cargo.toml

@@ -39,7 +39,7 @@ notify = "4.0"
 shared_child = "1.0"
 toml_edit = "0.14"
 json-patch = "0.2"
-tauri-utils = { version = "1.0.3", path = "../../core/tauri-utils", features = [ "isolation", "schema", "config-json5" ] }
+tauri-utils = { version = "1.0.3", path = "../../core/tauri-utils", features = [ "isolation", "schema", "config-json5", "config-toml" ] }
 toml = "0.5"
 valico = "3.6"
 handlebars = "4.3"

+ 2 - 2
tooling/cli/schema.json

@@ -1,7 +1,7 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "title": "Config",
-  "description": "The tauri.conf.json is a file generated by the [`tauri init`](https://tauri.app/v1/api/cli#init) command that lives in your Tauri application source directory (src-tauri).\n\nOnce generated, you may modify it at will to customize your Tauri application.\n\n## Platform-Specific Configuration\n\nIn addition to the JSON defined on the `tauri.conf.json` file, Tauri can read a platform-specific configuration from `tauri.linux.conf.json`, `tauri.windows.conf.json`, and `tauri.macos.conf.json` and merges it with the main `tauri.conf.json` configuration.\n\n## Configuration Structure\n\n`tauri.conf.json` is composed of the following objects:\n\n- [`package`](#packageconfig): Package settings - [`tauri`](#tauriconfig): The Tauri config - [`build`](#buildconfig): The build configuration - [`plugins`](#pluginconfig): The plugins config\n\n```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 } ] } } ```",
+  "description": "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.\n\nThe 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).\n\nOnce generated, you may modify it at will to customize your Tauri application.\n\n## File Formats\n\nBy default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\nTauri 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`.\n\n## Platform-Specific Configuration\n\nIn 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.\n\n## Configuration Structure\n\nThe configuration is composed of the following objects:\n\n- [`package`](#packageconfig): Package settings - [`tauri`](#tauriconfig): The Tauri config - [`build`](#buildconfig): The build configuration - [`plugins`](#pluginconfig): The plugins config\n\n```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 } ] } } ```",
   "type": "object",
   "properties": {
     "$schema": {
@@ -587,7 +587,7 @@
           "type": "boolean"
         },
         "transparent": {
-          "description": "Whether the window is transparent or not.\n\nNote that on `macOS` this requires the `macos-private-api` feature flag, enabled under `tauri.conf.json > tauri > macOSPrivateApi`. WARNING: Using private APIs on `macOS` prevents your application from being accepted to the `App Store`.",
+          "description": "Whether the window is transparent or not.\n\nNote 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`.",
           "default": false,
           "type": "boolean"
         },

+ 2 - 4
tooling/cli/src/build.rs

@@ -84,10 +84,8 @@ pub fn command(mut options: Options) -> Result<()> {
   let config_ = config_guard.as_ref().unwrap();
 
   let bundle_identifier_source = match config_.find_bundle_identifier_overwriter() {
-    Some(source) if source == MERGE_CONFIG_EXTENSION_NAME => {
-      merge_config_path.unwrap_or_else(|| source.into())
-    }
-    Some(source) => source.into(),
+    Some(source) if source == MERGE_CONFIG_EXTENSION_NAME => merge_config_path.unwrap_or(source),
+    Some(source) => source,
     None => "tauri.conf.json".into(),
   };
 

+ 11 - 3
tooling/cli/src/helpers/app_paths.rs

@@ -13,6 +13,8 @@ use std::{
 use ignore::WalkBuilder;
 use once_cell::sync::Lazy;
 
+use tauri_utils::config::parse::ConfigFormat;
+
 const TAURI_GITIGNORE: &[u8] = include_bytes!("../../tauri.gitignore");
 
 fn lookup<F: Fn(&PathBuf, FileType) -> bool>(dir: &Path, checker: F) -> Option<PathBuf> {
@@ -66,14 +68,20 @@ fn get_tauri_dir() -> PathBuf {
   }
 
   lookup(&cwd, |path, file_type| if file_type.is_dir() {
-    path.join("tauri.conf.json").exists() || path.join("tauri.conf.json5").exists()
+    path.join(ConfigFormat::Json.into_file_name()).exists() || path.join(ConfigFormat::Json5.into_file_name()).exists() || path.join(ConfigFormat::Toml.into_file_name()).exists()
   } else if let Some(file_name) = path.file_name() {
-    file_name == OsStr::new("tauri.conf.json") || file_name == OsStr::new("tauri.conf.json5")
+    file_name == OsStr::new(ConfigFormat::Json.into_file_name()) || file_name == OsStr::new(ConfigFormat::Json5.into_file_name()) || file_name == OsStr::new(ConfigFormat::Toml.into_file_name())
   } else {
     false
   })
   .map(|p| if p.is_dir() { p } else {  p.parent().unwrap().to_path_buf() })
-  .expect("Couldn't recognize the current folder as a Tauri project. It must contain a `tauri.conf.json` or `tauri.conf.json5` file in any subfolder.")
+  .unwrap_or_else(||
+    panic!("Couldn't recognize the current folder as a Tauri project. It must contain a `{}`, `{}` or `{}` file in any subfolder.",
+      ConfigFormat::Json.into_file_name(),
+      ConfigFormat::Json5.into_file_name(),
+      ConfigFormat::Toml.into_file_name()
+    )
+  )
 }
 
 fn get_app_dir() -> Option<PathBuf> {

+ 40 - 31
tooling/cli/src/helpers/config.rs

@@ -12,6 +12,7 @@ pub use tauri_utils::config::*;
 use std::{
   collections::HashMap,
   env::set_var,
+  ffi::OsStr,
   process::exit,
   sync::{Arc, Mutex},
 };
@@ -23,7 +24,7 @@ pub struct ConfigMetadata {
   inner: Config,
   /// The config extensions (platform-specific config files or the config CLI argument).
   /// Maps the extension name to its value.
-  extensions: HashMap<&'static str, JsonValue>,
+  extensions: HashMap<String, JsonValue>,
 }
 
 impl std::ops::Deref for ConfigMetadata {
@@ -37,7 +38,7 @@ impl std::ops::Deref for ConfigMetadata {
 
 impl ConfigMetadata {
   /// Checks which config is overwriting the bundle identifier.
-  pub fn find_bundle_identifier_overwriter(&self) -> Option<&'static str> {
+  pub fn find_bundle_identifier_overwriter(&self) -> Option<String> {
     for (ext, config) in &self.extensions {
       if let Some(identifier) = config
         .as_object()
@@ -49,7 +50,7 @@ impl ConfigMetadata {
         .and_then(|id| id.as_str())
       {
         if identifier == self.inner.tauri.bundle.identifier {
-          return Some(ext);
+          return Some(ext.clone());
         }
       }
     }
@@ -106,13 +107,17 @@ fn get_internal(merge_config: Option<&str>, reload: bool) -> crate::Result<Confi
   }
 
   let tauri_dir = super::app_paths::tauri_dir();
-  let mut config = tauri_utils::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))?;
+  let (mut config, config_path) =
+    tauri_utils::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))?;
+  let config_file_name = config_path.file_name().unwrap().to_string_lossy();
   let mut extensions = HashMap::new();
 
-  if let Some(platform_config) = tauri_utils::config::parse::read_platform(tauri_dir)? {
+  if let Some((platform_config, config_path)) =
+    tauri_utils::config::parse::read_platform(tauri_dir)?
+  {
     merge(&mut config, &platform_config);
     extensions.insert(
-      tauri_utils::config::parse::get_platform_config_filename(),
+      config_path.file_name().unwrap().to_str().unwrap().into(),
       platform_config,
     );
   }
@@ -122,35 +127,39 @@ fn get_internal(merge_config: Option<&str>, reload: bool) -> crate::Result<Confi
     let merge_config: JsonValue =
       serde_json::from_str(merge_config).with_context(|| "failed to parse config to merge")?;
     merge(&mut config, &merge_config);
-    extensions.insert(MERGE_CONFIG_EXTENSION_NAME, merge_config);
+    extensions.insert(MERGE_CONFIG_EXTENSION_NAME.into(), merge_config);
   };
 
-  let schema: JsonValue = serde_json::from_str(include_str!("../../schema.json"))?;
-  let mut scope = valico::json_schema::Scope::new();
-  let schema = scope.compile_and_return(schema, false).unwrap();
-  let state = schema.validate(&config);
-  if !state.errors.is_empty() {
-    for error in state.errors {
-      let path = error
-        .get_path()
-        .chars()
-        .skip(1)
-        .collect::<String>()
-        .replace('/', " > ");
-      if path.is_empty() {
-        eprintln!(
-          "`tauri.conf.json` error: {}",
-          error.get_detail().unwrap_or_else(|| error.get_title()),
-        );
-      } else {
-        eprintln!(
-          "`tauri.conf.json` error on `{}`: {}",
-          path,
-          error.get_detail().unwrap_or_else(|| error.get_title()),
-        );
+  if config_path.extension() == Some(OsStr::new("json"))
+    || config_path.extension() == Some(OsStr::new("json5"))
+  {
+    let schema: JsonValue = serde_json::from_str(include_str!("../../schema.json"))?;
+    let mut scope = valico::json_schema::Scope::new();
+    let schema = scope.compile_and_return(schema, false).unwrap();
+    let state = schema.validate(&config);
+    if !state.errors.is_empty() {
+      for error in state.errors {
+        let path = error
+          .get_path()
+          .chars()
+          .skip(1)
+          .collect::<String>()
+          .replace('/', " > ");
+        if path.is_empty() {
+          eprintln!(
+            "`{config_file_name}` error: {}",
+            error.get_detail().unwrap_or_else(|| error.get_title()),
+          );
+        } else {
+          eprintln!(
+            "`{config_file_name}` error on `{}`: {}",
+            path,
+            error.get_detail().unwrap_or_else(|| error.get_title()),
+          );
+        }
       }
+      exit(1);
     }
-    exit(1);
   }
 
   let config: Config = serde_json::from_value(config)?;