1use semver::Version;
14use serde::{
15 Deserialize, Serialize, Serializer,
16 de::{Deserializer, Error as DeError, Visitor},
17};
18use serde_json::Value as JsonValue;
19use serde_with::skip_serializing_none;
20use url::Url;
21
22use std::{
23 collections::HashMap,
24 fmt::{self, Display},
25 fs::read_to_string,
26 path::PathBuf,
27 str::FromStr,
28};
29
30pub mod parse;
32
33fn default_true() -> bool {
34 true
35}
36
37#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
39#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
40#[serde(untagged)]
41#[non_exhaustive]
42pub enum WindowUrl {
43 External(Url),
45 App(PathBuf),
49}
50
51impl fmt::Display for WindowUrl {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Self::External(url) => write!(f, "{url}"),
55 Self::App(path) => write!(f, "{}", path.display()),
56 }
57 }
58}
59
60impl Default for WindowUrl {
61 fn default() -> Self {
62 Self::App("index.html".into())
63 }
64}
65
66#[derive(Debug, PartialEq, Eq, Clone)]
68#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
69#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))]
70pub enum BundleType {
71 Deb,
73 AppImage,
75 Msi,
77 Nsis,
79 App,
81 Dmg,
83 Updater,
85}
86
87impl Display for BundleType {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(
90 f,
91 "{}",
92 match self {
93 Self::Deb => "deb",
94 Self::AppImage => "appimage",
95 Self::Msi => "msi",
96 Self::Nsis => "nsis",
97 Self::App => "app",
98 Self::Dmg => "dmg",
99 Self::Updater => "updater",
100 }
101 )
102 }
103}
104
105impl Serialize for BundleType {
106 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
107 where
108 S: Serializer,
109 {
110 serializer.serialize_str(self.to_string().as_ref())
111 }
112}
113
114impl<'de> Deserialize<'de> for BundleType {
115 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
116 where
117 D: Deserializer<'de>,
118 {
119 let s = String::deserialize(deserializer)?;
120 match s.to_lowercase().as_str() {
121 "deb" => Ok(Self::Deb),
122 "appimage" => Ok(Self::AppImage),
123 "msi" => Ok(Self::Msi),
124 "nsis" => Ok(Self::Nsis),
125 "app" => Ok(Self::App),
126 "dmg" => Ok(Self::Dmg),
127 "updater" => Ok(Self::Updater),
128 _ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
129 }
130 }
131}
132
133#[derive(Debug, PartialEq, Eq, Clone, Default)]
135pub enum BundleTarget {
136 #[default]
138 All,
139 List(Vec<BundleType>),
141 One(BundleType),
143}
144
145#[cfg(feature = "schema")]
146fn add_description(
147 mut schema: schemars::Schema,
148 description: impl Into<String>,
149) -> schemars::Schema {
150 let value = description.into();
151 if !value.is_empty() {
152 schema.insert("description".to_string(), serde_json::Value::String(value));
153 }
154 schema
155}
156
157#[cfg(feature = "schema")]
158impl schemars::JsonSchema for BundleTarget {
159 fn schema_name() -> std::borrow::Cow<'static, str> {
160 "BundleTarget".into()
161 }
162
163 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
164 let any_of: Vec<serde_json::Value> = vec![
165 serde_json::json!({
166 "enum": ["all"],
167 "description": "Bundle all targets."
168 }),
169 serde_json::Value::from(add_description(
170 generator.subschema_for::<Vec<BundleType>>(),
171 "A list of bundle targets.",
172 )),
173 serde_json::Value::from(add_description(
174 generator.subschema_for::<BundleType>(),
175 "A single bundle target.",
176 )),
177 ];
178
179 schemars::json_schema!({
180 "anyOf": any_of,
181 "description": "Targets to bundle. Each value is case insensitive."
182 })
183 }
184}
185
186impl Serialize for BundleTarget {
187 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
188 where
189 S: Serializer,
190 {
191 match self {
192 Self::All => serializer.serialize_str("all"),
193 Self::List(l) => l.serialize(serializer),
194 Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
195 }
196 }
197}
198
199impl<'de> Deserialize<'de> for BundleTarget {
200 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
201 where
202 D: Deserializer<'de>,
203 {
204 #[derive(Deserialize, Serialize)]
205 #[serde(untagged)]
206 pub enum BundleTargetInner {
207 List(Vec<BundleType>),
208 One(BundleType),
209 All(String),
210 }
211
212 match BundleTargetInner::deserialize(deserializer)? {
213 BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
214 BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {t}"))),
215 BundleTargetInner::List(l) => Ok(Self::List(l)),
216 BundleTargetInner::One(t) => Ok(Self::One(t)),
217 }
218 }
219}
220
221#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
225#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
226#[serde(rename_all = "camelCase", deny_unknown_fields)]
227pub struct AppImageConfig {
228 #[serde(default, alias = "bundle-media-framework")]
231 pub bundle_media_framework: bool,
232}
233
234#[skip_serializing_none]
238#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
239#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
240#[serde(rename_all = "camelCase", deny_unknown_fields)]
241pub struct DebConfig {
242 pub depends: Option<Vec<String>>,
244 #[serde(default)]
246 pub files: HashMap<PathBuf, PathBuf>,
247 pub desktop_template: Option<PathBuf>,
251 pub section: Option<String>,
253 pub priority: Option<String>,
256 pub changelog: Option<PathBuf>,
259}
260
261fn de_minimum_system_version<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
262where
263 D: Deserializer<'de>,
264{
265 let version = Option::<String>::deserialize(deserializer)?;
266 match version {
267 Some(v) if v.is_empty() => Ok(minimum_system_version()),
268 e => Ok(e),
269 }
270}
271
272#[skip_serializing_none]
276#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
277#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
278#[serde(rename_all = "camelCase", deny_unknown_fields)]
279pub struct MacConfig {
280 pub frameworks: Option<Vec<String>>,
284 #[serde(
291 deserialize_with = "de_minimum_system_version",
292 default = "minimum_system_version",
293 alias = "minimum-system-version"
294 )]
295 pub minimum_system_version: Option<String>,
296 #[serde(alias = "exception-domain")]
299 pub exception_domain: Option<String>,
300 pub license: Option<String>,
302 #[serde(alias = "signing-identity")]
304 pub signing_identity: Option<String>,
305 #[serde(alias = "provider-short-name")]
307 pub provider_short_name: Option<String>,
308 pub entitlements: Option<String>,
310}
311
312impl Default for MacConfig {
313 fn default() -> Self {
314 Self {
315 frameworks: None,
316 minimum_system_version: minimum_system_version(),
317 exception_domain: None,
318 license: None,
319 signing_identity: None,
320 provider_short_name: None,
321 entitlements: None,
322 }
323 }
324}
325
326fn minimum_system_version() -> Option<String> {
327 Some("10.13".into())
328}
329
330#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
334#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
335#[serde(rename_all = "camelCase", deny_unknown_fields)]
336pub struct WixLanguageConfig {
337 #[serde(alias = "locale-path")]
339 pub locale_path: Option<String>,
340}
341
342#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
344#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
345#[serde(untagged)]
346pub enum WixLanguage {
347 One(String),
349 List(Vec<String>),
351 Localized(HashMap<String, WixLanguageConfig>),
353}
354
355impl Default for WixLanguage {
356 fn default() -> Self {
357 Self::One("en-US".into())
358 }
359}
360
361#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
365#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
366#[serde(rename_all = "camelCase", deny_unknown_fields)]
367pub struct WixConfig {
368 #[serde(default)]
370 pub language: WixLanguage,
371 pub template: Option<PathBuf>,
373 #[serde(default, alias = "fragment-paths")]
375 pub fragment_paths: Vec<PathBuf>,
376 #[serde(default, alias = "component-group-refs")]
378 pub component_group_refs: Vec<String>,
379 #[serde(default, alias = "component-refs")]
381 pub component_refs: Vec<String>,
382 #[serde(default, alias = "feature-group-refs")]
384 pub feature_group_refs: Vec<String>,
385 #[serde(default, alias = "feature-refs")]
387 pub feature_refs: Vec<String>,
388 #[serde(default, alias = "merge-refs")]
390 pub merge_refs: Vec<String>,
391 #[serde(default, alias = "skip-webview-install")]
395 pub skip_webview_install: bool,
396 pub license: Option<PathBuf>,
400 #[serde(default, alias = "enable-elevated-update-task")]
402 pub enable_elevated_update_task: bool,
403 #[serde(alias = "banner-path")]
408 pub banner_path: Option<PathBuf>,
409 #[serde(alias = "dialog-image-path")]
414 pub dialog_image_path: Option<PathBuf>,
415}
416
417#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
421#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
422#[serde(rename_all = "camelCase", deny_unknown_fields)]
423pub enum NsisCompression {
424 Zlib,
426 Bzip2,
428 Lzma,
430}
431
432#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
434#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
435#[serde(rename_all = "camelCase", deny_unknown_fields)]
436pub struct NsisConfig {
437 pub template: Option<PathBuf>,
439 pub license: Option<PathBuf>,
441 #[serde(alias = "header-image")]
445 pub header_image: Option<PathBuf>,
446 #[serde(alias = "sidebar-image")]
450 pub sidebar_image: Option<PathBuf>,
451 #[serde(alias = "install-icon")]
453 pub installer_icon: Option<PathBuf>,
454 #[serde(default, alias = "install-mode")]
456 pub install_mode: NSISInstallerMode,
457 pub languages: Option<Vec<String>>,
463 pub custom_language_files: Option<HashMap<String, PathBuf>>,
470 #[serde(default, alias = "display-language-selector")]
473 pub display_language_selector: bool,
474 pub compression: Option<NsisCompression>,
478}
479
480#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Default)]
482#[serde(rename_all = "camelCase", deny_unknown_fields)]
483#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
484pub enum NSISInstallerMode {
485 #[default]
491 CurrentUser,
492 PerMachine,
497 Both,
503}
504
505#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
510#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
511#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
512pub enum WebviewInstallMode {
513 Skip,
515 DownloadBootstrapper {
519 #[serde(default = "default_true")]
521 silent: bool,
522 },
523 EmbedBootstrapper {
527 #[serde(default = "default_true")]
529 silent: bool,
530 },
531 OfflineInstaller {
535 #[serde(default = "default_true")]
537 silent: bool,
538 },
539 FixedRuntime {
542 path: PathBuf,
547 },
548}
549
550impl Default for WebviewInstallMode {
551 fn default() -> Self {
552 Self::DownloadBootstrapper { silent: true }
553 }
554}
555
556#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
560#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
561#[serde(rename_all = "camelCase", deny_unknown_fields)]
562pub struct WindowsConfig {
563 #[serde(alias = "digest-algorithm")]
566 pub digest_algorithm: Option<String>,
567 #[serde(alias = "certificate-thumbprint")]
569 pub certificate_thumbprint: Option<String>,
570 #[serde(alias = "timestamp-url")]
572 pub timestamp_url: Option<String>,
573 #[serde(default)]
576 pub tsp: bool,
577 #[serde(default, alias = "webview-install-mode")]
579 pub webview_install_mode: WebviewInstallMode,
580 #[serde(alias = "webview-fixed-runtime-path")]
587 pub webview_fixed_runtime_path: Option<PathBuf>,
588 #[serde(default = "default_true", alias = "allow-downgrades")]
594 pub allow_downgrades: bool,
595 pub wix: Option<WixConfig>,
597 pub nsis: Option<NsisConfig>,
599}
600
601impl Default for WindowsConfig {
602 fn default() -> Self {
603 Self {
604 digest_algorithm: None,
605 certificate_thumbprint: None,
606 timestamp_url: None,
607 tsp: false,
608 webview_install_mode: Default::default(),
609 webview_fixed_runtime_path: None,
610 allow_downgrades: true,
611 wix: None,
612 nsis: None,
613 }
614 }
615}
616
617#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
620#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
621#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
622pub enum BundleResources {
623 List(Vec<String>),
625 Map(HashMap<String, String>),
627}
628
629#[skip_serializing_none]
633#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
634#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
635#[serde(rename_all = "camelCase", deny_unknown_fields)]
636pub struct BundleConfig {
637 #[serde(default)]
639 pub active: bool,
640 #[serde(default)]
642 pub targets: BundleTarget,
643 pub identifier: String,
649 pub publisher: Option<String>,
652 #[serde(default)]
654 pub icon: Vec<String>,
655 pub resources: Option<BundleResources>,
659 pub copyright: Option<String>,
661 pub category: Option<String>,
666 #[serde(alias = "short-description")]
668 pub short_description: Option<String>,
669 #[serde(alias = "long-description")]
671 pub long_description: Option<String>,
672 #[serde(default)]
674 pub appimage: AppImageConfig,
675 #[serde(default)]
677 pub deb: DebConfig,
678 #[serde(rename = "macOS", default)]
680 pub macos: MacConfig,
681 #[serde(alias = "external-bin")]
693 pub external_bin: Option<Vec<String>>,
694 #[serde(default)]
696 pub windows: WindowsConfig,
697}
698
699#[skip_serializing_none]
701#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
702#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
703#[serde(rename_all = "camelCase", deny_unknown_fields)]
704pub struct CliArg {
705 pub short: Option<char>,
709 pub name: String,
711 pub description: Option<String>,
714 #[serde(alias = "long-description")]
717 pub long_description: Option<String>,
718 #[serde(default, alias = "takes-value")]
725 pub takes_value: bool,
726 #[serde(default)]
732 pub multiple: bool,
733 #[serde(default, alias = "multiple-occurrences")]
739 pub multiple_occurrences: bool,
740 #[serde(alias = "number-of-values")]
751 pub number_of_values: Option<usize>,
752 #[serde(alias = "possible-values")]
755 pub possible_values: Option<Vec<String>>,
756 #[serde(alias = "min-values")]
760 pub min_values: Option<usize>,
761 #[serde(alias = "max-values")]
765 pub max_values: Option<usize>,
766 #[serde(default)]
771 pub required: bool,
772 #[serde(alias = "required-unless-present")]
775 pub required_unless_present: Option<String>,
776 #[serde(alias = "required-unless-present-all")]
779 pub required_unless_present_all: Option<Vec<String>>,
780 #[serde(alias = "required-unless-present-any")]
783 pub required_unless_present_any: Option<Vec<String>>,
784 #[serde(alias = "conflicts-with")]
787 pub conflicts_with: Option<String>,
788 #[serde(alias = "conflicts-with-all")]
790 pub conflicts_with_all: Option<Vec<String>>,
791 pub requires: Option<String>,
794 #[serde(alias = "requires-all")]
797 pub requires_all: Option<Vec<String>>,
798 #[serde(alias = "requires-if")]
801 pub requires_if: Option<Vec<String>>,
802 #[serde(alias = "requires-if-eq")]
805 pub required_if_eq: Option<Vec<String>>,
806 #[serde(alias = "requires-equals")]
809 pub require_equals: Option<bool>,
810 #[cfg_attr(feature = "schema", validate(range(min = 1)))]
816 pub index: Option<usize>,
817}
818
819#[skip_serializing_none]
823#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
824#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
825#[serde(rename_all = "camelCase", deny_unknown_fields)]
826pub struct CliConfig {
827 pub description: Option<String>,
829 #[serde(alias = "long-description")]
831 pub long_description: Option<String>,
832 #[serde(alias = "before-help")]
836 pub before_help: Option<String>,
837 #[serde(alias = "after-help")]
841 pub after_help: Option<String>,
842 pub args: Option<Vec<CliArg>>,
844 pub subcommands: Option<HashMap<String, CliConfig>>,
846}
847
848#[skip_serializing_none]
852#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
853#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
854#[serde(rename_all = "camelCase", deny_unknown_fields)]
855pub struct WindowConfig {
856 #[serde(default = "default_window_label")]
858 pub label: String,
859 #[serde(default)]
861 pub url: WindowUrl,
862 #[serde(alias = "user-agent")]
864 pub user_agent: Option<String>,
865 #[serde(default = "default_true", alias = "file-drop-enabled")]
869 pub file_drop_enabled: bool,
870 #[serde(default)]
872 pub center: bool,
873 pub x: Option<f64>,
875 pub y: Option<f64>,
877 #[serde(default = "default_width")]
879 pub width: f64,
880 #[serde(default = "default_height")]
882 pub height: f64,
883 #[serde(alias = "min-width")]
885 pub min_width: Option<f64>,
886 #[serde(alias = "min-height")]
888 pub min_height: Option<f64>,
889 #[serde(alias = "max-width")]
891 pub max_width: Option<f64>,
892 #[serde(alias = "max-height")]
894 pub max_height: Option<f64>,
895 #[serde(default = "default_true")]
897 pub resizable: bool,
898 #[serde(default = "default_true")]
906 pub maximizable: bool,
907 #[serde(default = "default_true")]
913 pub minimizable: bool,
914 #[serde(default = "default_true")]
922 pub closable: bool,
923 #[serde(default = "default_title")]
925 pub title: String,
926 #[serde(default)]
928 pub fullscreen: bool,
929 #[serde(default = "default_true")]
931 pub focus: bool,
932 #[serde(default)]
937 pub transparent: bool,
938 #[serde(default)]
940 pub maximized: bool,
941 #[serde(default = "default_true")]
943 pub visible: bool,
944 #[serde(default = "default_true")]
946 pub decorations: bool,
947 #[serde(default, alias = "always-on-top")]
949 pub always_on_top: bool,
950 #[serde(default, alias = "content-protected")]
952 pub content_protected: bool,
953 #[serde(default, alias = "skip-taskbar")]
955 pub skip_taskbar: bool,
956 pub theme: Option<Theme>,
958 #[serde(default, alias = "title-bar-style")]
960 pub title_bar_style: TitleBarStyle,
961 #[serde(default, alias = "hidden-title")]
963 pub hidden_title: bool,
964 #[serde(default, alias = "accept-first-mouse")]
966 pub accept_first_mouse: bool,
967 #[serde(default, alias = "tabbing-identifier")]
974 pub tabbing_identifier: Option<String>,
975 #[serde(default, alias = "additional-browser-args")]
978 pub additional_browser_args: Option<String>,
979}
980
981impl Default for WindowConfig {
982 fn default() -> Self {
983 Self {
984 label: default_window_label(),
985 url: WindowUrl::default(),
986 user_agent: None,
987 file_drop_enabled: true,
988 center: false,
989 x: None,
990 y: None,
991 width: default_width(),
992 height: default_height(),
993 min_width: None,
994 min_height: None,
995 max_width: None,
996 max_height: None,
997 resizable: true,
998 maximizable: true,
999 minimizable: true,
1000 closable: true,
1001 title: default_title(),
1002 fullscreen: false,
1003 focus: false,
1004 transparent: false,
1005 maximized: false,
1006 visible: true,
1007 decorations: true,
1008 always_on_top: false,
1009 content_protected: false,
1010 skip_taskbar: false,
1011 theme: None,
1012 title_bar_style: Default::default(),
1013 hidden_title: false,
1014 accept_first_mouse: false,
1015 tabbing_identifier: None,
1016 additional_browser_args: None,
1017 }
1018 }
1019}
1020
1021fn default_window_label() -> String {
1022 "main".to_string()
1023}
1024
1025fn default_width() -> f64 {
1026 800f64
1027}
1028
1029fn default_height() -> f64 {
1030 600f64
1031}
1032
1033fn default_title() -> String {
1034 "Tauri App".to_string()
1035}
1036
1037#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1040#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1041#[serde(rename_all = "camelCase", untagged)]
1042pub enum CspDirectiveSources {
1043 Inline(String),
1045 List(Vec<String>),
1047}
1048
1049impl Default for CspDirectiveSources {
1050 fn default() -> Self {
1051 Self::List(Vec::new())
1052 }
1053}
1054
1055impl From<CspDirectiveSources> for Vec<String> {
1056 fn from(sources: CspDirectiveSources) -> Self {
1057 match sources {
1058 CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
1059 CspDirectiveSources::List(l) => l,
1060 }
1061 }
1062}
1063
1064impl CspDirectiveSources {
1065 pub fn contains(&self, source: &str) -> bool {
1067 match self {
1068 Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
1069 Self::List(l) => l.contains(&source.into()),
1070 }
1071 }
1072
1073 pub fn push<S: AsRef<str>>(&mut self, source: S) {
1075 match self {
1076 Self::Inline(s) => {
1077 s.push(' ');
1078 s.push_str(source.as_ref());
1079 }
1080 Self::List(l) => {
1081 l.push(source.as_ref().to_string());
1082 }
1083 }
1084 }
1085}
1086
1087#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1090#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1091#[serde(rename_all = "camelCase", untagged)]
1092pub enum Csp {
1093 Policy(String),
1095 DirectiveMap(HashMap<String, CspDirectiveSources>),
1097}
1098
1099impl From<HashMap<String, CspDirectiveSources>> for Csp {
1100 fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
1101 Self::DirectiveMap(map)
1102 }
1103}
1104
1105impl From<Csp> for HashMap<String, CspDirectiveSources> {
1106 fn from(csp: Csp) -> Self {
1107 match csp {
1108 Csp::Policy(policy) => {
1109 let mut map = HashMap::new();
1110 for directive in policy.split(';') {
1111 let mut tokens = directive.trim().split(' ');
1112 if let Some(directive) = tokens.next() {
1113 let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
1114 map.insert(directive.to_string(), CspDirectiveSources::List(sources));
1115 }
1116 }
1117 map
1118 }
1119 Csp::DirectiveMap(m) => m,
1120 }
1121 }
1122}
1123
1124impl Display for Csp {
1125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1126 match self {
1127 Self::Policy(s) => write!(f, "{s}"),
1128 Self::DirectiveMap(m) => {
1129 let len = m.len();
1130 let mut i = 0;
1131 for (directive, sources) in m {
1132 let sources: Vec<String> = sources.clone().into();
1133 write!(f, "{} {}", directive, sources.join(" "))?;
1134 i += 1;
1135 if i != len {
1136 write!(f, "; ")?;
1137 }
1138 }
1139 Ok(())
1140 }
1141 }
1142 }
1143}
1144
1145#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1147#[serde(untagged)]
1148#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1149pub enum DisabledCspModificationKind {
1150 Flag(bool),
1153 List(Vec<String>),
1155}
1156
1157impl Default for DisabledCspModificationKind {
1158 fn default() -> Self {
1159 Self::Flag(false)
1160 }
1161}
1162
1163#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1165#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1166#[serde(rename_all = "camelCase", deny_unknown_fields)]
1167pub struct RemoteDomainAccessScope {
1168 pub scheme: Option<String>,
1170 pub domain: String,
1172 pub windows: Vec<String>,
1174 #[serde(default)]
1177 pub plugins: Vec<String>,
1178 #[serde(default, rename = "enableTauriAPI", alias = "enable-tauri-api")]
1180 pub enable_tauri_api: bool,
1181}
1182
1183#[skip_serializing_none]
1187#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1188#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1189#[serde(rename_all = "camelCase", deny_unknown_fields)]
1190pub struct SecurityConfig {
1191 pub csp: Option<Csp>,
1197 #[serde(alias = "dev-csp")]
1202 pub dev_csp: Option<Csp>,
1203 #[serde(default, alias = "freeze-prototype")]
1205 pub freeze_prototype: bool,
1206 #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
1219 pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
1220 #[serde(default, alias = "dangerous-remote-domain-ipc-access")]
1233 pub dangerous_remote_domain_ipc_access: Vec<RemoteDomainAccessScope>,
1234 #[serde(default, alias = "dangerous-use-http-scheme")]
1238 pub dangerous_use_http_scheme: bool,
1239}
1240
1241pub trait Allowlist {
1243 fn all_features() -> Vec<&'static str>;
1245 fn to_features(&self) -> Vec<&'static str>;
1247}
1248
1249macro_rules! check_feature {
1250 ($self:ident, $features:ident, $flag:ident, $feature_name: expr) => {
1251 if $self.$flag {
1252 $features.push($feature_name)
1253 }
1254 };
1255}
1256
1257#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1266#[serde(untagged)]
1267#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1268pub enum FsAllowlistScope {
1269 AllowedPaths(Vec<PathBuf>),
1271 #[serde(rename_all = "camelCase")]
1273 Scope {
1274 #[serde(default)]
1276 allow: Vec<PathBuf>,
1277 #[serde(default)]
1280 deny: Vec<PathBuf>,
1281 #[serde(alias = "require-literal-leading-dot")]
1290 require_literal_leading_dot: Option<bool>,
1291 },
1292}
1293
1294impl Default for FsAllowlistScope {
1295 fn default() -> Self {
1296 Self::AllowedPaths(Vec::new())
1297 }
1298}
1299
1300#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1304#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1305#[serde(rename_all = "camelCase", deny_unknown_fields)]
1306pub struct FsAllowlistConfig {
1307 #[serde(default)]
1309 pub scope: FsAllowlistScope,
1310 #[serde(default)]
1312 pub all: bool,
1313 #[serde(default, alias = "read-file")]
1315 pub read_file: bool,
1316 #[serde(default, alias = "write-file")]
1318 pub write_file: bool,
1319 #[serde(default, alias = "read-dir")]
1321 pub read_dir: bool,
1322 #[serde(default, alias = "copy-file")]
1324 pub copy_file: bool,
1325 #[serde(default, alias = "create-dir")]
1327 pub create_dir: bool,
1328 #[serde(default, alias = "remove-dir")]
1330 pub remove_dir: bool,
1331 #[serde(default, alias = "remove-file")]
1333 pub remove_file: bool,
1334 #[serde(default, alias = "rename-file")]
1336 pub rename_file: bool,
1337 #[serde(default)]
1339 pub exists: bool,
1340}
1341
1342impl Allowlist for FsAllowlistConfig {
1343 fn all_features() -> Vec<&'static str> {
1344 let allowlist = Self {
1345 scope: Default::default(),
1346 all: false,
1347 read_file: true,
1348 write_file: true,
1349 read_dir: true,
1350 copy_file: true,
1351 create_dir: true,
1352 remove_dir: true,
1353 remove_file: true,
1354 rename_file: true,
1355 exists: true,
1356 };
1357 let mut features = allowlist.to_features();
1358 features.push("fs-all");
1359 features
1360 }
1361
1362 fn to_features(&self) -> Vec<&'static str> {
1363 if self.all {
1364 vec!["fs-all"]
1365 } else {
1366 let mut features = Vec::new();
1367 check_feature!(self, features, read_file, "fs-read-file");
1368 check_feature!(self, features, write_file, "fs-write-file");
1369 check_feature!(self, features, read_dir, "fs-read-dir");
1370 check_feature!(self, features, copy_file, "fs-copy-file");
1371 check_feature!(self, features, create_dir, "fs-create-dir");
1372 check_feature!(self, features, remove_dir, "fs-remove-dir");
1373 check_feature!(self, features, remove_file, "fs-remove-file");
1374 check_feature!(self, features, rename_file, "fs-rename-file");
1375 check_feature!(self, features, exists, "fs-exists");
1376 features
1377 }
1378 }
1379}
1380
1381#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1385#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1386#[serde(rename_all = "camelCase", deny_unknown_fields)]
1387pub struct WindowAllowlistConfig {
1388 #[serde(default)]
1390 pub all: bool,
1391 #[serde(default)]
1393 pub create: bool,
1394 #[serde(default)]
1396 pub center: bool,
1397 #[serde(default, alias = "request-user-attention")]
1399 pub request_user_attention: bool,
1400 #[serde(default, alias = "set-resizable")]
1402 pub set_resizable: bool,
1403 #[serde(default, alias = "set-maximizable")]
1405 pub set_maximizable: bool,
1406 #[serde(default, alias = "set-minimizable")]
1408 pub set_minimizable: bool,
1409 #[serde(default, alias = "set-closable")]
1411 pub set_closable: bool,
1412 #[serde(default, alias = "set-title")]
1414 pub set_title: bool,
1415 #[serde(default)]
1417 pub maximize: bool,
1418 #[serde(default)]
1420 pub unmaximize: bool,
1421 #[serde(default)]
1423 pub minimize: bool,
1424 #[serde(default)]
1426 pub unminimize: bool,
1427 #[serde(default)]
1429 pub show: bool,
1430 #[serde(default)]
1432 pub hide: bool,
1433 #[serde(default)]
1435 pub close: bool,
1436 #[serde(default, alias = "set-decorations")]
1438 pub set_decorations: bool,
1439 #[serde(default, alias = "set-always-on-top")]
1441 pub set_always_on_top: bool,
1442 #[serde(default, alias = "set-content-protected")]
1444 pub set_content_protected: bool,
1445 #[serde(default, alias = "set-size")]
1447 pub set_size: bool,
1448 #[serde(default, alias = "set-min-size")]
1450 pub set_min_size: bool,
1451 #[serde(default, alias = "set-max-size")]
1453 pub set_max_size: bool,
1454 #[serde(default, alias = "set-position")]
1456 pub set_position: bool,
1457 #[serde(default, alias = "set-fullscreen")]
1459 pub set_fullscreen: bool,
1460 #[serde(default, alias = "set-focus")]
1462 pub set_focus: bool,
1463 #[serde(default, alias = "set-icon")]
1465 pub set_icon: bool,
1466 #[serde(default, alias = "set-skip-taskbar")]
1468 pub set_skip_taskbar: bool,
1469 #[serde(default, alias = "set-cursor-grab")]
1471 pub set_cursor_grab: bool,
1472 #[serde(default, alias = "set-cursor-visible")]
1474 pub set_cursor_visible: bool,
1475 #[serde(default, alias = "set-cursor-icon")]
1477 pub set_cursor_icon: bool,
1478 #[serde(default, alias = "set-cursor-position")]
1480 pub set_cursor_position: bool,
1481 #[serde(default, alias = "set-ignore-cursor-events")]
1483 pub set_ignore_cursor_events: bool,
1484 #[serde(default, alias = "start-dragging")]
1486 pub start_dragging: bool,
1487 #[serde(default)]
1489 pub print: bool,
1490}
1491
1492impl Allowlist for WindowAllowlistConfig {
1493 fn all_features() -> Vec<&'static str> {
1494 let allowlist = Self {
1495 all: false,
1496 create: true,
1497 center: true,
1498 request_user_attention: true,
1499 set_resizable: true,
1500 set_maximizable: true,
1501 set_minimizable: true,
1502 set_closable: true,
1503 set_title: true,
1504 maximize: true,
1505 unmaximize: true,
1506 minimize: true,
1507 unminimize: true,
1508 show: true,
1509 hide: true,
1510 close: true,
1511 set_decorations: true,
1512 set_always_on_top: true,
1513 set_content_protected: false,
1514 set_size: true,
1515 set_min_size: true,
1516 set_max_size: true,
1517 set_position: true,
1518 set_fullscreen: true,
1519 set_focus: true,
1520 set_icon: true,
1521 set_skip_taskbar: true,
1522 set_cursor_grab: true,
1523 set_cursor_visible: true,
1524 set_cursor_icon: true,
1525 set_cursor_position: true,
1526 set_ignore_cursor_events: true,
1527 start_dragging: true,
1528 print: true,
1529 };
1530 let mut features = allowlist.to_features();
1531 features.push("window-all");
1532 features
1533 }
1534
1535 fn to_features(&self) -> Vec<&'static str> {
1536 if self.all {
1537 vec!["window-all"]
1538 } else {
1539 let mut features = Vec::new();
1540 check_feature!(self, features, create, "window-create");
1541 check_feature!(self, features, center, "window-center");
1542 check_feature!(
1543 self,
1544 features,
1545 request_user_attention,
1546 "window-request-user-attention"
1547 );
1548 check_feature!(self, features, set_resizable, "window-set-resizable");
1549 check_feature!(self, features, set_maximizable, "window-set-maximizable");
1550 check_feature!(self, features, set_minimizable, "window-set-minimizable");
1551 check_feature!(self, features, set_closable, "window-set-closable");
1552 check_feature!(self, features, set_title, "window-set-title");
1553 check_feature!(self, features, maximize, "window-maximize");
1554 check_feature!(self, features, unmaximize, "window-unmaximize");
1555 check_feature!(self, features, minimize, "window-minimize");
1556 check_feature!(self, features, unminimize, "window-unminimize");
1557 check_feature!(self, features, show, "window-show");
1558 check_feature!(self, features, hide, "window-hide");
1559 check_feature!(self, features, close, "window-close");
1560 check_feature!(self, features, set_decorations, "window-set-decorations");
1561 check_feature!(
1562 self,
1563 features,
1564 set_always_on_top,
1565 "window-set-always-on-top"
1566 );
1567 check_feature!(
1568 self,
1569 features,
1570 set_content_protected,
1571 "window-set-content-protected"
1572 );
1573 check_feature!(self, features, set_size, "window-set-size");
1574 check_feature!(self, features, set_min_size, "window-set-min-size");
1575 check_feature!(self, features, set_max_size, "window-set-max-size");
1576 check_feature!(self, features, set_position, "window-set-position");
1577 check_feature!(self, features, set_fullscreen, "window-set-fullscreen");
1578 check_feature!(self, features, set_focus, "window-set-focus");
1579 check_feature!(self, features, set_icon, "window-set-icon");
1580 check_feature!(self, features, set_skip_taskbar, "window-set-skip-taskbar");
1581 check_feature!(self, features, set_cursor_grab, "window-set-cursor-grab");
1582 check_feature!(
1583 self,
1584 features,
1585 set_cursor_visible,
1586 "window-set-cursor-visible"
1587 );
1588 check_feature!(self, features, set_cursor_icon, "window-set-cursor-icon");
1589 check_feature!(
1590 self,
1591 features,
1592 set_cursor_position,
1593 "window-set-cursor-position"
1594 );
1595 check_feature!(
1596 self,
1597 features,
1598 set_ignore_cursor_events,
1599 "window-set-ignore-cursor-events"
1600 );
1601 check_feature!(self, features, start_dragging, "window-start-dragging");
1602 check_feature!(self, features, print, "window-print");
1603 features
1604 }
1605 }
1606}
1607
1608#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
1610#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1611pub struct ShellAllowedCommand {
1612 pub name: String,
1617
1618 #[serde(rename = "cmd", default)] pub command: PathBuf,
1626
1627 #[serde(default)]
1629 pub args: ShellAllowedArgs,
1630
1631 #[serde(default)]
1633 pub sidecar: bool,
1634}
1635
1636impl<'de> Deserialize<'de> for ShellAllowedCommand {
1637 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1638 where
1639 D: Deserializer<'de>,
1640 {
1641 #[derive(Deserialize)]
1642 struct InnerShellAllowedCommand {
1643 name: String,
1644 #[serde(rename = "cmd")]
1645 command: Option<PathBuf>,
1646 #[serde(default)]
1647 args: ShellAllowedArgs,
1648 #[serde(default)]
1649 sidecar: bool,
1650 }
1651
1652 let config = InnerShellAllowedCommand::deserialize(deserializer)?;
1653
1654 if !config.sidecar && config.command.is_none() {
1655 return Err(DeError::custom(
1656 "The shell scope `command` value is required.",
1657 ));
1658 }
1659
1660 Ok(ShellAllowedCommand {
1661 name: config.name,
1662 command: config.command.unwrap_or_default(),
1663 args: config.args,
1664 sidecar: config.sidecar,
1665 })
1666 }
1667}
1668
1669#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1675#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1676#[serde(untagged, deny_unknown_fields)]
1677#[non_exhaustive]
1678pub enum ShellAllowedArgs {
1679 Flag(bool),
1681
1682 List(Vec<ShellAllowedArg>),
1684}
1685
1686impl Default for ShellAllowedArgs {
1687 fn default() -> Self {
1688 Self::Flag(false)
1689 }
1690}
1691
1692#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1694#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1695#[serde(untagged, deny_unknown_fields)]
1696#[non_exhaustive]
1697pub enum ShellAllowedArg {
1698 Fixed(String),
1700
1701 Var {
1704 validator: String,
1711 },
1712}
1713
1714#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1717#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1718pub struct ShellAllowlistScope(pub Vec<ShellAllowedCommand>);
1719
1720#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1722#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1723#[serde(untagged, deny_unknown_fields)]
1724#[non_exhaustive]
1725pub enum ShellAllowlistOpen {
1726 Flag(bool),
1730
1731 Validate(String),
1736}
1737
1738impl Default for ShellAllowlistOpen {
1739 fn default() -> Self {
1740 Self::Flag(false)
1741 }
1742}
1743
1744#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1748#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1749#[serde(rename_all = "camelCase", deny_unknown_fields)]
1750pub struct ShellAllowlistConfig {
1751 #[serde(default)]
1754 pub scope: ShellAllowlistScope,
1755 #[serde(default)]
1757 pub all: bool,
1758 #[serde(default)]
1760 pub execute: bool,
1761 #[serde(default)]
1765 pub sidecar: bool,
1766 #[serde(default)]
1768 pub open: ShellAllowlistOpen,
1769}
1770
1771impl Allowlist for ShellAllowlistConfig {
1772 fn all_features() -> Vec<&'static str> {
1773 let allowlist = Self {
1774 scope: Default::default(),
1775 all: false,
1776 execute: true,
1777 sidecar: true,
1778 open: ShellAllowlistOpen::Flag(true),
1779 };
1780 let mut features = allowlist.to_features();
1781 features.push("shell-all");
1782 features
1783 }
1784
1785 fn to_features(&self) -> Vec<&'static str> {
1786 if self.all {
1787 vec!["shell-all"]
1788 } else {
1789 let mut features = Vec::new();
1790 check_feature!(self, features, execute, "shell-execute");
1791 check_feature!(self, features, sidecar, "shell-sidecar");
1792
1793 if !matches!(self.open, ShellAllowlistOpen::Flag(false)) {
1794 features.push("shell-open")
1795 }
1796
1797 features
1798 }
1799 }
1800}
1801
1802#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1806#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1807#[serde(rename_all = "camelCase", deny_unknown_fields)]
1808pub struct DialogAllowlistConfig {
1809 #[serde(default)]
1811 pub all: bool,
1812 #[serde(default)]
1814 pub open: bool,
1815 #[serde(default)]
1817 pub save: bool,
1818 #[serde(default)]
1820 pub message: bool,
1821 #[serde(default)]
1823 pub ask: bool,
1824 #[serde(default)]
1826 pub confirm: bool,
1827}
1828
1829impl Allowlist for DialogAllowlistConfig {
1830 fn all_features() -> Vec<&'static str> {
1831 let allowlist = Self {
1832 all: false,
1833 open: true,
1834 save: true,
1835 message: true,
1836 ask: true,
1837 confirm: true,
1838 };
1839 let mut features = allowlist.to_features();
1840 features.push("dialog-all");
1841 features
1842 }
1843
1844 fn to_features(&self) -> Vec<&'static str> {
1845 if self.all {
1846 vec!["dialog-all"]
1847 } else {
1848 let mut features = Vec::new();
1849 check_feature!(self, features, open, "dialog-open");
1850 check_feature!(self, features, save, "dialog-save");
1851 check_feature!(self, features, message, "dialog-message");
1852 check_feature!(self, features, ask, "dialog-ask");
1853 check_feature!(self, features, confirm, "dialog-confirm");
1854 features
1855 }
1856 }
1857}
1858
1859#[allow(rustdoc::bare_urls)]
1868#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1869#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1872pub struct HttpAllowlistScope(pub Vec<Url>);
1873
1874#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1878#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1879#[serde(rename_all = "camelCase", deny_unknown_fields)]
1880pub struct HttpAllowlistConfig {
1881 #[serde(default)]
1883 pub scope: HttpAllowlistScope,
1884 #[serde(default)]
1886 pub all: bool,
1887 #[serde(default)]
1889 pub request: bool,
1890}
1891
1892impl Allowlist for HttpAllowlistConfig {
1893 fn all_features() -> Vec<&'static str> {
1894 let allowlist = Self {
1895 scope: Default::default(),
1896 all: false,
1897 request: true,
1898 };
1899 let mut features = allowlist.to_features();
1900 features.push("http-all");
1901 features
1902 }
1903
1904 fn to_features(&self) -> Vec<&'static str> {
1905 if self.all {
1906 vec!["http-all"]
1907 } else {
1908 let mut features = Vec::new();
1909 check_feature!(self, features, request, "http-request");
1910 features
1911 }
1912 }
1913}
1914
1915#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1919#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1920#[serde(rename_all = "camelCase", deny_unknown_fields)]
1921pub struct NotificationAllowlistConfig {
1922 #[serde(default)]
1924 pub all: bool,
1925}
1926
1927impl Allowlist for NotificationAllowlistConfig {
1928 fn all_features() -> Vec<&'static str> {
1929 let allowlist = Self { all: false };
1930 let mut features = allowlist.to_features();
1931 features.push("notification-all");
1932 features
1933 }
1934
1935 fn to_features(&self) -> Vec<&'static str> {
1936 if self.all {
1937 vec!["notification-all"]
1938 } else {
1939 vec![]
1940 }
1941 }
1942}
1943
1944#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1948#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1949#[serde(rename_all = "camelCase", deny_unknown_fields)]
1950pub struct GlobalShortcutAllowlistConfig {
1951 #[serde(default)]
1953 pub all: bool,
1954}
1955
1956impl Allowlist for GlobalShortcutAllowlistConfig {
1957 fn all_features() -> Vec<&'static str> {
1958 let allowlist = Self { all: false };
1959 let mut features = allowlist.to_features();
1960 features.push("global-shortcut-all");
1961 features
1962 }
1963
1964 fn to_features(&self) -> Vec<&'static str> {
1965 if self.all {
1966 vec!["global-shortcut-all"]
1967 } else {
1968 vec![]
1969 }
1970 }
1971}
1972
1973#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1977#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1978#[serde(rename_all = "camelCase", deny_unknown_fields)]
1979pub struct OsAllowlistConfig {
1980 #[serde(default)]
1982 pub all: bool,
1983}
1984
1985impl Allowlist for OsAllowlistConfig {
1986 fn all_features() -> Vec<&'static str> {
1987 let allowlist = Self { all: false };
1988 let mut features = allowlist.to_features();
1989 features.push("os-all");
1990 features
1991 }
1992
1993 fn to_features(&self) -> Vec<&'static str> {
1994 if self.all { vec!["os-all"] } else { vec![] }
1995 }
1996}
1997
1998#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2002#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2003#[serde(rename_all = "camelCase", deny_unknown_fields)]
2004pub struct PathAllowlistConfig {
2005 #[serde(default)]
2007 pub all: bool,
2008}
2009
2010impl Allowlist for PathAllowlistConfig {
2011 fn all_features() -> Vec<&'static str> {
2012 let allowlist = Self { all: false };
2013 let mut features = allowlist.to_features();
2014 features.push("path-all");
2015 features
2016 }
2017
2018 fn to_features(&self) -> Vec<&'static str> {
2019 if self.all { vec!["path-all"] } else { vec![] }
2020 }
2021}
2022
2023#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2027#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2028#[serde(rename_all = "camelCase", deny_unknown_fields)]
2029pub struct ProtocolAllowlistConfig {
2030 #[serde(default, alias = "asset-scope")]
2032 pub asset_scope: FsAllowlistScope,
2033 #[serde(default)]
2035 pub all: bool,
2036 #[serde(default)]
2038 pub asset: bool,
2039}
2040
2041impl Allowlist for ProtocolAllowlistConfig {
2042 fn all_features() -> Vec<&'static str> {
2043 let allowlist = Self {
2044 asset_scope: Default::default(),
2045 all: false,
2046 asset: true,
2047 };
2048 let mut features = allowlist.to_features();
2049 features.push("protocol-all");
2050 features
2051 }
2052
2053 fn to_features(&self) -> Vec<&'static str> {
2054 if self.all {
2055 vec!["protocol-all"]
2056 } else {
2057 let mut features = Vec::new();
2058 check_feature!(self, features, asset, "protocol-asset");
2059 features
2060 }
2061 }
2062}
2063
2064#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2068#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2069#[serde(rename_all = "camelCase", deny_unknown_fields)]
2070pub struct ProcessAllowlistConfig {
2071 #[serde(default)]
2073 pub all: bool,
2074 #[serde(default)]
2076 pub relaunch: bool,
2077 #[serde(
2082 default,
2083 alias = "relaunchDangerousAllowSymlinkMacOS",
2084 alias = "relaunch-dangerous-allow-symlink-macos"
2085 )]
2086 pub relaunch_dangerous_allow_symlink_macos: bool,
2087 #[serde(default)]
2089 pub exit: bool,
2090}
2091
2092impl Allowlist for ProcessAllowlistConfig {
2093 fn all_features() -> Vec<&'static str> {
2094 let allowlist = Self {
2095 all: false,
2096 relaunch: true,
2097 relaunch_dangerous_allow_symlink_macos: false,
2098 exit: true,
2099 };
2100 let mut features = allowlist.to_features();
2101 features.push("process-all");
2102 features
2103 }
2104
2105 fn to_features(&self) -> Vec<&'static str> {
2106 if self.all {
2107 vec!["process-all"]
2108 } else {
2109 let mut features = Vec::new();
2110 check_feature!(self, features, relaunch, "process-relaunch");
2111 check_feature!(
2112 self,
2113 features,
2114 relaunch_dangerous_allow_symlink_macos,
2115 "process-relaunch-dangerous-allow-symlink-macos"
2116 );
2117 check_feature!(self, features, exit, "process-exit");
2118 features
2119 }
2120 }
2121}
2122
2123#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2127#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2128#[serde(rename_all = "camelCase", deny_unknown_fields)]
2129pub struct ClipboardAllowlistConfig {
2130 #[serde(default)]
2132 pub all: bool,
2133 #[serde(default, alias = "writeText")]
2135 pub write_text: bool,
2136 #[serde(default, alias = "readText")]
2138 pub read_text: bool,
2139}
2140
2141impl Allowlist for ClipboardAllowlistConfig {
2142 fn all_features() -> Vec<&'static str> {
2143 let allowlist = Self {
2144 all: false,
2145 write_text: true,
2146 read_text: true,
2147 };
2148 let mut features = allowlist.to_features();
2149 features.push("clipboard-all");
2150 features
2151 }
2152
2153 fn to_features(&self) -> Vec<&'static str> {
2154 if self.all {
2155 vec!["clipboard-all"]
2156 } else {
2157 let mut features = Vec::new();
2158 check_feature!(self, features, write_text, "clipboard-write-text");
2159 check_feature!(self, features, read_text, "clipboard-read-text");
2160 features
2161 }
2162 }
2163}
2164
2165#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2169#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2170#[serde(rename_all = "camelCase", deny_unknown_fields)]
2171pub struct AppAllowlistConfig {
2172 #[serde(default)]
2174 pub all: bool,
2175 #[serde(default)]
2177 pub show: bool,
2178 #[serde(default)]
2180 pub hide: bool,
2181}
2182
2183impl Allowlist for AppAllowlistConfig {
2184 fn all_features() -> Vec<&'static str> {
2185 let allowlist = Self {
2186 all: false,
2187 show: true,
2188 hide: true,
2189 };
2190 let mut features = allowlist.to_features();
2191 features.push("app-all");
2192 features
2193 }
2194
2195 fn to_features(&self) -> Vec<&'static str> {
2196 if self.all {
2197 vec!["app-all"]
2198 } else {
2199 let mut features = Vec::new();
2200 check_feature!(self, features, show, "app-show");
2201 check_feature!(self, features, hide, "app-hide");
2202 features
2203 }
2204 }
2205}
2206
2207#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2218#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2219#[serde(rename_all = "camelCase", deny_unknown_fields)]
2220pub struct AllowlistConfig {
2221 #[serde(default)]
2223 pub all: bool,
2224 #[serde(default)]
2226 pub fs: FsAllowlistConfig,
2227 #[serde(default)]
2229 pub window: WindowAllowlistConfig,
2230 #[serde(default)]
2232 pub shell: ShellAllowlistConfig,
2233 #[serde(default)]
2235 pub dialog: DialogAllowlistConfig,
2236 #[serde(default)]
2238 pub http: HttpAllowlistConfig,
2239 #[serde(default)]
2241 pub notification: NotificationAllowlistConfig,
2242 #[serde(default, alias = "global-shortcut")]
2244 pub global_shortcut: GlobalShortcutAllowlistConfig,
2245 #[serde(default)]
2247 pub os: OsAllowlistConfig,
2248 #[serde(default)]
2250 pub path: PathAllowlistConfig,
2251 #[serde(default)]
2253 pub protocol: ProtocolAllowlistConfig,
2254 #[serde(default)]
2256 pub process: ProcessAllowlistConfig,
2257 #[serde(default)]
2259 pub clipboard: ClipboardAllowlistConfig,
2260 #[serde(default)]
2262 pub app: AppAllowlistConfig,
2263}
2264
2265impl Allowlist for AllowlistConfig {
2266 fn all_features() -> Vec<&'static str> {
2267 let mut features = vec!["api-all"];
2268 features.extend(FsAllowlistConfig::all_features());
2269 features.extend(WindowAllowlistConfig::all_features());
2270 features.extend(ShellAllowlistConfig::all_features());
2271 features.extend(DialogAllowlistConfig::all_features());
2272 features.extend(HttpAllowlistConfig::all_features());
2273 features.extend(NotificationAllowlistConfig::all_features());
2274 features.extend(GlobalShortcutAllowlistConfig::all_features());
2275 features.extend(OsAllowlistConfig::all_features());
2276 features.extend(PathAllowlistConfig::all_features());
2277 features.extend(ProtocolAllowlistConfig::all_features());
2278 features.extend(ProcessAllowlistConfig::all_features());
2279 features.extend(ClipboardAllowlistConfig::all_features());
2280 features.extend(AppAllowlistConfig::all_features());
2281 features
2282 }
2283
2284 fn to_features(&self) -> Vec<&'static str> {
2285 if self.all {
2286 vec!["api-all"]
2287 } else {
2288 let mut features = Vec::new();
2289 features.extend(self.fs.to_features());
2290 features.extend(self.window.to_features());
2291 features.extend(self.shell.to_features());
2292 features.extend(self.dialog.to_features());
2293 features.extend(self.http.to_features());
2294 features.extend(self.notification.to_features());
2295 features.extend(self.global_shortcut.to_features());
2296 features.extend(self.os.to_features());
2297 features.extend(self.path.to_features());
2298 features.extend(self.protocol.to_features());
2299 features.extend(self.process.to_features());
2300 features.extend(self.clipboard.to_features());
2301 features.extend(self.app.to_features());
2302 features
2303 }
2304 }
2305}
2306
2307#[skip_serializing_none]
2309#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
2310#[serde(rename_all = "lowercase", tag = "use", content = "options")]
2311#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2312pub enum PatternKind {
2313 #[default]
2315 Brownfield,
2316 Isolation {
2318 dir: PathBuf,
2320 },
2321}
2322
2323#[skip_serializing_none]
2327#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2328#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2329#[serde(rename_all = "camelCase", deny_unknown_fields)]
2330pub struct TauriConfig {
2331 #[serde(default)]
2333 pub pattern: PatternKind,
2334 #[serde(default)]
2336 pub windows: Vec<WindowConfig>,
2337 pub cli: Option<CliConfig>,
2339 #[serde(default)]
2341 pub bundle: BundleConfig,
2342 #[serde(default)]
2344 pub allowlist: AllowlistConfig,
2345 #[serde(default)]
2347 pub security: SecurityConfig,
2348 #[serde(default)]
2350 pub updater: UpdaterConfig,
2351 #[serde(alias = "system-tray")]
2353 pub system_tray: Option<SystemTrayConfig>,
2354 #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
2356 pub macos_private_api: bool,
2357}
2358
2359#[skip_serializing_none]
2363#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
2364#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2365pub struct UpdaterEndpoint(pub Url);
2366
2367impl std::fmt::Display for UpdaterEndpoint {
2368 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2369 write!(f, "{}", self.0)
2370 }
2371}
2372
2373impl<'de> Deserialize<'de> for UpdaterEndpoint {
2374 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2375 where
2376 D: Deserializer<'de>,
2377 {
2378 let url = Url::deserialize(deserializer)?;
2379 #[cfg(all(not(debug_assertions), not(feature = "schema")))]
2380 {
2381 if url.scheme() != "https" {
2382 return Err(serde::de::Error::custom(
2383 "The configured updater endpoint must use the `https` protocol.",
2384 ));
2385 }
2386 }
2387 Ok(Self(url))
2388 }
2389}
2390
2391#[derive(Debug, PartialEq, Eq, Clone, Default)]
2393#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2394#[cfg_attr(feature = "schema", schemars(rename_all = "camelCase"))]
2395pub enum WindowsUpdateInstallMode {
2396 BasicUi,
2398 Quiet,
2401 #[default]
2403 Passive,
2404 }
2407
2408impl Display for WindowsUpdateInstallMode {
2409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2410 write!(
2411 f,
2412 "{}",
2413 match self {
2414 Self::BasicUi => "basicUI",
2415 Self::Quiet => "quiet",
2416 Self::Passive => "passive",
2417 }
2418 )
2419 }
2420}
2421
2422impl Serialize for WindowsUpdateInstallMode {
2423 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
2424 where
2425 S: Serializer,
2426 {
2427 serializer.serialize_str(self.to_string().as_ref())
2428 }
2429}
2430
2431impl<'de> Deserialize<'de> for WindowsUpdateInstallMode {
2432 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
2433 where
2434 D: Deserializer<'de>,
2435 {
2436 let s = String::deserialize(deserializer)?;
2437 match s.to_lowercase().as_str() {
2438 "basicui" => Ok(Self::BasicUi),
2439 "quiet" => Ok(Self::Quiet),
2440 "passive" => Ok(Self::Passive),
2441 _ => Err(DeError::custom(format!(
2442 "unknown update install mode '{s}'"
2443 ))),
2444 }
2445 }
2446}
2447
2448#[skip_serializing_none]
2452#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
2453#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2454#[serde(rename_all = "camelCase", deny_unknown_fields)]
2455pub struct UpdaterWindowsConfig {
2456 #[serde(default, alias = "installer-args")]
2458 pub installer_args: Vec<String>,
2459 #[serde(default, alias = "install-mode")]
2461 pub install_mode: WindowsUpdateInstallMode,
2462}
2463
2464#[skip_serializing_none]
2468#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
2469#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2470#[serde(rename_all = "camelCase", deny_unknown_fields)]
2471pub struct UpdaterConfig {
2472 #[serde(default)]
2474 pub active: bool,
2475 #[serde(default = "default_true")]
2477 pub dialog: bool,
2478 #[allow(rustdoc::bare_urls)]
2489 pub endpoints: Option<Vec<UpdaterEndpoint>>,
2490 #[serde(default)] pub pubkey: String,
2493 #[serde(default)]
2495 pub windows: UpdaterWindowsConfig,
2496}
2497
2498impl<'de> Deserialize<'de> for UpdaterConfig {
2499 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2500 where
2501 D: Deserializer<'de>,
2502 {
2503 #[derive(Deserialize)]
2504 struct InnerUpdaterConfig {
2505 #[serde(default)]
2506 active: bool,
2507 #[serde(default = "default_true")]
2508 dialog: bool,
2509 endpoints: Option<Vec<UpdaterEndpoint>>,
2510 pubkey: Option<String>,
2511 #[serde(default)]
2512 windows: UpdaterWindowsConfig,
2513 }
2514
2515 let config = InnerUpdaterConfig::deserialize(deserializer)?;
2516
2517 if config.active && config.pubkey.is_none() {
2518 return Err(DeError::custom(
2519 "The updater `pubkey` configuration is required.",
2520 ));
2521 }
2522
2523 Ok(UpdaterConfig {
2524 active: config.active,
2525 dialog: config.dialog,
2526 endpoints: config.endpoints,
2527 pubkey: config.pubkey.unwrap_or_default(),
2528 windows: config.windows,
2529 })
2530 }
2531}
2532
2533impl Default for UpdaterConfig {
2534 fn default() -> Self {
2535 Self {
2536 active: false,
2537 dialog: true,
2538 endpoints: None,
2539 pubkey: "".into(),
2540 windows: Default::default(),
2541 }
2542 }
2543}
2544
2545#[skip_serializing_none]
2549#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2550#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2551#[serde(rename_all = "camelCase", deny_unknown_fields)]
2552pub struct SystemTrayConfig {
2553 #[serde(alias = "icon-path")]
2555 pub icon_path: PathBuf,
2556 #[serde(default, alias = "icon-as-template")]
2558 pub icon_as_template: bool,
2559 #[serde(default = "default_true", alias = "menu-on-left-click")]
2561 pub menu_on_left_click: bool,
2562 pub title: Option<String>,
2564}
2565
2566#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2568#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2569#[serde(untagged, deny_unknown_fields)]
2570#[non_exhaustive]
2571pub enum AppUrl {
2572 Url(WindowUrl),
2574 Files(Vec<PathBuf>),
2576}
2577
2578impl std::fmt::Display for AppUrl {
2579 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2580 match self {
2581 Self::Url(url) => write!(f, "{url}"),
2582 Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
2583 }
2584 }
2585}
2586
2587#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2589#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2590#[serde(rename_all = "camelCase", untagged)]
2591pub enum BeforeDevCommand {
2592 Script(String),
2594 ScriptWithOptions {
2596 script: String,
2598 cwd: Option<String>,
2600 #[serde(default)]
2602 wait: bool,
2603 },
2604}
2605
2606#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2608#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2609#[serde(rename_all = "camelCase", untagged)]
2610pub enum HookCommand {
2611 Script(String),
2613 ScriptWithOptions {
2615 script: String,
2617 cwd: Option<String>,
2619 },
2620}
2621
2622#[skip_serializing_none]
2626#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2627#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2628#[serde(rename_all = "camelCase", deny_unknown_fields)]
2629pub struct BuildConfig {
2630 pub runner: Option<String>,
2632 #[serde(default = "default_dev_path", alias = "dev-path")]
2640 pub dev_path: AppUrl,
2641 #[serde(default = "default_dist_dir", alias = "dist-dir")]
2653 pub dist_dir: AppUrl,
2654 #[serde(alias = "before-dev-command")]
2658 pub before_dev_command: Option<BeforeDevCommand>,
2659 #[serde(alias = "before-build-command")]
2663 pub before_build_command: Option<HookCommand>,
2664 #[serde(alias = "before-bundle-command")]
2668 pub before_bundle_command: Option<HookCommand>,
2669 pub features: Option<Vec<String>>,
2671 #[serde(default, alias = "with-global-tauri")]
2673 pub with_global_tauri: bool,
2674}
2675
2676impl Default for BuildConfig {
2677 fn default() -> Self {
2678 Self {
2679 runner: None,
2680 dev_path: default_dev_path(),
2681 dist_dir: default_dist_dir(),
2682 before_dev_command: None,
2683 before_build_command: None,
2684 before_bundle_command: None,
2685 features: None,
2686 with_global_tauri: false,
2687 }
2688 }
2689}
2690
2691fn default_dev_path() -> AppUrl {
2692 AppUrl::Url(WindowUrl::External(
2693 Url::parse("http://localhost:8080").unwrap(),
2694 ))
2695}
2696
2697fn default_dist_dir() -> AppUrl {
2698 AppUrl::Url(WindowUrl::App("../dist".into()))
2699}
2700
2701#[derive(Debug, PartialEq, Eq)]
2702struct PackageVersion(String);
2703
2704impl<'d> serde::Deserialize<'d> for PackageVersion {
2705 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<PackageVersion, D::Error> {
2706 struct PackageVersionVisitor;
2707
2708 impl<'d> Visitor<'d> for PackageVersionVisitor {
2709 type Value = PackageVersion;
2710
2711 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2712 write!(
2713 formatter,
2714 "a semver string or a path to a package.json file"
2715 )
2716 }
2717
2718 fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
2719 let path = PathBuf::from(value);
2720 if path.exists() {
2721 let json_str = read_to_string(&path)
2722 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2723 let package_json: serde_json::Value = serde_json::from_str(&json_str)
2724 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2725 if let Some(obj) = package_json.as_object() {
2726 let version = obj
2727 .get("version")
2728 .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
2729 .as_str()
2730 .ok_or_else(|| {
2731 DeError::custom(format!("`{} > version` must be a string", path.display()))
2732 })?;
2733 Ok(PackageVersion(
2734 Version::from_str(version)
2735 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2736 .to_string(),
2737 ))
2738 } else {
2739 Err(DeError::custom(
2740 "`package > version` value is not a path to a JSON object",
2741 ))
2742 }
2743 } else {
2744 Ok(PackageVersion(
2745 Version::from_str(value)
2746 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2747 .to_string(),
2748 ))
2749 }
2750 }
2751 }
2752
2753 deserializer.deserialize_string(PackageVersionVisitor {})
2754 }
2755}
2756
2757#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
2761#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2762#[serde(rename_all = "camelCase", deny_unknown_fields)]
2763pub struct PackageConfig {
2764 #[serde(alias = "product-name")]
2766 #[cfg_attr(feature = "schema", schemars(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
2767 pub product_name: Option<String>,
2768 #[serde(deserialize_with = "version_deserializer", default)]
2770 pub version: Option<String>,
2771}
2772
2773fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
2774where
2775 D: Deserializer<'de>,
2776{
2777 Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
2778}
2779
2780#[allow(rustdoc::invalid_codeblock_attributes)]
2852#[skip_serializing_none]
2853#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2854#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2855#[serde(rename_all = "camelCase", deny_unknown_fields)]
2856pub struct Config {
2857 #[serde(rename = "$schema")]
2859 pub schema: Option<String>,
2860 #[serde(default)]
2862 pub package: PackageConfig,
2863 #[serde(default)]
2865 pub tauri: TauriConfig,
2866 #[serde(default = "default_build")]
2868 pub build: BuildConfig,
2869 #[serde(default)]
2871 pub plugins: PluginConfig,
2872}
2873
2874#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
2878#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2879pub struct PluginConfig(pub HashMap<String, JsonValue>);
2880
2881fn default_build() -> BuildConfig {
2882 BuildConfig {
2883 runner: None,
2884 dev_path: default_dev_path(),
2885 dist_dir: default_dist_dir(),
2886 before_dev_command: None,
2887 before_build_command: None,
2888 before_bundle_command: None,
2889 features: None,
2890 with_global_tauri: false,
2891 }
2892}
2893
2894#[derive(Debug, Clone, PartialEq, Eq, Default)]
2896#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2897pub enum TitleBarStyle {
2898 #[default]
2900 Visible,
2901 Transparent,
2905 Overlay,
2912}
2913
2914impl Serialize for TitleBarStyle {
2915 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
2916 where
2917 S: Serializer,
2918 {
2919 serializer.serialize_str(self.to_string().as_ref())
2920 }
2921}
2922
2923impl<'de> Deserialize<'de> for TitleBarStyle {
2924 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
2925 where
2926 D: Deserializer<'de>,
2927 {
2928 let s = String::deserialize(deserializer)?;
2929 Ok(match s.to_lowercase().as_str() {
2930 "transparent" => Self::Transparent,
2931 "overlay" => Self::Overlay,
2932 _ => Self::Visible,
2933 })
2934 }
2935}
2936
2937impl Display for TitleBarStyle {
2938 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2939 write!(
2940 f,
2941 "{}",
2942 match self {
2943 Self::Visible => "Visible",
2944 Self::Transparent => "Transparent",
2945 Self::Overlay => "Overlay",
2946 }
2947 )
2948 }
2949}
2950
2951#[derive(Debug, Copy, Clone, PartialEq, Eq)]
2953#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
2954#[non_exhaustive]
2955pub enum Theme {
2956 Light,
2958 Dark,
2960}
2961
2962impl Serialize for Theme {
2963 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
2964 where
2965 S: Serializer,
2966 {
2967 serializer.serialize_str(self.to_string().as_ref())
2968 }
2969}
2970
2971impl<'de> Deserialize<'de> for Theme {
2972 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
2973 where
2974 D: Deserializer<'de>,
2975 {
2976 let s = String::deserialize(deserializer)?;
2977 Ok(match s.to_lowercase().as_str() {
2978 "dark" => Self::Dark,
2979 _ => Self::Light,
2980 })
2981 }
2982}
2983
2984impl Display for Theme {
2985 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2986 write!(
2987 f,
2988 "{}",
2989 match self {
2990 Self::Light => "light",
2991 Self::Dark => "dark",
2992 }
2993 )
2994 }
2995}
2996
2997#[cfg(test)]
2998mod test {
2999 use super::*;
3000
3001 #[test]
3004 fn test_defaults() {
3006 let t_config = TauriConfig::default();
3008 let b_config = BuildConfig::default();
3010 let d_path = default_dev_path();
3012 let d_windows: Vec<WindowConfig> = vec![];
3014 let d_bundle = BundleConfig::default();
3016 let d_updater = UpdaterConfig::default();
3018
3019 let tauri = TauriConfig {
3021 pattern: Default::default(),
3022 windows: vec![],
3023 bundle: BundleConfig {
3024 active: false,
3025 targets: Default::default(),
3026 identifier: String::from(""),
3027 publisher: None,
3028 icon: Vec::new(),
3029 resources: None,
3030 copyright: None,
3031 category: None,
3032 short_description: None,
3033 long_description: None,
3034 appimage: Default::default(),
3035 deb: Default::default(),
3036 macos: Default::default(),
3037 external_bin: None,
3038 windows: Default::default(),
3039 },
3040 cli: None,
3041 updater: UpdaterConfig {
3042 active: false,
3043 dialog: true,
3044 pubkey: "".into(),
3045 endpoints: None,
3046 windows: Default::default(),
3047 },
3048 security: SecurityConfig {
3049 csp: None,
3050 dev_csp: None,
3051 freeze_prototype: false,
3052 dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
3053 dangerous_remote_domain_ipc_access: Vec::new(),
3054 dangerous_use_http_scheme: false,
3055 },
3056 allowlist: AllowlistConfig::default(),
3057 system_tray: None,
3058 macos_private_api: false,
3059 };
3060
3061 let build = BuildConfig {
3063 runner: None,
3064 dev_path: AppUrl::Url(WindowUrl::External(
3065 Url::parse("http://localhost:8080").unwrap(),
3066 )),
3067 dist_dir: AppUrl::Url(WindowUrl::App("../dist".into())),
3068 before_dev_command: None,
3069 before_build_command: None,
3070 before_bundle_command: None,
3071 features: None,
3072 with_global_tauri: false,
3073 };
3074
3075 assert_eq!(t_config, tauri);
3077 assert_eq!(b_config, build);
3078 assert_eq!(d_bundle, tauri.bundle);
3079 assert_eq!(d_updater, tauri.updater);
3080 assert_eq!(
3081 d_path,
3082 AppUrl::Url(WindowUrl::External(
3083 Url::parse("http://localhost:8080").unwrap()
3084 ))
3085 );
3086 assert_eq!(d_windows, tauri.windows);
3087 }
3088}