From ae83d008f9e1b89bfc8dddaca42aa5c1fbc36f6d Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 2 Aug 2022 14:12:26 -0300 Subject: [PATCH] feat: add support to TOML config file `Tauri.toml`, closes #4806 (#4813) --- .changes/config-toml.md | 11 ++ .changes/utils-parse-refactor.md | 5 + core/tauri-build/Cargo.toml | 1 + core/tauri-build/src/lib.rs | 2 + core/tauri-codegen/Cargo.toml | 1 + core/tauri-macros/Cargo.toml | 1 + core/tauri-macros/src/context.rs | 4 +- core/tauri-utils/Cargo.toml | 2 + core/tauri-utils/src/config.rs | 180 ++++++++++++++++++--------- core/tauri-utils/src/config/parse.rs | 179 +++++++++++++++++++++----- core/tauri/Cargo.toml | 1 + core/tauri/src/lib.rs | 1 + tooling/cli/Cargo.lock | 1 + tooling/cli/Cargo.toml | 2 +- tooling/cli/schema.json | 4 +- tooling/cli/src/build.rs | 6 +- tooling/cli/src/helpers/app_paths.rs | 14 ++- tooling/cli/src/helpers/config.rs | 71 ++++++----- 18 files changed, 350 insertions(+), 136 deletions(-) create mode 100644 .changes/config-toml.md create mode 100644 .changes/utils-parse-refactor.md diff --git a/.changes/config-toml.md b/.changes/config-toml.md new file mode 100644 index 000000000..c9029ef5d --- /dev/null +++ b/.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). diff --git a/.changes/utils-parse-refactor.md b/.changes/utils-parse-refactor.md new file mode 100644 index 000000000..c0b593c38 --- /dev/null +++ b/.changes/utils-parse-refactor.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": minor +--- + +Refactored the `config::parse` module. diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml index 1e1bf731a..8b89c0be7 100644 --- a/core/tauri-build/Cargo.toml +++ b/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" ] diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index ec219c465..b14c76e5a 100644 --- a/core/tauri-build/src/lib.rs +++ b/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"; diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index feb1c4bda..590465c8f 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/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" ] diff --git a/core/tauri-macros/Cargo.toml b/core/tauri-macros/Cargo.toml index bd5546fbb..ffc706c64 100644 --- a/core/tauri-macros/Cargo.toml +++ b/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" ] diff --git a/core/tauri-macros/src/context.rs b/core/tauri-macros/src/context.rs index 337fe3a60..4be617d20 100644 --- a/core/tauri-macros/src/context.rs +++ b/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!( diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index e2ab5fa2d..1c59d5a65 100644 --- a/core/tauri-utils/Cargo.toml +++ b/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" ] diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 2730b418d..a1cc88aea 100644 --- a/core/tauri-utils/src/config.rs +++ b/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, /// 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, /// The path to the license file to add to the DMG bundle. pub license: Option, /// Identity to use for code signing. + #[serde(alias = "signing-identity")] pub signing_identity: Option, /// Provider short name for notarization. + #[serde(alias = "provider-short-name")] pub provider_short_name: Option, /// Path to the entitlements file. pub entitlements: Option, @@ -333,6 +337,7 @@ fn minimum_system_version() -> Option { #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WixLanguageConfig { /// The path to a locale (`.wxl`) file. See . + #[serde(alias = "locale-path")] pub locale_path: Option, } @@ -366,44 +371,46 @@ pub struct WixConfig { /// A custom .wxs template to use. pub template: Option, /// A list of paths to .wxs files with WiX fragments to use. - #[serde(default)] + #[serde(default, alias = "fragment-paths")] pub fragment_paths: Vec, /// The ComponentGroup element ids you want to reference from the fragments. - #[serde(default)] + #[serde(default, alias = "component-group-refs")] pub component_group_refs: Vec, /// The Component element ids you want to reference from the fragments. - #[serde(default)] + #[serde(default, alias = "component-refs")] pub component_refs: Vec, /// The FeatureGroup element ids you want to reference from the fragments. - #[serde(default)] + #[serde(default, alias = "feature-group-refs")] pub feature_group_refs: Vec, /// The Feature element ids you want to reference from the fragments. - #[serde(default)] + #[serde(default, alias = "feature-refs")] pub feature_refs: Vec, /// The Merge element ids you want to reference from the fragments. - #[serde(default)] + #[serde(default, alias = "merge-refs")] pub merge_refs: Vec, /// 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, /// 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, /// 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, } @@ -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, /// Specifies the SHA1 hash of the signing certificate. + #[serde(alias = "certificate-thumbprint")] pub certificate_thumbprint: Option, /// Server to use during timestamping. + #[serde(alias = "timestamp-url")] pub timestamp_url: Option, /// 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, /// 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, @@ -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, /// A short description of your application. + #[serde(alias = "short-description")] pub short_description: Option, /// A longer, multi-line description of the application. + #[serde(alias = "long-description")] pub long_description: Option, /// 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>, /// Configuration for the Windows bundle. #[serde(default)] @@ -599,6 +613,7 @@ pub struct CliArg { pub description: Option, /// 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, /// 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 ` 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, /// 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>, /// Specifies the minimum number of values for this argument. /// For example, if you had a -f `` 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, /// Specifies the maximum number of values are for this argument. /// For example, if you had a -f `` 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, /// 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, /// 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>, /// 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>, /// 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, /// The same as conflictsWith but allows specifying multiple two-way conflicts per argument. + #[serde(alias = "conflicts-with-all")] pub conflicts_with_all: Option>, /// 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, /// 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>, /// 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>, /// 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>, /// 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, /// 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, /// Command long description which will be shown on the help information. + #[serde(alias = "long-description")] pub long_description: Option, /// 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, /// 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, /// List of arguments for the command pub args: Option>, @@ -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, /// The min window height. + #[serde(alias = "min-height")] pub min_height: Option, /// The max window width. + #[serde(alias = "max-width")] pub max_width: Option, /// The max window height. + #[serde(alias = "max-height")] pub max_height: Option, /// 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, @@ -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 . + #[serde(alias = "dev-csp")] pub dev_csp: Option, /// 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, /// 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, /// 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, /// Features passed to `cargo` commands. pub features: Option>, /// 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, /// 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 diff --git a/core/tauri-utils/src/config/parse.rs b/core/tauri-utils/src/config/parse.rs index 5af03b0f6..6cf710faa 100644 --- a/core/tauri-utils/src/config/parse.rs +++ b/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 { - 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, 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, 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, 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) -> bool { +pub fn does_supported_file_name_exist(path: impl Into) -> 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) -> 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) -> Result { +/// 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) -> 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) -> Result { +pub fn parse_value(path: impl Into) -> Result<(Value, PathBuf), ConfigError> { do_parse(path.into()) } -fn do_parse(path: PathBuf) -> Result { - let json5 = path.with_extension("json5"); +fn do_parse(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(path: PathBuf) -> Result { } }; - 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(path: PathBuf) -> Result { 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(raw: &str, path: &Path) -> Result(raw: &str, path: &Path) -> Result { + ::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 { std::fs::read_to_string(path).map_err(|error| ConfigError::Io { diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 21fefe28c..661a39197 100644 --- a/core/tauri/Cargo.toml +++ b/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" ] diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index eae655c40..2144eae60 100644 --- a/core/tauri/src/lib.rs +++ b/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. //! diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 5d99a0428..e930e7e53 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -2933,6 +2933,7 @@ dependencies = [ "serde_with 1.14.0", "serialize-to-javascript", "thiserror", + "toml", "url", "walkdir", "windows", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 99de4a79c..ac9b478e9 100644 --- a/tooling/cli/Cargo.toml +++ b/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" diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index be0244241..13856f2f6 100644 --- a/tooling/cli/schema.json +++ b/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" }, diff --git a/tooling/cli/src/build.rs b/tooling/cli/src/build.rs index a98dbd5b7..d36cd918d 100644 --- a/tooling/cli/src/build.rs +++ b/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(), }; diff --git a/tooling/cli/src/helpers/app_paths.rs b/tooling/cli/src/helpers/app_paths.rs index e1882e8b3..e9f5b77dc 100644 --- a/tooling/cli/src/helpers/app_paths.rs +++ b/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 bool>(dir: &Path, checker: F) -> Option { @@ -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 { diff --git a/tooling/cli/src/helpers/config.rs b/tooling/cli/src/helpers/config.rs index 7bbf63d80..65d177fa4 100644 --- a/tooling/cli/src/helpers/config.rs +++ b/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, } 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 { 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, reload: bool) -> crate::Result() - .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::() + .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)?;