1use http::response::Builder;
27#[cfg(feature = "schema")]
28use schemars::schema::Schema;
29#[cfg(feature = "schema")]
30use schemars::JsonSchema;
31use semver::Version;
32use serde::{
33 de::{Deserializer, Error as DeError, Visitor},
34 Deserialize, Serialize, Serializer,
35};
36use serde_json::Value as JsonValue;
37use serde_untagged::UntaggedEnumVisitor;
38use serde_with::skip_serializing_none;
39use url::Url;
40
41use std::{
42 collections::HashMap,
43 fmt::{self, Display},
44 fs::read_to_string,
45 path::PathBuf,
46 str::FromStr,
47};
48
49#[cfg(feature = "schema")]
50fn add_description(schema: Schema, description: impl Into<String>) -> Schema {
51 let value = description.into();
52 if value.is_empty() {
53 schema
54 } else {
55 let mut schema_obj = schema.into_object();
56 schema_obj.metadata().description = value.into();
57 Schema::Object(schema_obj)
58 }
59}
60
61pub mod parse;
63
64use crate::{acl::capability::Capability, TitleBarStyle, WindowEffect, WindowEffectState};
65
66pub use self::parse::parse;
67
68fn default_true() -> bool {
69 true
70}
71
72#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
74#[cfg_attr(feature = "schema", derive(JsonSchema))]
75#[serde(untagged)]
76#[non_exhaustive]
77pub enum WebviewUrl {
78 External(Url),
80 App(PathBuf),
84 CustomProtocol(Url),
86}
87
88impl<'de> Deserialize<'de> for WebviewUrl {
89 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
90 where
91 D: Deserializer<'de>,
92 {
93 #[derive(Deserialize)]
94 #[serde(untagged)]
95 enum WebviewUrlDeserializer {
96 Url(Url),
97 Path(PathBuf),
98 }
99
100 match WebviewUrlDeserializer::deserialize(deserializer)? {
101 WebviewUrlDeserializer::Url(u) => {
102 if u.scheme() == "https" || u.scheme() == "http" {
103 Ok(Self::External(u))
104 } else {
105 Ok(Self::CustomProtocol(u))
106 }
107 }
108 WebviewUrlDeserializer::Path(p) => Ok(Self::App(p)),
109 }
110 }
111}
112
113impl fmt::Display for WebviewUrl {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self {
116 Self::External(url) | Self::CustomProtocol(url) => write!(f, "{url}"),
117 Self::App(path) => write!(f, "{}", path.display()),
118 }
119 }
120}
121
122impl Default for WebviewUrl {
123 fn default() -> Self {
124 Self::App("index.html".into())
125 }
126}
127
128#[derive(Debug, PartialEq, Eq, Clone)]
130#[cfg_attr(feature = "schema", derive(JsonSchema))]
131#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))]
132pub enum BundleType {
133 Deb,
135 Rpm,
137 AppImage,
139 Msi,
141 Nsis,
143 App,
145 Dmg,
147}
148
149impl BundleType {
150 fn all() -> &'static [Self] {
152 &[
153 BundleType::Deb,
154 BundleType::Rpm,
155 BundleType::AppImage,
156 BundleType::Msi,
157 BundleType::Nsis,
158 BundleType::App,
159 BundleType::Dmg,
160 ]
161 }
162}
163
164impl Display for BundleType {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(
167 f,
168 "{}",
169 match self {
170 Self::Deb => "deb",
171 Self::Rpm => "rpm",
172 Self::AppImage => "appimage",
173 Self::Msi => "msi",
174 Self::Nsis => "nsis",
175 Self::App => "app",
176 Self::Dmg => "dmg",
177 }
178 )
179 }
180}
181
182impl Serialize for BundleType {
183 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
184 where
185 S: Serializer,
186 {
187 serializer.serialize_str(self.to_string().as_ref())
188 }
189}
190
191impl<'de> Deserialize<'de> for BundleType {
192 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
193 where
194 D: Deserializer<'de>,
195 {
196 let s = String::deserialize(deserializer)?;
197 match s.to_lowercase().as_str() {
198 "deb" => Ok(Self::Deb),
199 "rpm" => Ok(Self::Rpm),
200 "appimage" => Ok(Self::AppImage),
201 "msi" => Ok(Self::Msi),
202 "nsis" => Ok(Self::Nsis),
203 "app" => Ok(Self::App),
204 "dmg" => Ok(Self::Dmg),
205 _ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
206 }
207 }
208}
209
210#[derive(Debug, PartialEq, Eq, Clone)]
212pub enum BundleTarget {
213 All,
215 List(Vec<BundleType>),
217 One(BundleType),
219}
220
221#[cfg(feature = "schema")]
222impl schemars::JsonSchema for BundleTarget {
223 fn schema_name() -> std::string::String {
224 "BundleTarget".to_owned()
225 }
226
227 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
228 let any_of = vec![
229 schemars::schema::SchemaObject {
230 const_value: Some("all".into()),
231 metadata: Some(Box::new(schemars::schema::Metadata {
232 description: Some("Bundle all targets.".to_owned()),
233 ..Default::default()
234 })),
235 ..Default::default()
236 }
237 .into(),
238 add_description(
239 gen.subschema_for::<Vec<BundleType>>(),
240 "A list of bundle targets.",
241 ),
242 add_description(gen.subschema_for::<BundleType>(), "A single bundle target."),
243 ];
244
245 schemars::schema::SchemaObject {
246 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
247 any_of: Some(any_of),
248 ..Default::default()
249 })),
250 metadata: Some(Box::new(schemars::schema::Metadata {
251 description: Some("Targets to bundle. Each value is case insensitive.".to_owned()),
252 ..Default::default()
253 })),
254 ..Default::default()
255 }
256 .into()
257 }
258}
259
260impl Default for BundleTarget {
261 fn default() -> Self {
262 Self::All
263 }
264}
265
266impl Serialize for BundleTarget {
267 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
268 where
269 S: Serializer,
270 {
271 match self {
272 Self::All => serializer.serialize_str("all"),
273 Self::List(l) => l.serialize(serializer),
274 Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
275 }
276 }
277}
278
279impl<'de> Deserialize<'de> for BundleTarget {
280 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
281 where
282 D: Deserializer<'de>,
283 {
284 #[derive(Deserialize, Serialize)]
285 #[serde(untagged)]
286 pub enum BundleTargetInner {
287 List(Vec<BundleType>),
288 One(BundleType),
289 All(String),
290 }
291
292 match BundleTargetInner::deserialize(deserializer)? {
293 BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
294 BundleTargetInner::All(t) => Err(DeError::custom(format!(
295 "invalid bundle type {t}, expected one of `all`, {}",
296 BundleType::all()
297 .iter()
298 .map(|b| format!("`{b}`"))
299 .collect::<Vec<_>>()
300 .join(", ")
301 ))),
302 BundleTargetInner::List(l) => Ok(Self::List(l)),
303 BundleTargetInner::One(t) => Ok(Self::One(t)),
304 }
305 }
306}
307
308impl BundleTarget {
309 #[allow(dead_code)]
311 pub fn to_vec(&self) -> Vec<BundleType> {
312 match self {
313 Self::All => BundleType::all().to_vec(),
314 Self::List(list) => list.clone(),
315 Self::One(i) => vec![i.clone()],
316 }
317 }
318}
319
320#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
324#[cfg_attr(feature = "schema", derive(JsonSchema))]
325#[serde(rename_all = "camelCase", deny_unknown_fields)]
326pub struct AppImageConfig {
327 #[serde(default, alias = "bundle-media-framework")]
330 pub bundle_media_framework: bool,
331 #[serde(default)]
333 pub files: HashMap<PathBuf, PathBuf>,
334}
335
336#[skip_serializing_none]
340#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
341#[cfg_attr(feature = "schema", derive(JsonSchema))]
342#[serde(rename_all = "camelCase", deny_unknown_fields)]
343pub struct DebConfig {
344 pub depends: Option<Vec<String>>,
346 pub recommends: Option<Vec<String>>,
348 pub provides: Option<Vec<String>>,
350 pub conflicts: Option<Vec<String>>,
352 pub replaces: Option<Vec<String>>,
354 #[serde(default)]
356 pub files: HashMap<PathBuf, PathBuf>,
357 pub section: Option<String>,
359 pub priority: Option<String>,
362 pub changelog: Option<PathBuf>,
365 #[serde(alias = "desktop-template")]
369 pub desktop_template: Option<PathBuf>,
370 #[serde(alias = "pre-install-script")]
373 pub pre_install_script: Option<PathBuf>,
374 #[serde(alias = "post-install-script")]
377 pub post_install_script: Option<PathBuf>,
378 #[serde(alias = "pre-remove-script")]
381 pub pre_remove_script: Option<PathBuf>,
382 #[serde(alias = "post-remove-script")]
385 pub post_remove_script: Option<PathBuf>,
386}
387
388#[skip_serializing_none]
392#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
393#[cfg_attr(feature = "schema", derive(JsonSchema))]
394#[serde(rename_all = "camelCase", deny_unknown_fields)]
395pub struct LinuxConfig {
396 #[serde(default)]
398 pub appimage: AppImageConfig,
399 #[serde(default)]
401 pub deb: DebConfig,
402 #[serde(default)]
404 pub rpm: RpmConfig,
405}
406
407#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
409#[cfg_attr(feature = "schema", derive(JsonSchema))]
410#[serde(rename_all = "camelCase", deny_unknown_fields, tag = "type")]
411#[non_exhaustive]
412pub enum RpmCompression {
413 Gzip {
415 level: u32,
417 },
418 Zstd {
420 level: i32,
422 },
423 Xz {
425 level: u32,
427 },
428 Bzip2 {
430 level: u32,
432 },
433 None,
435}
436
437#[skip_serializing_none]
439#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
440#[cfg_attr(feature = "schema", derive(JsonSchema))]
441#[serde(rename_all = "camelCase", deny_unknown_fields)]
442pub struct RpmConfig {
443 pub depends: Option<Vec<String>>,
445 pub recommends: Option<Vec<String>>,
447 pub provides: Option<Vec<String>>,
449 pub conflicts: Option<Vec<String>>,
452 pub obsoletes: Option<Vec<String>>,
455 #[serde(default = "default_release")]
457 pub release: String,
458 #[serde(default)]
460 pub epoch: u32,
461 #[serde(default)]
463 pub files: HashMap<PathBuf, PathBuf>,
464 #[serde(alias = "desktop-template")]
468 pub desktop_template: Option<PathBuf>,
469 #[serde(alias = "pre-install-script")]
472 pub pre_install_script: Option<PathBuf>,
473 #[serde(alias = "post-install-script")]
476 pub post_install_script: Option<PathBuf>,
477 #[serde(alias = "pre-remove-script")]
480 pub pre_remove_script: Option<PathBuf>,
481 #[serde(alias = "post-remove-script")]
484 pub post_remove_script: Option<PathBuf>,
485 pub compression: Option<RpmCompression>,
487}
488
489impl Default for RpmConfig {
490 fn default() -> Self {
491 Self {
492 depends: None,
493 recommends: None,
494 provides: None,
495 conflicts: None,
496 obsoletes: None,
497 release: default_release(),
498 epoch: 0,
499 files: Default::default(),
500 desktop_template: None,
501 pre_install_script: None,
502 post_install_script: None,
503 pre_remove_script: None,
504 post_remove_script: None,
505 compression: None,
506 }
507 }
508}
509
510fn default_release() -> String {
511 "1".into()
512}
513
514#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
516#[cfg_attr(feature = "schema", derive(JsonSchema))]
517#[serde(rename_all = "camelCase", deny_unknown_fields)]
518pub struct Position {
519 pub x: u32,
521 pub y: u32,
523}
524
525#[derive(Default, Debug, PartialEq, Clone, Deserialize, Serialize)]
527#[cfg_attr(feature = "schema", derive(JsonSchema))]
528#[serde(rename_all = "camelCase", deny_unknown_fields)]
529pub struct LogicalPosition {
530 pub x: f64,
532 pub y: f64,
534}
535
536#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
538#[cfg_attr(feature = "schema", derive(JsonSchema))]
539#[serde(rename_all = "camelCase", deny_unknown_fields)]
540pub struct Size {
541 pub width: u32,
543 pub height: u32,
545}
546
547#[skip_serializing_none]
551#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
552#[cfg_attr(feature = "schema", derive(JsonSchema))]
553#[serde(rename_all = "camelCase", deny_unknown_fields)]
554pub struct DmgConfig {
555 pub background: Option<PathBuf>,
557 pub window_position: Option<Position>,
559 #[serde(default = "dmg_window_size", alias = "window-size")]
561 pub window_size: Size,
562 #[serde(default = "dmg_app_position", alias = "app-position")]
564 pub app_position: Position,
565 #[serde(
567 default = "dmg_application_folder_position",
568 alias = "application-folder-position"
569 )]
570 pub application_folder_position: Position,
571}
572
573impl Default for DmgConfig {
574 fn default() -> Self {
575 Self {
576 background: None,
577 window_position: None,
578 window_size: dmg_window_size(),
579 app_position: dmg_app_position(),
580 application_folder_position: dmg_application_folder_position(),
581 }
582 }
583}
584
585fn dmg_window_size() -> Size {
586 Size {
587 width: 660,
588 height: 400,
589 }
590}
591
592fn dmg_app_position() -> Position {
593 Position { x: 180, y: 170 }
594}
595
596fn dmg_application_folder_position() -> Position {
597 Position { x: 480, y: 170 }
598}
599
600fn de_macos_minimum_system_version<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
601where
602 D: Deserializer<'de>,
603{
604 let version = Option::<String>::deserialize(deserializer)?;
605 match version {
606 Some(v) if v.is_empty() => Ok(macos_minimum_system_version()),
607 e => Ok(e),
608 }
609}
610
611#[skip_serializing_none]
615#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
616#[cfg_attr(feature = "schema", derive(JsonSchema))]
617#[serde(rename_all = "camelCase", deny_unknown_fields)]
618pub struct MacConfig {
619 pub frameworks: Option<Vec<String>>,
623 #[serde(default)]
625 pub files: HashMap<PathBuf, PathBuf>,
626 #[serde(alias = "bundle-version")]
630 pub bundle_version: Option<String>,
631 #[serde(alias = "bundle-name")]
637 pub bundle_name: Option<String>,
638 #[serde(
647 deserialize_with = "de_macos_minimum_system_version",
648 default = "macos_minimum_system_version",
649 alias = "minimum-system-version"
650 )]
651 pub minimum_system_version: Option<String>,
652 #[serde(alias = "exception-domain")]
655 pub exception_domain: Option<String>,
656 #[serde(alias = "signing-identity")]
658 pub signing_identity: Option<String>,
659 #[serde(alias = "hardened-runtime", default = "default_true")]
661 pub hardened_runtime: bool,
662 #[serde(alias = "provider-short-name")]
664 pub provider_short_name: Option<String>,
665 pub entitlements: Option<String>,
667 #[serde(alias = "info-plist")]
671 pub info_plist: Option<PathBuf>,
672 #[serde(default)]
674 pub dmg: DmgConfig,
675}
676
677impl Default for MacConfig {
678 fn default() -> Self {
679 Self {
680 frameworks: None,
681 files: HashMap::new(),
682 bundle_version: None,
683 bundle_name: None,
684 minimum_system_version: macos_minimum_system_version(),
685 exception_domain: None,
686 signing_identity: None,
687 hardened_runtime: true,
688 provider_short_name: None,
689 entitlements: None,
690 info_plist: None,
691 dmg: Default::default(),
692 }
693 }
694}
695
696fn macos_minimum_system_version() -> Option<String> {
697 Some("10.13".into())
698}
699
700fn ios_minimum_system_version() -> String {
701 "14.0".into()
702}
703
704#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
708#[cfg_attr(feature = "schema", derive(JsonSchema))]
709#[serde(rename_all = "camelCase", deny_unknown_fields)]
710pub struct WixLanguageConfig {
711 #[serde(alias = "locale-path")]
713 pub locale_path: Option<String>,
714}
715
716#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
718#[cfg_attr(feature = "schema", derive(JsonSchema))]
719#[serde(untagged)]
720pub enum WixLanguage {
721 One(String),
723 List(Vec<String>),
725 Localized(HashMap<String, WixLanguageConfig>),
727}
728
729impl Default for WixLanguage {
730 fn default() -> Self {
731 Self::One("en-US".into())
732 }
733}
734
735#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
739#[cfg_attr(feature = "schema", derive(JsonSchema))]
740#[serde(rename_all = "camelCase", deny_unknown_fields)]
741pub struct WixConfig {
742 pub version: Option<String>,
751 #[serde(alias = "upgrade-code")]
760 pub upgrade_code: Option<uuid::Uuid>,
761 #[serde(default)]
763 pub language: WixLanguage,
764 pub template: Option<PathBuf>,
766 #[serde(default, alias = "fragment-paths")]
768 pub fragment_paths: Vec<PathBuf>,
769 #[serde(default, alias = "component-group-refs")]
771 pub component_group_refs: Vec<String>,
772 #[serde(default, alias = "component-refs")]
774 pub component_refs: Vec<String>,
775 #[serde(default, alias = "feature-group-refs")]
777 pub feature_group_refs: Vec<String>,
778 #[serde(default, alias = "feature-refs")]
780 pub feature_refs: Vec<String>,
781 #[serde(default, alias = "merge-refs")]
783 pub merge_refs: Vec<String>,
784 #[serde(default, alias = "enable-elevated-update-task")]
786 pub enable_elevated_update_task: bool,
787 #[serde(alias = "banner-path")]
792 pub banner_path: Option<PathBuf>,
793 #[serde(alias = "dialog-image-path")]
798 pub dialog_image_path: Option<PathBuf>,
799 #[serde(default, alias = "fips-compliant")]
802 pub fips_compliant: bool,
803}
804
805#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
809#[cfg_attr(feature = "schema", derive(JsonSchema))]
810#[serde(rename_all = "camelCase", deny_unknown_fields)]
811pub enum NsisCompression {
812 Zlib,
814 Bzip2,
816 Lzma,
818 None,
820}
821
822impl Default for NsisCompression {
823 fn default() -> Self {
824 Self::Lzma
825 }
826}
827
828#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
830#[serde(rename_all = "camelCase", deny_unknown_fields)]
831#[cfg_attr(feature = "schema", derive(JsonSchema))]
832pub enum NSISInstallerMode {
833 CurrentUser,
839 PerMachine,
844 Both,
850}
851
852impl Default for NSISInstallerMode {
853 fn default() -> Self {
854 Self::CurrentUser
855 }
856}
857
858#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
860#[cfg_attr(feature = "schema", derive(JsonSchema))]
861#[serde(rename_all = "camelCase", deny_unknown_fields)]
862pub struct NsisConfig {
863 pub template: Option<PathBuf>,
865 #[serde(alias = "header-image")]
869 pub header_image: Option<PathBuf>,
870 #[serde(alias = "sidebar-image")]
874 pub sidebar_image: Option<PathBuf>,
875 #[serde(alias = "install-icon")]
877 pub installer_icon: Option<PathBuf>,
878 #[serde(default, alias = "install-mode")]
880 pub install_mode: NSISInstallerMode,
881 pub languages: Option<Vec<String>>,
887 pub custom_language_files: Option<HashMap<String, PathBuf>>,
894 #[serde(default, alias = "display-language-selector")]
897 pub display_language_selector: bool,
898 #[serde(default)]
902 pub compression: NsisCompression,
903 #[serde(alias = "start-menu-folder")]
912 pub start_menu_folder: Option<String>,
913 #[serde(alias = "installer-hooks")]
943 pub installer_hooks: Option<PathBuf>,
944 #[serde(alias = "minimum-webview2-version")]
948 pub minimum_webview2_version: Option<String>,
949}
950
951#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
956#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
957#[cfg_attr(feature = "schema", derive(JsonSchema))]
958pub enum WebviewInstallMode {
959 Skip,
961 DownloadBootstrapper {
965 #[serde(default = "default_true")]
967 silent: bool,
968 },
969 EmbedBootstrapper {
973 #[serde(default = "default_true")]
975 silent: bool,
976 },
977 OfflineInstaller {
981 #[serde(default = "default_true")]
983 silent: bool,
984 },
985 FixedRuntime {
988 path: PathBuf,
993 },
994}
995
996impl Default for WebviewInstallMode {
997 fn default() -> Self {
998 Self::DownloadBootstrapper { silent: true }
999 }
1000}
1001
1002#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1004#[cfg_attr(feature = "schema", derive(JsonSchema))]
1005#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1006pub enum CustomSignCommandConfig {
1007 Command(String),
1016 CommandWithOptions {
1021 cmd: String,
1023 args: Vec<String>,
1027 },
1028}
1029
1030#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1034#[cfg_attr(feature = "schema", derive(JsonSchema))]
1035#[serde(rename_all = "camelCase", deny_unknown_fields)]
1036pub struct WindowsConfig {
1037 #[serde(alias = "digest-algorithm")]
1040 pub digest_algorithm: Option<String>,
1041 #[serde(alias = "certificate-thumbprint")]
1043 pub certificate_thumbprint: Option<String>,
1044 #[serde(alias = "timestamp-url")]
1046 pub timestamp_url: Option<String>,
1047 #[serde(default)]
1050 pub tsp: bool,
1051 #[serde(default, alias = "webview-install-mode")]
1053 pub webview_install_mode: WebviewInstallMode,
1054 #[serde(default = "default_true", alias = "allow-downgrades")]
1060 pub allow_downgrades: bool,
1061 pub wix: Option<WixConfig>,
1063 pub nsis: Option<NsisConfig>,
1065 #[serde(alias = "sign-command")]
1073 pub sign_command: Option<CustomSignCommandConfig>,
1074}
1075
1076impl Default for WindowsConfig {
1077 fn default() -> Self {
1078 Self {
1079 digest_algorithm: None,
1080 certificate_thumbprint: None,
1081 timestamp_url: None,
1082 tsp: false,
1083 webview_install_mode: Default::default(),
1084 allow_downgrades: true,
1085 wix: None,
1086 nsis: None,
1087 sign_command: None,
1088 }
1089 }
1090}
1091
1092#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1094#[cfg_attr(feature = "schema", derive(JsonSchema))]
1095pub enum BundleTypeRole {
1096 #[default]
1098 Editor,
1099 Viewer,
1101 Shell,
1103 QLGenerator,
1105 None,
1107}
1108
1109impl Display for BundleTypeRole {
1110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1111 match self {
1112 Self::Editor => write!(f, "Editor"),
1113 Self::Viewer => write!(f, "Viewer"),
1114 Self::Shell => write!(f, "Shell"),
1115 Self::QLGenerator => write!(f, "QLGenerator"),
1116 Self::None => write!(f, "None"),
1117 }
1118 }
1119}
1120
1121#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1125#[cfg_attr(feature = "schema", derive(JsonSchema))]
1126pub enum HandlerRank {
1127 #[default]
1129 Default,
1130 Owner,
1132 Alternate,
1134 None,
1136}
1137
1138impl Display for HandlerRank {
1139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1140 match self {
1141 Self::Default => write!(f, "Default"),
1142 Self::Owner => write!(f, "Owner"),
1143 Self::Alternate => write!(f, "Alternate"),
1144 Self::None => write!(f, "None"),
1145 }
1146 }
1147}
1148
1149#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
1153#[cfg_attr(feature = "schema", derive(JsonSchema))]
1154pub struct AssociationExt(pub String);
1155
1156impl fmt::Display for AssociationExt {
1157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1158 write!(f, "{}", self.0)
1159 }
1160}
1161
1162impl<'d> serde::Deserialize<'d> for AssociationExt {
1163 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
1164 let ext = String::deserialize(deserializer)?;
1165 if let Some(ext) = ext.strip_prefix('.') {
1166 Ok(AssociationExt(ext.into()))
1167 } else {
1168 Ok(AssociationExt(ext))
1169 }
1170 }
1171}
1172
1173#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1175#[cfg_attr(feature = "schema", derive(JsonSchema))]
1176#[serde(rename_all = "camelCase", deny_unknown_fields)]
1177pub struct FileAssociation {
1178 pub ext: Vec<AssociationExt>,
1180 #[serde(alias = "content-types")]
1185 pub content_types: Option<Vec<String>>,
1186 pub name: Option<String>,
1188 pub description: Option<String>,
1190 #[serde(default)]
1192 pub role: BundleTypeRole,
1193 #[serde(alias = "mime-type")]
1195 pub mime_type: Option<String>,
1196 #[serde(default)]
1198 pub rank: HandlerRank,
1199 pub exported_type: Option<ExportedFileAssociation>,
1203}
1204
1205#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1207#[cfg_attr(feature = "schema", derive(JsonSchema))]
1208#[serde(rename_all = "camelCase", deny_unknown_fields)]
1209pub struct ExportedFileAssociation {
1210 pub identifier: String,
1212 #[serde(alias = "conforms-to")]
1216 pub conforms_to: Option<Vec<String>>,
1217}
1218
1219#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1221#[cfg_attr(feature = "schema", derive(JsonSchema))]
1222#[serde(rename_all = "camelCase", deny_unknown_fields)]
1223pub struct DeepLinkProtocol {
1224 #[serde(default)]
1226 pub schemes: Vec<String>,
1227 #[serde(default)]
1235 pub domains: Vec<String>,
1236 pub name: Option<String>,
1238 #[serde(default)]
1240 pub role: BundleTypeRole,
1241}
1242
1243#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1246#[cfg_attr(feature = "schema", derive(JsonSchema))]
1247#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1248pub enum BundleResources {
1249 List(Vec<String>),
1251 Map(HashMap<String, String>),
1253}
1254
1255impl BundleResources {
1256 pub fn push(&mut self, path: impl Into<String>) {
1258 match self {
1259 Self::List(l) => l.push(path.into()),
1260 Self::Map(l) => {
1261 let path = path.into();
1262 l.insert(path.clone(), path);
1263 }
1264 }
1265 }
1266}
1267
1268#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1270#[cfg_attr(feature = "schema", derive(JsonSchema))]
1271#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1272pub enum Updater {
1273 String(V1Compatible),
1275 Bool(bool),
1278}
1279
1280impl Default for Updater {
1281 fn default() -> Self {
1282 Self::Bool(false)
1283 }
1284}
1285
1286#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1288#[cfg_attr(feature = "schema", derive(JsonSchema))]
1289#[serde(rename_all = "camelCase", deny_unknown_fields)]
1290pub enum V1Compatible {
1291 V1Compatible,
1293}
1294
1295#[skip_serializing_none]
1299#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1300#[cfg_attr(feature = "schema", derive(JsonSchema))]
1301#[serde(rename_all = "camelCase", deny_unknown_fields)]
1302pub struct BundleConfig {
1303 #[serde(default)]
1305 pub active: bool,
1306 #[serde(default)]
1308 pub targets: BundleTarget,
1309 #[serde(default)]
1310 pub create_updater_artifacts: Updater,
1312 pub publisher: Option<String>,
1317 pub homepage: Option<String>,
1322 #[serde(default)]
1324 pub icon: Vec<String>,
1325 pub resources: Option<BundleResources>,
1370 pub copyright: Option<String>,
1372 pub license: Option<String>,
1375 #[serde(alias = "license-file")]
1377 pub license_file: Option<PathBuf>,
1378 pub category: Option<String>,
1383 pub file_associations: Option<Vec<FileAssociation>>,
1385 #[serde(alias = "short-description")]
1387 pub short_description: Option<String>,
1388 #[serde(alias = "long-description")]
1390 pub long_description: Option<String>,
1391 #[serde(default, alias = "use-local-tools-dir")]
1399 pub use_local_tools_dir: bool,
1400 #[serde(alias = "external-bin")]
1412 pub external_bin: Option<Vec<String>>,
1413 #[serde(default)]
1415 pub windows: WindowsConfig,
1416 #[serde(default)]
1418 pub linux: LinuxConfig,
1419 #[serde(rename = "macOS", alias = "macos", default)]
1421 pub macos: MacConfig,
1422 #[serde(rename = "iOS", alias = "ios", default)]
1424 pub ios: IosConfig,
1425 #[serde(default)]
1427 pub android: AndroidConfig,
1428}
1429
1430#[derive(Debug, PartialEq, Eq, Serialize, Default, Clone, Copy)]
1432#[serde(rename_all = "camelCase", deny_unknown_fields)]
1433pub struct Color(pub u8, pub u8, pub u8, pub u8);
1434
1435impl From<Color> for (u8, u8, u8, u8) {
1436 fn from(value: Color) -> Self {
1437 (value.0, value.1, value.2, value.3)
1438 }
1439}
1440
1441impl From<Color> for (u8, u8, u8) {
1442 fn from(value: Color) -> Self {
1443 (value.0, value.1, value.2)
1444 }
1445}
1446
1447impl From<(u8, u8, u8, u8)> for Color {
1448 fn from(value: (u8, u8, u8, u8)) -> Self {
1449 Color(value.0, value.1, value.2, value.3)
1450 }
1451}
1452
1453impl From<(u8, u8, u8)> for Color {
1454 fn from(value: (u8, u8, u8)) -> Self {
1455 Color(value.0, value.1, value.2, 255)
1456 }
1457}
1458
1459impl From<Color> for [u8; 4] {
1460 fn from(value: Color) -> Self {
1461 [value.0, value.1, value.2, value.3]
1462 }
1463}
1464
1465impl From<Color> for [u8; 3] {
1466 fn from(value: Color) -> Self {
1467 [value.0, value.1, value.2]
1468 }
1469}
1470
1471impl From<[u8; 4]> for Color {
1472 fn from(value: [u8; 4]) -> Self {
1473 Color(value[0], value[1], value[2], value[3])
1474 }
1475}
1476
1477impl From<[u8; 3]> for Color {
1478 fn from(value: [u8; 3]) -> Self {
1479 Color(value[0], value[1], value[2], 255)
1480 }
1481}
1482
1483impl FromStr for Color {
1484 type Err = String;
1485 fn from_str(mut color: &str) -> Result<Self, Self::Err> {
1486 color = color.trim().strip_prefix('#').unwrap_or(color);
1487 let color = match color.len() {
1488 3 => color.chars()
1490 .flat_map(|c| std::iter::repeat(c).take(2))
1491 .chain(std::iter::repeat('f').take(2))
1492 .collect(),
1493 6 => format!("{color}FF"),
1494 8 => color.to_string(),
1495 _ => return Err("Invalid hex color length, must be either 3, 6 or 8, for example: #fff, #ffffff, or #ffffffff".into()),
1496 };
1497
1498 let r = u8::from_str_radix(&color[0..2], 16).map_err(|e| e.to_string())?;
1499 let g = u8::from_str_radix(&color[2..4], 16).map_err(|e| e.to_string())?;
1500 let b = u8::from_str_radix(&color[4..6], 16).map_err(|e| e.to_string())?;
1501 let a = u8::from_str_radix(&color[6..8], 16).map_err(|e| e.to_string())?;
1502
1503 Ok(Color(r, g, b, a))
1504 }
1505}
1506
1507fn default_alpha() -> u8 {
1508 255
1509}
1510
1511#[derive(Deserialize)]
1512#[cfg_attr(feature = "schema", derive(JsonSchema))]
1513#[serde(untagged)]
1514enum InnerColor {
1515 String(String),
1517 Rgb((u8, u8, u8)),
1519 Rgba((u8, u8, u8, u8)),
1521 RgbaObject {
1523 red: u8,
1524 green: u8,
1525 blue: u8,
1526 #[serde(default = "default_alpha")]
1527 alpha: u8,
1528 },
1529}
1530
1531impl<'de> Deserialize<'de> for Color {
1532 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1533 where
1534 D: Deserializer<'de>,
1535 {
1536 let color = InnerColor::deserialize(deserializer)?;
1537 let color = match color {
1538 InnerColor::String(string) => string.parse().map_err(serde::de::Error::custom)?,
1539 InnerColor::Rgb(rgb) => Color(rgb.0, rgb.1, rgb.2, 255),
1540 InnerColor::Rgba(rgb) => rgb.into(),
1541 InnerColor::RgbaObject {
1542 red,
1543 green,
1544 blue,
1545 alpha,
1546 } => Color(red, green, blue, alpha),
1547 };
1548
1549 Ok(color)
1550 }
1551}
1552
1553#[cfg(feature = "schema")]
1554impl schemars::JsonSchema for Color {
1555 fn schema_name() -> String {
1556 "Color".to_string()
1557 }
1558
1559 fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1560 let mut schema = schemars::schema_for!(InnerColor).schema;
1561 schema.metadata = None; let any_of = schema.subschemas().any_of.as_mut().unwrap();
1565 let schemars::schema::Schema::Object(str_schema) = any_of.first_mut().unwrap() else {
1566 unreachable!()
1567 };
1568 str_schema.string().pattern = Some("^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$".into());
1569
1570 schema.into()
1571 }
1572}
1573
1574#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1576#[cfg_attr(feature = "schema", derive(JsonSchema))]
1577#[serde(rename_all = "camelCase", deny_unknown_fields)]
1578pub enum BackgroundThrottlingPolicy {
1579 Disabled,
1581 Suspend,
1583 Throttle,
1585}
1586
1587#[skip_serializing_none]
1589#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1590#[cfg_attr(feature = "schema", derive(JsonSchema))]
1591#[serde(rename_all = "camelCase", deny_unknown_fields)]
1592pub struct WindowEffectsConfig {
1593 pub effects: Vec<WindowEffect>,
1596 pub state: Option<WindowEffectState>,
1598 pub radius: Option<f64>,
1600 pub color: Option<Color>,
1603}
1604
1605#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1608#[cfg_attr(feature = "schema", derive(JsonSchema))]
1609#[serde(rename_all = "camelCase", deny_unknown_fields)]
1610pub struct PreventOverflowMargin {
1611 pub width: u32,
1613 pub height: u32,
1615}
1616
1617#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1619#[cfg_attr(feature = "schema", derive(JsonSchema))]
1620#[serde(untagged)]
1621pub enum PreventOverflowConfig {
1622 Enable(bool),
1624 Margin(PreventOverflowMargin),
1627}
1628
1629#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
1635#[cfg_attr(feature = "schema", derive(JsonSchema))]
1636#[serde(rename_all = "camelCase", deny_unknown_fields)]
1637#[non_exhaustive]
1638pub enum ScrollBarStyle {
1639 #[default]
1640 Default,
1642
1643 FluentOverlay,
1648}
1649
1650#[skip_serializing_none]
1654#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
1655#[cfg_attr(feature = "schema", derive(JsonSchema))]
1656#[serde(rename_all = "camelCase", deny_unknown_fields)]
1657pub struct WindowConfig {
1658 #[serde(default = "default_window_label")]
1660 pub label: String,
1661 #[serde(default = "default_true")]
1676 pub create: bool,
1677 #[serde(default)]
1679 pub url: WebviewUrl,
1680 #[serde(alias = "user-agent")]
1682 pub user_agent: Option<String>,
1683 #[serde(default = "default_true", alias = "drag-drop-enabled")]
1687 pub drag_drop_enabled: bool,
1688 #[serde(default)]
1690 pub center: bool,
1691 pub x: Option<f64>,
1693 pub y: Option<f64>,
1695 #[serde(default = "default_width")]
1697 pub width: f64,
1698 #[serde(default = "default_height")]
1700 pub height: f64,
1701 #[serde(alias = "min-width")]
1703 pub min_width: Option<f64>,
1704 #[serde(alias = "min-height")]
1706 pub min_height: Option<f64>,
1707 #[serde(alias = "max-width")]
1709 pub max_width: Option<f64>,
1710 #[serde(alias = "max-height")]
1712 pub max_height: Option<f64>,
1713 #[serde(alias = "prevent-overflow")]
1719 pub prevent_overflow: Option<PreventOverflowConfig>,
1720 #[serde(default = "default_true")]
1722 pub resizable: bool,
1723 #[serde(default = "default_true")]
1731 pub maximizable: bool,
1732 #[serde(default = "default_true")]
1738 pub minimizable: bool,
1739 #[serde(default = "default_true")]
1747 pub closable: bool,
1748 #[serde(default = "default_title")]
1750 pub title: String,
1751 #[serde(default)]
1753 pub fullscreen: bool,
1754 #[serde(default = "default_true")]
1756 pub focus: bool,
1757 #[serde(default = "default_true")]
1759 pub focusable: bool,
1760 #[serde(default)]
1765 pub transparent: bool,
1766 #[serde(default)]
1768 pub maximized: bool,
1769 #[serde(default = "default_true")]
1771 pub visible: bool,
1772 #[serde(default = "default_true")]
1774 pub decorations: bool,
1775 #[serde(default, alias = "always-on-bottom")]
1777 pub always_on_bottom: bool,
1778 #[serde(default, alias = "always-on-top")]
1780 pub always_on_top: bool,
1781 #[serde(default, alias = "visible-on-all-workspaces")]
1787 pub visible_on_all_workspaces: bool,
1788 #[serde(default, alias = "content-protected")]
1790 pub content_protected: bool,
1791 #[serde(default, alias = "skip-taskbar")]
1793 pub skip_taskbar: bool,
1794 pub window_classname: Option<String>,
1796 pub theme: Option<crate::Theme>,
1798 #[serde(default, alias = "title-bar-style")]
1800 pub title_bar_style: TitleBarStyle,
1801 #[serde(default, alias = "traffic-light-position")]
1805 pub traffic_light_position: Option<LogicalPosition>,
1806 #[serde(default, alias = "hidden-title")]
1808 pub hidden_title: bool,
1809 #[serde(default, alias = "accept-first-mouse")]
1811 pub accept_first_mouse: bool,
1812 #[serde(default, alias = "tabbing-identifier")]
1819 pub tabbing_identifier: Option<String>,
1820 #[serde(default, alias = "additional-browser-args")]
1823 pub additional_browser_args: Option<String>,
1824 #[serde(default = "default_true")]
1834 pub shadow: bool,
1835 #[serde(default, alias = "window-effects")]
1844 pub window_effects: Option<WindowEffectsConfig>,
1845 #[serde(default)]
1851 pub incognito: bool,
1852 pub parent: Option<String>,
1864 #[serde(alias = "proxy-url")]
1872 pub proxy_url: Option<Url>,
1873 #[serde(default, alias = "zoom-hotkeys-enabled")]
1883 pub zoom_hotkeys_enabled: bool,
1884 #[serde(default, alias = "browser-extensions-enabled")]
1891 pub browser_extensions_enabled: bool,
1892
1893 #[serde(default, alias = "use-https-scheme")]
1903 pub use_https_scheme: bool,
1904 pub devtools: Option<bool>,
1914
1915 #[serde(alias = "background-color")]
1923 pub background_color: Option<Color>,
1924
1925 #[serde(default, alias = "background-throttling")]
1940 pub background_throttling: Option<BackgroundThrottlingPolicy>,
1941 #[serde(default, alias = "javascript-disabled")]
1943 pub javascript_disabled: bool,
1944 #[serde(default = "default_true", alias = "allow-link-preview")]
1947 pub allow_link_preview: bool,
1948 #[serde(
1953 default,
1954 alias = "disable-input-accessory-view",
1955 alias = "disable_input_accessory_view"
1956 )]
1957 pub disable_input_accessory_view: bool,
1958 #[serde(default, alias = "data-directory")]
1969 pub data_directory: Option<PathBuf>,
1970 #[serde(default, alias = "data-store-identifier")]
1982 pub data_store_identifier: Option<[u8; 16]>,
1983
1984 #[serde(default, alias = "scroll-bar-style")]
1997 pub scroll_bar_style: ScrollBarStyle,
1998}
1999
2000impl Default for WindowConfig {
2001 fn default() -> Self {
2002 Self {
2003 label: default_window_label(),
2004 url: WebviewUrl::default(),
2005 create: true,
2006 user_agent: None,
2007 drag_drop_enabled: true,
2008 center: false,
2009 x: None,
2010 y: None,
2011 width: default_width(),
2012 height: default_height(),
2013 min_width: None,
2014 min_height: None,
2015 max_width: None,
2016 max_height: None,
2017 prevent_overflow: None,
2018 resizable: true,
2019 maximizable: true,
2020 minimizable: true,
2021 closable: true,
2022 title: default_title(),
2023 fullscreen: false,
2024 focus: false,
2025 focusable: true,
2026 transparent: false,
2027 maximized: false,
2028 visible: true,
2029 decorations: true,
2030 always_on_bottom: false,
2031 always_on_top: false,
2032 visible_on_all_workspaces: false,
2033 content_protected: false,
2034 skip_taskbar: false,
2035 window_classname: None,
2036 theme: None,
2037 title_bar_style: Default::default(),
2038 traffic_light_position: None,
2039 hidden_title: false,
2040 accept_first_mouse: false,
2041 tabbing_identifier: None,
2042 additional_browser_args: None,
2043 shadow: true,
2044 window_effects: None,
2045 incognito: false,
2046 parent: None,
2047 proxy_url: None,
2048 zoom_hotkeys_enabled: false,
2049 browser_extensions_enabled: false,
2050 use_https_scheme: false,
2051 devtools: None,
2052 background_color: None,
2053 background_throttling: None,
2054 javascript_disabled: false,
2055 allow_link_preview: true,
2056 disable_input_accessory_view: false,
2057 data_directory: None,
2058 data_store_identifier: None,
2059 scroll_bar_style: ScrollBarStyle::Default,
2060 }
2061 }
2062}
2063
2064fn default_window_label() -> String {
2065 "main".to_string()
2066}
2067
2068fn default_width() -> f64 {
2069 800f64
2070}
2071
2072fn default_height() -> f64 {
2073 600f64
2074}
2075
2076fn default_title() -> String {
2077 "Tauri App".to_string()
2078}
2079
2080#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2083#[cfg_attr(feature = "schema", derive(JsonSchema))]
2084#[serde(rename_all = "camelCase", untagged)]
2085pub enum CspDirectiveSources {
2086 Inline(String),
2088 List(Vec<String>),
2090}
2091
2092impl Default for CspDirectiveSources {
2093 fn default() -> Self {
2094 Self::List(Vec::new())
2095 }
2096}
2097
2098impl From<CspDirectiveSources> for Vec<String> {
2099 fn from(sources: CspDirectiveSources) -> Self {
2100 match sources {
2101 CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
2102 CspDirectiveSources::List(l) => l,
2103 }
2104 }
2105}
2106
2107impl CspDirectiveSources {
2108 pub fn contains(&self, source: &str) -> bool {
2110 match self {
2111 Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
2112 Self::List(l) => l.contains(&source.into()),
2113 }
2114 }
2115
2116 pub fn push<S: AsRef<str>>(&mut self, source: S) {
2118 match self {
2119 Self::Inline(s) => {
2120 s.push(' ');
2121 s.push_str(source.as_ref());
2122 }
2123 Self::List(l) => {
2124 l.push(source.as_ref().to_string());
2125 }
2126 }
2127 }
2128
2129 pub fn extend(&mut self, sources: Vec<String>) {
2131 for s in sources {
2132 self.push(s);
2133 }
2134 }
2135}
2136
2137#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2140#[cfg_attr(feature = "schema", derive(JsonSchema))]
2141#[serde(rename_all = "camelCase", untagged)]
2142pub enum Csp {
2143 Policy(String),
2145 DirectiveMap(HashMap<String, CspDirectiveSources>),
2147}
2148
2149impl From<HashMap<String, CspDirectiveSources>> for Csp {
2150 fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
2151 Self::DirectiveMap(map)
2152 }
2153}
2154
2155impl From<Csp> for HashMap<String, CspDirectiveSources> {
2156 fn from(csp: Csp) -> Self {
2157 match csp {
2158 Csp::Policy(policy) => {
2159 let mut map = HashMap::new();
2160 for directive in policy.split(';') {
2161 let mut tokens = directive.trim().split(' ');
2162 if let Some(directive) = tokens.next() {
2163 let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
2164 map.insert(directive.to_string(), CspDirectiveSources::List(sources));
2165 }
2166 }
2167 map
2168 }
2169 Csp::DirectiveMap(m) => m,
2170 }
2171 }
2172}
2173
2174impl Display for Csp {
2175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2176 match self {
2177 Self::Policy(s) => write!(f, "{s}"),
2178 Self::DirectiveMap(m) => {
2179 let len = m.len();
2180 let mut i = 0;
2181 for (directive, sources) in m {
2182 let sources: Vec<String> = sources.clone().into();
2183 write!(f, "{} {}", directive, sources.join(" "))?;
2184 i += 1;
2185 if i != len {
2186 write!(f, "; ")?;
2187 }
2188 }
2189 Ok(())
2190 }
2191 }
2192 }
2193}
2194
2195#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2197#[serde(untagged)]
2198#[cfg_attr(feature = "schema", derive(JsonSchema))]
2199pub enum DisabledCspModificationKind {
2200 Flag(bool),
2203 List(Vec<String>),
2205}
2206
2207impl DisabledCspModificationKind {
2208 pub fn can_modify(&self, directive: &str) -> bool {
2210 match self {
2211 Self::Flag(f) => !f,
2212 Self::List(l) => !l.contains(&directive.into()),
2213 }
2214 }
2215}
2216
2217impl Default for DisabledCspModificationKind {
2218 fn default() -> Self {
2219 Self::Flag(false)
2220 }
2221}
2222
2223#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2232#[serde(untagged)]
2233#[cfg_attr(feature = "schema", derive(JsonSchema))]
2234pub enum FsScope {
2235 AllowedPaths(Vec<PathBuf>),
2237 #[serde(rename_all = "camelCase")]
2239 Scope {
2240 #[serde(default)]
2242 allow: Vec<PathBuf>,
2243 #[serde(default)]
2246 deny: Vec<PathBuf>,
2247 #[serde(alias = "require-literal-leading-dot")]
2256 require_literal_leading_dot: Option<bool>,
2257 },
2258}
2259
2260impl Default for FsScope {
2261 fn default() -> Self {
2262 Self::AllowedPaths(Vec::new())
2263 }
2264}
2265
2266impl FsScope {
2267 pub fn allowed_paths(&self) -> &Vec<PathBuf> {
2269 match self {
2270 Self::AllowedPaths(p) => p,
2271 Self::Scope { allow, .. } => allow,
2272 }
2273 }
2274
2275 pub fn forbidden_paths(&self) -> Option<&Vec<PathBuf>> {
2277 match self {
2278 Self::AllowedPaths(_) => None,
2279 Self::Scope { deny, .. } => Some(deny),
2280 }
2281 }
2282}
2283
2284#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2288#[cfg_attr(feature = "schema", derive(JsonSchema))]
2289#[serde(rename_all = "camelCase", deny_unknown_fields)]
2290pub struct AssetProtocolConfig {
2291 #[serde(default)]
2293 pub scope: FsScope,
2294 #[serde(default)]
2296 pub enable: bool,
2297}
2298
2299#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2303#[cfg_attr(feature = "schema", derive(JsonSchema))]
2304#[serde(rename_all = "camelCase", untagged)]
2305pub enum HeaderSource {
2306 Inline(String),
2308 List(Vec<String>),
2310 Map(HashMap<String, String>),
2312}
2313
2314impl Display for HeaderSource {
2315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2316 match self {
2317 Self::Inline(s) => write!(f, "{s}"),
2318 Self::List(l) => write!(f, "{}", l.join(", ")),
2319 Self::Map(m) => {
2320 let len = m.len();
2321 let mut i = 0;
2322 for (key, value) in m {
2323 write!(f, "{key} {value}")?;
2324 i += 1;
2325 if i != len {
2326 write!(f, "; ")?;
2327 }
2328 }
2329 Ok(())
2330 }
2331 }
2332 }
2333}
2334
2335pub trait HeaderAddition {
2339 fn add_configured_headers(self, headers: Option<&HeaderConfig>) -> http::response::Builder;
2341}
2342
2343impl HeaderAddition for Builder {
2344 fn add_configured_headers(mut self, headers: Option<&HeaderConfig>) -> http::response::Builder {
2348 if let Some(headers) = headers {
2349 if let Some(value) = &headers.access_control_allow_credentials {
2351 self = self.header("Access-Control-Allow-Credentials", value.to_string());
2352 };
2353
2354 if let Some(value) = &headers.access_control_allow_headers {
2356 self = self.header("Access-Control-Allow-Headers", value.to_string());
2357 };
2358
2359 if let Some(value) = &headers.access_control_allow_methods {
2361 self = self.header("Access-Control-Allow-Methods", value.to_string());
2362 };
2363
2364 if let Some(value) = &headers.access_control_expose_headers {
2366 self = self.header("Access-Control-Expose-Headers", value.to_string());
2367 };
2368
2369 if let Some(value) = &headers.access_control_max_age {
2371 self = self.header("Access-Control-Max-Age", value.to_string());
2372 };
2373
2374 if let Some(value) = &headers.cross_origin_embedder_policy {
2376 self = self.header("Cross-Origin-Embedder-Policy", value.to_string());
2377 };
2378
2379 if let Some(value) = &headers.cross_origin_opener_policy {
2381 self = self.header("Cross-Origin-Opener-Policy", value.to_string());
2382 };
2383
2384 if let Some(value) = &headers.cross_origin_resource_policy {
2386 self = self.header("Cross-Origin-Resource-Policy", value.to_string());
2387 };
2388
2389 if let Some(value) = &headers.permissions_policy {
2391 self = self.header("Permission-Policy", value.to_string());
2392 };
2393
2394 if let Some(value) = &headers.service_worker_allowed {
2395 self = self.header("Service-Worker-Allowed", value.to_string());
2396 }
2397
2398 if let Some(value) = &headers.timing_allow_origin {
2400 self = self.header("Timing-Allow-Origin", value.to_string());
2401 };
2402
2403 if let Some(value) = &headers.x_content_type_options {
2405 self = self.header("X-Content-Type-Options", value.to_string());
2406 };
2407
2408 if let Some(value) = &headers.tauri_custom_header {
2410 self = self.header("Tauri-Custom-Header", value.to_string());
2412 };
2413 }
2414 self
2415 }
2416}
2417
2418#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2470#[cfg_attr(feature = "schema", derive(JsonSchema))]
2471#[serde(deny_unknown_fields)]
2472pub struct HeaderConfig {
2473 #[serde(rename = "Access-Control-Allow-Credentials")]
2478 pub access_control_allow_credentials: Option<HeaderSource>,
2479 #[serde(rename = "Access-Control-Allow-Headers")]
2487 pub access_control_allow_headers: Option<HeaderSource>,
2488 #[serde(rename = "Access-Control-Allow-Methods")]
2493 pub access_control_allow_methods: Option<HeaderSource>,
2494 #[serde(rename = "Access-Control-Expose-Headers")]
2500 pub access_control_expose_headers: Option<HeaderSource>,
2501 #[serde(rename = "Access-Control-Max-Age")]
2508 pub access_control_max_age: Option<HeaderSource>,
2509 #[serde(rename = "Cross-Origin-Embedder-Policy")]
2514 pub cross_origin_embedder_policy: Option<HeaderSource>,
2515 #[serde(rename = "Cross-Origin-Opener-Policy")]
2522 pub cross_origin_opener_policy: Option<HeaderSource>,
2523 #[serde(rename = "Cross-Origin-Resource-Policy")]
2528 pub cross_origin_resource_policy: Option<HeaderSource>,
2529 #[serde(rename = "Permissions-Policy")]
2534 pub permissions_policy: Option<HeaderSource>,
2535 #[serde(rename = "Service-Worker-Allowed")]
2545 pub service_worker_allowed: Option<HeaderSource>,
2546 #[serde(rename = "Timing-Allow-Origin")]
2552 pub timing_allow_origin: Option<HeaderSource>,
2553 #[serde(rename = "X-Content-Type-Options")]
2560 pub x_content_type_options: Option<HeaderSource>,
2561 #[serde(rename = "Tauri-Custom-Header")]
2566 pub tauri_custom_header: Option<HeaderSource>,
2567}
2568
2569impl HeaderConfig {
2570 pub fn new() -> Self {
2572 HeaderConfig {
2573 access_control_allow_credentials: None,
2574 access_control_allow_methods: None,
2575 access_control_allow_headers: None,
2576 access_control_expose_headers: None,
2577 access_control_max_age: None,
2578 cross_origin_embedder_policy: None,
2579 cross_origin_opener_policy: None,
2580 cross_origin_resource_policy: None,
2581 permissions_policy: None,
2582 service_worker_allowed: None,
2583 timing_allow_origin: None,
2584 x_content_type_options: None,
2585 tauri_custom_header: None,
2586 }
2587 }
2588}
2589
2590#[skip_serializing_none]
2594#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2595#[cfg_attr(feature = "schema", derive(JsonSchema))]
2596#[serde(rename_all = "camelCase", deny_unknown_fields)]
2597pub struct SecurityConfig {
2598 pub csp: Option<Csp>,
2604 #[serde(alias = "dev-csp")]
2609 pub dev_csp: Option<Csp>,
2610 #[serde(default, alias = "freeze-prototype")]
2612 pub freeze_prototype: bool,
2613 #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
2626 pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
2627 #[serde(default, alias = "asset-protocol")]
2629 pub asset_protocol: AssetProtocolConfig,
2630 #[serde(default)]
2632 pub pattern: PatternKind,
2633 #[serde(default)]
2656 pub capabilities: Vec<CapabilityEntry>,
2657 #[serde(default)]
2660 pub headers: Option<HeaderConfig>,
2661}
2662
2663#[derive(Debug, Clone, PartialEq, Serialize)]
2665#[cfg_attr(feature = "schema", derive(JsonSchema))]
2666#[serde(untagged)]
2667pub enum CapabilityEntry {
2668 Inlined(Capability),
2670 Reference(String),
2672}
2673
2674impl<'de> Deserialize<'de> for CapabilityEntry {
2675 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2676 where
2677 D: Deserializer<'de>,
2678 {
2679 UntaggedEnumVisitor::new()
2680 .string(|string| Ok(Self::Reference(string.to_owned())))
2681 .map(|map| map.deserialize::<Capability>().map(Self::Inlined))
2682 .deserialize(deserializer)
2683 }
2684}
2685
2686#[skip_serializing_none]
2688#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
2689#[serde(rename_all = "lowercase", tag = "use", content = "options")]
2690#[cfg_attr(feature = "schema", derive(JsonSchema))]
2691pub enum PatternKind {
2692 Brownfield,
2694 Isolation {
2696 dir: PathBuf,
2698 },
2699}
2700
2701impl Default for PatternKind {
2702 fn default() -> Self {
2703 Self::Brownfield
2704 }
2705}
2706
2707#[skip_serializing_none]
2711#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2712#[cfg_attr(feature = "schema", derive(JsonSchema))]
2713#[serde(rename_all = "camelCase", deny_unknown_fields)]
2714pub struct AppConfig {
2715 #[serde(default)]
2770 pub windows: Vec<WindowConfig>,
2771 #[serde(default)]
2773 pub security: SecurityConfig,
2774 #[serde(alias = "tray-icon")]
2776 pub tray_icon: Option<TrayIconConfig>,
2777 #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
2779 pub macos_private_api: bool,
2780 #[serde(default, alias = "with-global-tauri")]
2782 pub with_global_tauri: bool,
2783 #[serde(rename = "enableGTKAppId", alias = "enable-gtk-app-id", default)]
2785 pub enable_gtk_app_id: bool,
2786}
2787
2788impl AppConfig {
2789 pub fn all_features() -> Vec<&'static str> {
2791 vec![
2792 "tray-icon",
2793 "macos-private-api",
2794 "protocol-asset",
2795 "isolation",
2796 ]
2797 }
2798
2799 pub fn features(&self) -> Vec<&str> {
2801 let mut features = Vec::new();
2802 if self.tray_icon.is_some() {
2803 features.push("tray-icon");
2804 }
2805 if self.macos_private_api {
2806 features.push("macos-private-api");
2807 }
2808 if self.security.asset_protocol.enable {
2809 features.push("protocol-asset");
2810 }
2811
2812 if let PatternKind::Isolation { .. } = self.security.pattern {
2813 features.push("isolation");
2814 }
2815
2816 features.sort_unstable();
2817 features
2818 }
2819}
2820
2821#[skip_serializing_none]
2825#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2826#[cfg_attr(feature = "schema", derive(JsonSchema))]
2827#[serde(rename_all = "camelCase", deny_unknown_fields)]
2828pub struct TrayIconConfig {
2829 pub id: Option<String>,
2831 #[serde(alias = "icon-path")]
2837 pub icon_path: PathBuf,
2838 #[serde(default, alias = "icon-as-template")]
2840 pub icon_as_template: bool,
2841 #[serde(default = "default_true", alias = "menu-on-left-click")]
2847 #[deprecated(since = "2.2.0", note = "Use `show_menu_on_left_click` instead.")]
2848 pub menu_on_left_click: bool,
2849 #[serde(default = "default_true", alias = "show-menu-on-left-click")]
2855 pub show_menu_on_left_click: bool,
2856 pub title: Option<String>,
2858 pub tooltip: Option<String>,
2860}
2861
2862#[skip_serializing_none]
2864#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2865#[cfg_attr(feature = "schema", derive(JsonSchema))]
2866#[serde(rename_all = "camelCase", deny_unknown_fields)]
2867pub struct IosConfig {
2868 pub template: Option<PathBuf>,
2872 pub frameworks: Option<Vec<String>>,
2876 #[serde(alias = "development-team")]
2879 pub development_team: Option<String>,
2880 #[serde(alias = "bundle-version")]
2884 pub bundle_version: Option<String>,
2885 #[serde(
2889 alias = "minimum-system-version",
2890 default = "ios_minimum_system_version"
2891 )]
2892 pub minimum_system_version: String,
2893 #[serde(alias = "info-plist")]
2897 pub info_plist: Option<PathBuf>,
2898}
2899
2900impl Default for IosConfig {
2901 fn default() -> Self {
2902 Self {
2903 template: None,
2904 frameworks: None,
2905 development_team: None,
2906 bundle_version: None,
2907 minimum_system_version: ios_minimum_system_version(),
2908 info_plist: None,
2909 }
2910 }
2911}
2912
2913#[skip_serializing_none]
2915#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2916#[cfg_attr(feature = "schema", derive(JsonSchema))]
2917#[serde(rename_all = "camelCase", deny_unknown_fields)]
2918pub struct AndroidConfig {
2919 #[serde(alias = "min-sdk-version", default = "default_min_sdk_version")]
2922 pub min_sdk_version: u32,
2923
2924 #[serde(alias = "version-code")]
2930 #[cfg_attr(feature = "schema", validate(range(min = 1, max = 2_100_000_000)))]
2931 pub version_code: Option<u32>,
2932
2933 #[serde(alias = "auto-increment-version-code", default)]
2941 pub auto_increment_version_code: bool,
2942}
2943
2944impl Default for AndroidConfig {
2945 fn default() -> Self {
2946 Self {
2947 min_sdk_version: default_min_sdk_version(),
2948 version_code: None,
2949 auto_increment_version_code: false,
2950 }
2951 }
2952}
2953
2954fn default_min_sdk_version() -> u32 {
2955 24
2956}
2957
2958#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2960#[cfg_attr(feature = "schema", derive(JsonSchema))]
2961#[serde(untagged, deny_unknown_fields)]
2962#[non_exhaustive]
2963pub enum FrontendDist {
2964 Url(Url),
2966 Directory(PathBuf),
2968 Files(Vec<PathBuf>),
2970}
2971
2972impl std::fmt::Display for FrontendDist {
2973 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2974 match self {
2975 Self::Url(url) => write!(f, "{url}"),
2976 Self::Directory(p) => write!(f, "{}", p.display()),
2977 Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
2978 }
2979 }
2980}
2981
2982#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2984#[cfg_attr(feature = "schema", derive(JsonSchema))]
2985#[serde(rename_all = "camelCase", untagged)]
2986pub enum BeforeDevCommand {
2987 Script(String),
2989 ScriptWithOptions {
2991 script: String,
2993 cwd: Option<String>,
2995 #[serde(default)]
2997 wait: bool,
2998 },
2999}
3000
3001#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
3003#[cfg_attr(feature = "schema", derive(JsonSchema))]
3004#[serde(rename_all = "camelCase", untagged)]
3005pub enum HookCommand {
3006 Script(String),
3008 ScriptWithOptions {
3010 script: String,
3012 cwd: Option<String>,
3014 },
3015}
3016
3017#[skip_serializing_none]
3019#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3020#[cfg_attr(feature = "schema", derive(JsonSchema))]
3021#[serde(untagged)]
3022pub enum RunnerConfig {
3023 String(String),
3025 Object {
3027 cmd: String,
3029 cwd: Option<String>,
3031 args: Option<Vec<String>>,
3033 },
3034}
3035
3036impl Default for RunnerConfig {
3037 fn default() -> Self {
3038 RunnerConfig::String("cargo".to_string())
3039 }
3040}
3041
3042impl RunnerConfig {
3043 pub fn cmd(&self) -> &str {
3045 match self {
3046 RunnerConfig::String(cmd) => cmd,
3047 RunnerConfig::Object { cmd, .. } => cmd,
3048 }
3049 }
3050
3051 pub fn cwd(&self) -> Option<&str> {
3053 match self {
3054 RunnerConfig::String(_) => None,
3055 RunnerConfig::Object { cwd, .. } => cwd.as_deref(),
3056 }
3057 }
3058
3059 pub fn args(&self) -> Option<&[String]> {
3061 match self {
3062 RunnerConfig::String(_) => None,
3063 RunnerConfig::Object { args, .. } => args.as_deref(),
3064 }
3065 }
3066}
3067
3068impl std::str::FromStr for RunnerConfig {
3069 type Err = std::convert::Infallible;
3070
3071 fn from_str(s: &str) -> Result<Self, Self::Err> {
3072 Ok(RunnerConfig::String(s.to_string()))
3073 }
3074}
3075
3076impl From<&str> for RunnerConfig {
3077 fn from(s: &str) -> Self {
3078 RunnerConfig::String(s.to_string())
3079 }
3080}
3081
3082impl From<String> for RunnerConfig {
3083 fn from(s: String) -> Self {
3084 RunnerConfig::String(s)
3085 }
3086}
3087
3088#[skip_serializing_none]
3092#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
3093#[cfg_attr(feature = "schema", derive(JsonSchema))]
3094#[serde(rename_all = "camelCase", deny_unknown_fields)]
3095pub struct BuildConfig {
3096 pub runner: Option<RunnerConfig>,
3098 #[serde(alias = "dev-url")]
3106 pub dev_url: Option<Url>,
3107 #[serde(alias = "frontend-dist")]
3121 pub frontend_dist: Option<FrontendDist>,
3122 #[serde(alias = "before-dev-command")]
3126 pub before_dev_command: Option<BeforeDevCommand>,
3127 #[serde(alias = "before-build-command")]
3131 pub before_build_command: Option<HookCommand>,
3132 #[serde(alias = "before-bundle-command")]
3136 pub before_bundle_command: Option<HookCommand>,
3137 pub features: Option<Vec<String>>,
3139 #[serde(alias = "remove-unused-commands", default)]
3147 pub remove_unused_commands: bool,
3148 #[serde(alias = "additional-watch-directories", default)]
3150 pub additional_watch_folders: Vec<PathBuf>,
3151}
3152
3153#[derive(Debug, PartialEq, Eq)]
3154struct PackageVersion(String);
3155
3156impl<'d> serde::Deserialize<'d> for PackageVersion {
3157 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
3158 struct PackageVersionVisitor;
3159
3160 impl Visitor<'_> for PackageVersionVisitor {
3161 type Value = PackageVersion;
3162
3163 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
3164 write!(
3165 formatter,
3166 "a semver string or a path to a package.json file"
3167 )
3168 }
3169
3170 fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
3171 let path = PathBuf::from(value);
3172 if path.exists() {
3173 let json_str = read_to_string(&path)
3174 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
3175 let package_json: serde_json::Value = serde_json::from_str(&json_str)
3176 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
3177 if let Some(obj) = package_json.as_object() {
3178 let version = obj
3179 .get("version")
3180 .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
3181 .as_str()
3182 .ok_or_else(|| {
3183 DeError::custom(format!("`{} > version` must be a string", path.display()))
3184 })?;
3185 Ok(PackageVersion(
3186 Version::from_str(version)
3187 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
3188 .to_string(),
3189 ))
3190 } else {
3191 Err(DeError::custom(
3192 "`package > version` value is not a path to a JSON object",
3193 ))
3194 }
3195 } else {
3196 Ok(PackageVersion(
3197 Version::from_str(value)
3198 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
3199 .to_string(),
3200 ))
3201 }
3202 }
3203 }
3204
3205 deserializer.deserialize_string(PackageVersionVisitor {})
3206 }
3207}
3208
3209fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
3210where
3211 D: Deserializer<'de>,
3212{
3213 Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
3214}
3215
3216#[skip_serializing_none]
3282#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
3283#[cfg_attr(feature = "schema", derive(JsonSchema))]
3284#[serde(rename_all = "camelCase", deny_unknown_fields)]
3285pub struct Config {
3286 #[serde(rename = "$schema")]
3288 pub schema: Option<String>,
3289 #[serde(alias = "product-name")]
3291 #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
3292 pub product_name: Option<String>,
3293 #[serde(alias = "main-binary-name")]
3307 pub main_binary_name: Option<String>,
3308 #[serde(deserialize_with = "version_deserializer", default)]
3324 pub version: Option<String>,
3325 pub identifier: String,
3331 #[serde(default)]
3333 pub app: AppConfig,
3334 #[serde(default)]
3336 pub build: BuildConfig,
3337 #[serde(default)]
3339 pub bundle: BundleConfig,
3340 #[serde(default)]
3342 pub plugins: PluginConfig,
3343}
3344
3345#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
3349#[cfg_attr(feature = "schema", derive(JsonSchema))]
3350pub struct PluginConfig(pub HashMap<String, JsonValue>);
3351
3352#[cfg(feature = "build")]
3358mod build {
3359 use super::*;
3360 use crate::{literal_struct, tokens::*};
3361 use proc_macro2::TokenStream;
3362 use quote::{quote, ToTokens, TokenStreamExt};
3363 use std::convert::identity;
3364
3365 impl ToTokens for WebviewUrl {
3366 fn to_tokens(&self, tokens: &mut TokenStream) {
3367 let prefix = quote! { ::tauri::utils::config::WebviewUrl };
3368
3369 tokens.append_all(match self {
3370 Self::App(path) => {
3371 let path = path_buf_lit(path);
3372 quote! { #prefix::App(#path) }
3373 }
3374 Self::External(url) => {
3375 let url = url_lit(url);
3376 quote! { #prefix::External(#url) }
3377 }
3378 Self::CustomProtocol(url) => {
3379 let url = url_lit(url);
3380 quote! { #prefix::CustomProtocol(#url) }
3381 }
3382 })
3383 }
3384 }
3385
3386 impl ToTokens for BackgroundThrottlingPolicy {
3387 fn to_tokens(&self, tokens: &mut TokenStream) {
3388 let prefix = quote! { ::tauri::utils::config::BackgroundThrottlingPolicy };
3389 tokens.append_all(match self {
3390 Self::Disabled => quote! { #prefix::Disabled },
3391 Self::Throttle => quote! { #prefix::Throttle },
3392 Self::Suspend => quote! { #prefix::Suspend },
3393 })
3394 }
3395 }
3396
3397 impl ToTokens for crate::Theme {
3398 fn to_tokens(&self, tokens: &mut TokenStream) {
3399 let prefix = quote! { ::tauri::utils::Theme };
3400
3401 tokens.append_all(match self {
3402 Self::Light => quote! { #prefix::Light },
3403 Self::Dark => quote! { #prefix::Dark },
3404 })
3405 }
3406 }
3407
3408 impl ToTokens for Color {
3409 fn to_tokens(&self, tokens: &mut TokenStream) {
3410 let Color(r, g, b, a) = self;
3411 tokens.append_all(quote! {::tauri::utils::config::Color(#r,#g,#b,#a)});
3412 }
3413 }
3414 impl ToTokens for WindowEffectsConfig {
3415 fn to_tokens(&self, tokens: &mut TokenStream) {
3416 let effects = vec_lit(self.effects.clone(), |d| d);
3417 let state = opt_lit(self.state.as_ref());
3418 let radius = opt_lit(self.radius.as_ref());
3419 let color = opt_lit(self.color.as_ref());
3420
3421 literal_struct!(
3422 tokens,
3423 ::tauri::utils::config::WindowEffectsConfig,
3424 effects,
3425 state,
3426 radius,
3427 color
3428 )
3429 }
3430 }
3431
3432 impl ToTokens for crate::TitleBarStyle {
3433 fn to_tokens(&self, tokens: &mut TokenStream) {
3434 let prefix = quote! { ::tauri::utils::TitleBarStyle };
3435
3436 tokens.append_all(match self {
3437 Self::Visible => quote! { #prefix::Visible },
3438 Self::Transparent => quote! { #prefix::Transparent },
3439 Self::Overlay => quote! { #prefix::Overlay },
3440 })
3441 }
3442 }
3443
3444 impl ToTokens for LogicalPosition {
3445 fn to_tokens(&self, tokens: &mut TokenStream) {
3446 let LogicalPosition { x, y } = self;
3447 literal_struct!(tokens, ::tauri::utils::config::LogicalPosition, x, y)
3448 }
3449 }
3450
3451 impl ToTokens for crate::WindowEffect {
3452 fn to_tokens(&self, tokens: &mut TokenStream) {
3453 let prefix = quote! { ::tauri::utils::WindowEffect };
3454
3455 #[allow(deprecated)]
3456 tokens.append_all(match self {
3457 WindowEffect::AppearanceBased => quote! { #prefix::AppearanceBased},
3458 WindowEffect::Light => quote! { #prefix::Light},
3459 WindowEffect::Dark => quote! { #prefix::Dark},
3460 WindowEffect::MediumLight => quote! { #prefix::MediumLight},
3461 WindowEffect::UltraDark => quote! { #prefix::UltraDark},
3462 WindowEffect::Titlebar => quote! { #prefix::Titlebar},
3463 WindowEffect::Selection => quote! { #prefix::Selection},
3464 WindowEffect::Menu => quote! { #prefix::Menu},
3465 WindowEffect::Popover => quote! { #prefix::Popover},
3466 WindowEffect::Sidebar => quote! { #prefix::Sidebar},
3467 WindowEffect::HeaderView => quote! { #prefix::HeaderView},
3468 WindowEffect::Sheet => quote! { #prefix::Sheet},
3469 WindowEffect::WindowBackground => quote! { #prefix::WindowBackground},
3470 WindowEffect::HudWindow => quote! { #prefix::HudWindow},
3471 WindowEffect::FullScreenUI => quote! { #prefix::FullScreenUI},
3472 WindowEffect::Tooltip => quote! { #prefix::Tooltip},
3473 WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
3474 WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
3475 WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
3476 WindowEffect::Mica => quote! { #prefix::Mica},
3477 WindowEffect::MicaDark => quote! { #prefix::MicaDark},
3478 WindowEffect::MicaLight => quote! { #prefix::MicaLight},
3479 WindowEffect::Blur => quote! { #prefix::Blur},
3480 WindowEffect::Acrylic => quote! { #prefix::Acrylic},
3481 WindowEffect::Tabbed => quote! { #prefix::Tabbed },
3482 WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
3483 WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
3484 })
3485 }
3486 }
3487
3488 impl ToTokens for crate::WindowEffectState {
3489 fn to_tokens(&self, tokens: &mut TokenStream) {
3490 let prefix = quote! { ::tauri::utils::WindowEffectState };
3491
3492 #[allow(deprecated)]
3493 tokens.append_all(match self {
3494 WindowEffectState::Active => quote! { #prefix::Active},
3495 WindowEffectState::FollowsWindowActiveState => quote! { #prefix::FollowsWindowActiveState},
3496 WindowEffectState::Inactive => quote! { #prefix::Inactive},
3497 })
3498 }
3499 }
3500
3501 impl ToTokens for PreventOverflowMargin {
3502 fn to_tokens(&self, tokens: &mut TokenStream) {
3503 let width = self.width;
3504 let height = self.height;
3505
3506 literal_struct!(
3507 tokens,
3508 ::tauri::utils::config::PreventOverflowMargin,
3509 width,
3510 height
3511 )
3512 }
3513 }
3514
3515 impl ToTokens for PreventOverflowConfig {
3516 fn to_tokens(&self, tokens: &mut TokenStream) {
3517 let prefix = quote! { ::tauri::utils::config::PreventOverflowConfig };
3518
3519 #[allow(deprecated)]
3520 tokens.append_all(match self {
3521 Self::Enable(enable) => quote! { #prefix::Enable(#enable) },
3522 Self::Margin(margin) => quote! { #prefix::Margin(#margin) },
3523 })
3524 }
3525 }
3526
3527 impl ToTokens for ScrollBarStyle {
3528 fn to_tokens(&self, tokens: &mut TokenStream) {
3529 let prefix = quote! { ::tauri::utils::config::ScrollBarStyle };
3530
3531 tokens.append_all(match self {
3532 Self::Default => quote! { #prefix::Default },
3533 Self::FluentOverlay => quote! { #prefix::FluentOverlay },
3534 })
3535 }
3536 }
3537
3538 impl ToTokens for WindowConfig {
3539 fn to_tokens(&self, tokens: &mut TokenStream) {
3540 let label = str_lit(&self.label);
3541 let create = &self.create;
3542 let url = &self.url;
3543 let user_agent = opt_str_lit(self.user_agent.as_ref());
3544 let drag_drop_enabled = self.drag_drop_enabled;
3545 let center = self.center;
3546 let x = opt_lit(self.x.as_ref());
3547 let y = opt_lit(self.y.as_ref());
3548 let width = self.width;
3549 let height = self.height;
3550 let min_width = opt_lit(self.min_width.as_ref());
3551 let min_height = opt_lit(self.min_height.as_ref());
3552 let max_width = opt_lit(self.max_width.as_ref());
3553 let max_height = opt_lit(self.max_height.as_ref());
3554 let prevent_overflow = opt_lit(self.prevent_overflow.as_ref());
3555 let resizable = self.resizable;
3556 let maximizable = self.maximizable;
3557 let minimizable = self.minimizable;
3558 let closable = self.closable;
3559 let title = str_lit(&self.title);
3560 let proxy_url = opt_lit(self.proxy_url.as_ref().map(url_lit).as_ref());
3561 let fullscreen = self.fullscreen;
3562 let focus = self.focus;
3563 let focusable = self.focusable;
3564 let transparent = self.transparent;
3565 let maximized = self.maximized;
3566 let visible = self.visible;
3567 let decorations = self.decorations;
3568 let always_on_bottom = self.always_on_bottom;
3569 let always_on_top = self.always_on_top;
3570 let visible_on_all_workspaces = self.visible_on_all_workspaces;
3571 let content_protected = self.content_protected;
3572 let skip_taskbar = self.skip_taskbar;
3573 let window_classname = opt_str_lit(self.window_classname.as_ref());
3574 let theme = opt_lit(self.theme.as_ref());
3575 let title_bar_style = &self.title_bar_style;
3576 let traffic_light_position = opt_lit(self.traffic_light_position.as_ref());
3577 let hidden_title = self.hidden_title;
3578 let accept_first_mouse = self.accept_first_mouse;
3579 let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
3580 let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
3581 let shadow = self.shadow;
3582 let window_effects = opt_lit(self.window_effects.as_ref());
3583 let incognito = self.incognito;
3584 let parent = opt_str_lit(self.parent.as_ref());
3585 let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled;
3586 let browser_extensions_enabled = self.browser_extensions_enabled;
3587 let use_https_scheme = self.use_https_scheme;
3588 let devtools = opt_lit(self.devtools.as_ref());
3589 let background_color = opt_lit(self.background_color.as_ref());
3590 let background_throttling = opt_lit(self.background_throttling.as_ref());
3591 let javascript_disabled = self.javascript_disabled;
3592 let allow_link_preview = self.allow_link_preview;
3593 let disable_input_accessory_view = self.disable_input_accessory_view;
3594 let data_directory = opt_lit(self.data_directory.as_ref().map(path_buf_lit).as_ref());
3595 let data_store_identifier = opt_vec_lit(self.data_store_identifier, identity);
3596 let scroll_bar_style = &self.scroll_bar_style;
3597
3598 literal_struct!(
3599 tokens,
3600 ::tauri::utils::config::WindowConfig,
3601 label,
3602 url,
3603 create,
3604 user_agent,
3605 drag_drop_enabled,
3606 center,
3607 x,
3608 y,
3609 width,
3610 height,
3611 min_width,
3612 min_height,
3613 max_width,
3614 max_height,
3615 prevent_overflow,
3616 resizable,
3617 maximizable,
3618 minimizable,
3619 closable,
3620 title,
3621 proxy_url,
3622 fullscreen,
3623 focus,
3624 focusable,
3625 transparent,
3626 maximized,
3627 visible,
3628 decorations,
3629 always_on_bottom,
3630 always_on_top,
3631 visible_on_all_workspaces,
3632 content_protected,
3633 skip_taskbar,
3634 window_classname,
3635 theme,
3636 title_bar_style,
3637 traffic_light_position,
3638 hidden_title,
3639 accept_first_mouse,
3640 tabbing_identifier,
3641 additional_browser_args,
3642 shadow,
3643 window_effects,
3644 incognito,
3645 parent,
3646 zoom_hotkeys_enabled,
3647 browser_extensions_enabled,
3648 use_https_scheme,
3649 devtools,
3650 background_color,
3651 background_throttling,
3652 javascript_disabled,
3653 allow_link_preview,
3654 disable_input_accessory_view,
3655 data_directory,
3656 data_store_identifier,
3657 scroll_bar_style
3658 );
3659 }
3660 }
3661
3662 impl ToTokens for PatternKind {
3663 fn to_tokens(&self, tokens: &mut TokenStream) {
3664 let prefix = quote! { ::tauri::utils::config::PatternKind };
3665
3666 tokens.append_all(match self {
3667 Self::Brownfield => quote! { #prefix::Brownfield },
3668 #[cfg(not(feature = "isolation"))]
3669 Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
3670 #[cfg(feature = "isolation")]
3671 Self::Isolation { dir } => {
3672 let dir = path_buf_lit(dir);
3673 quote! { #prefix::Isolation { dir: #dir } }
3674 }
3675 })
3676 }
3677 }
3678
3679 impl ToTokens for WebviewInstallMode {
3680 fn to_tokens(&self, tokens: &mut TokenStream) {
3681 let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
3682
3683 tokens.append_all(match self {
3684 Self::Skip => quote! { #prefix::Skip },
3685 Self::DownloadBootstrapper { silent } => {
3686 quote! { #prefix::DownloadBootstrapper { silent: #silent } }
3687 }
3688 Self::EmbedBootstrapper { silent } => {
3689 quote! { #prefix::EmbedBootstrapper { silent: #silent } }
3690 }
3691 Self::OfflineInstaller { silent } => {
3692 quote! { #prefix::OfflineInstaller { silent: #silent } }
3693 }
3694 Self::FixedRuntime { path } => {
3695 let path = path_buf_lit(path);
3696 quote! { #prefix::FixedRuntime { path: #path } }
3697 }
3698 })
3699 }
3700 }
3701
3702 impl ToTokens for WindowsConfig {
3703 fn to_tokens(&self, tokens: &mut TokenStream) {
3704 let webview_install_mode = &self.webview_install_mode;
3705 tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
3706 webview_install_mode: #webview_install_mode,
3707 ..Default::default()
3708 }})
3709 }
3710 }
3711
3712 impl ToTokens for BundleConfig {
3713 fn to_tokens(&self, tokens: &mut TokenStream) {
3714 let publisher = quote!(None);
3715 let homepage = quote!(None);
3716 let icon = vec_lit(&self.icon, str_lit);
3717 let active = self.active;
3718 let targets = quote!(Default::default());
3719 let create_updater_artifacts = quote!(Default::default());
3720 let resources = quote!(None);
3721 let copyright = quote!(None);
3722 let category = quote!(None);
3723 let file_associations = quote!(None);
3724 let short_description = quote!(None);
3725 let long_description = quote!(None);
3726 let use_local_tools_dir = self.use_local_tools_dir;
3727 let external_bin = opt_vec_lit(self.external_bin.as_ref(), str_lit);
3728 let windows = &self.windows;
3729 let license = opt_str_lit(self.license.as_ref());
3730 let license_file = opt_lit(self.license_file.as_ref().map(path_buf_lit).as_ref());
3731 let linux = quote!(Default::default());
3732 let macos = quote!(Default::default());
3733 let ios = quote!(Default::default());
3734 let android = quote!(Default::default());
3735
3736 literal_struct!(
3737 tokens,
3738 ::tauri::utils::config::BundleConfig,
3739 active,
3740 publisher,
3741 homepage,
3742 icon,
3743 targets,
3744 create_updater_artifacts,
3745 resources,
3746 copyright,
3747 category,
3748 license,
3749 license_file,
3750 file_associations,
3751 short_description,
3752 long_description,
3753 use_local_tools_dir,
3754 external_bin,
3755 windows,
3756 linux,
3757 macos,
3758 ios,
3759 android
3760 );
3761 }
3762 }
3763
3764 impl ToTokens for FrontendDist {
3765 fn to_tokens(&self, tokens: &mut TokenStream) {
3766 let prefix = quote! { ::tauri::utils::config::FrontendDist };
3767
3768 tokens.append_all(match self {
3769 Self::Url(url) => {
3770 let url = url_lit(url);
3771 quote! { #prefix::Url(#url) }
3772 }
3773 Self::Directory(path) => {
3774 let path = path_buf_lit(path);
3775 quote! { #prefix::Directory(#path) }
3776 }
3777 Self::Files(files) => {
3778 let files = vec_lit(files, path_buf_lit);
3779 quote! { #prefix::Files(#files) }
3780 }
3781 })
3782 }
3783 }
3784
3785 impl ToTokens for RunnerConfig {
3786 fn to_tokens(&self, tokens: &mut TokenStream) {
3787 let prefix = quote! { ::tauri::utils::config::RunnerConfig };
3788
3789 tokens.append_all(match self {
3790 Self::String(cmd) => {
3791 let cmd = cmd.as_str();
3792 quote!(#prefix::String(#cmd.into()))
3793 }
3794 Self::Object { cmd, cwd, args } => {
3795 let cmd = cmd.as_str();
3796 let cwd = opt_str_lit(cwd.as_ref());
3797 let args = opt_lit(args.as_ref().map(|v| vec_lit(v, str_lit)).as_ref());
3798 quote!(#prefix::Object {
3799 cmd: #cmd.into(),
3800 cwd: #cwd,
3801 args: #args,
3802 })
3803 }
3804 })
3805 }
3806 }
3807
3808 impl ToTokens for BuildConfig {
3809 fn to_tokens(&self, tokens: &mut TokenStream) {
3810 let dev_url = opt_lit(self.dev_url.as_ref().map(url_lit).as_ref());
3811 let frontend_dist = opt_lit(self.frontend_dist.as_ref());
3812 let runner = opt_lit(self.runner.as_ref());
3813 let before_dev_command = quote!(None);
3814 let before_build_command = quote!(None);
3815 let before_bundle_command = quote!(None);
3816 let features = quote!(None);
3817 let remove_unused_commands = quote!(false);
3818 let additional_watch_folders = quote!(Vec::new());
3819
3820 literal_struct!(
3821 tokens,
3822 ::tauri::utils::config::BuildConfig,
3823 runner,
3824 dev_url,
3825 frontend_dist,
3826 before_dev_command,
3827 before_build_command,
3828 before_bundle_command,
3829 features,
3830 remove_unused_commands,
3831 additional_watch_folders
3832 );
3833 }
3834 }
3835
3836 impl ToTokens for CspDirectiveSources {
3837 fn to_tokens(&self, tokens: &mut TokenStream) {
3838 let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
3839
3840 tokens.append_all(match self {
3841 Self::Inline(sources) => {
3842 let sources = sources.as_str();
3843 quote!(#prefix::Inline(#sources.into()))
3844 }
3845 Self::List(list) => {
3846 let list = vec_lit(list, str_lit);
3847 quote!(#prefix::List(#list))
3848 }
3849 })
3850 }
3851 }
3852
3853 impl ToTokens for Csp {
3854 fn to_tokens(&self, tokens: &mut TokenStream) {
3855 let prefix = quote! { ::tauri::utils::config::Csp };
3856
3857 tokens.append_all(match self {
3858 Self::Policy(policy) => {
3859 let policy = policy.as_str();
3860 quote!(#prefix::Policy(#policy.into()))
3861 }
3862 Self::DirectiveMap(list) => {
3863 let map = map_lit(
3864 quote! { ::std::collections::HashMap },
3865 list,
3866 str_lit,
3867 identity,
3868 );
3869 quote!(#prefix::DirectiveMap(#map))
3870 }
3871 })
3872 }
3873 }
3874
3875 impl ToTokens for DisabledCspModificationKind {
3876 fn to_tokens(&self, tokens: &mut TokenStream) {
3877 let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
3878
3879 tokens.append_all(match self {
3880 Self::Flag(flag) => {
3881 quote! { #prefix::Flag(#flag) }
3882 }
3883 Self::List(directives) => {
3884 let directives = vec_lit(directives, str_lit);
3885 quote! { #prefix::List(#directives) }
3886 }
3887 });
3888 }
3889 }
3890
3891 impl ToTokens for CapabilityEntry {
3892 fn to_tokens(&self, tokens: &mut TokenStream) {
3893 let prefix = quote! { ::tauri::utils::config::CapabilityEntry };
3894
3895 tokens.append_all(match self {
3896 Self::Inlined(capability) => {
3897 quote! { #prefix::Inlined(#capability) }
3898 }
3899 Self::Reference(id) => {
3900 let id = str_lit(id);
3901 quote! { #prefix::Reference(#id) }
3902 }
3903 });
3904 }
3905 }
3906
3907 impl ToTokens for HeaderSource {
3908 fn to_tokens(&self, tokens: &mut TokenStream) {
3909 let prefix = quote! { ::tauri::utils::config::HeaderSource };
3910
3911 tokens.append_all(match self {
3912 Self::Inline(s) => {
3913 let line = s.as_str();
3914 quote!(#prefix::Inline(#line.into()))
3915 }
3916 Self::List(l) => {
3917 let list = vec_lit(l, str_lit);
3918 quote!(#prefix::List(#list))
3919 }
3920 Self::Map(m) => {
3921 let map = map_lit(quote! { ::std::collections::HashMap }, m, str_lit, str_lit);
3922 quote!(#prefix::Map(#map))
3923 }
3924 })
3925 }
3926 }
3927
3928 impl ToTokens for HeaderConfig {
3929 fn to_tokens(&self, tokens: &mut TokenStream) {
3930 let access_control_allow_credentials =
3931 opt_lit(self.access_control_allow_credentials.as_ref());
3932 let access_control_allow_headers = opt_lit(self.access_control_allow_headers.as_ref());
3933 let access_control_allow_methods = opt_lit(self.access_control_allow_methods.as_ref());
3934 let access_control_expose_headers = opt_lit(self.access_control_expose_headers.as_ref());
3935 let access_control_max_age = opt_lit(self.access_control_max_age.as_ref());
3936 let cross_origin_embedder_policy = opt_lit(self.cross_origin_embedder_policy.as_ref());
3937 let cross_origin_opener_policy = opt_lit(self.cross_origin_opener_policy.as_ref());
3938 let cross_origin_resource_policy = opt_lit(self.cross_origin_resource_policy.as_ref());
3939 let permissions_policy = opt_lit(self.permissions_policy.as_ref());
3940 let service_worker_allowed = opt_lit(self.service_worker_allowed.as_ref());
3941 let timing_allow_origin = opt_lit(self.timing_allow_origin.as_ref());
3942 let x_content_type_options = opt_lit(self.x_content_type_options.as_ref());
3943 let tauri_custom_header = opt_lit(self.tauri_custom_header.as_ref());
3944
3945 literal_struct!(
3946 tokens,
3947 ::tauri::utils::config::HeaderConfig,
3948 access_control_allow_credentials,
3949 access_control_allow_headers,
3950 access_control_allow_methods,
3951 access_control_expose_headers,
3952 access_control_max_age,
3953 cross_origin_embedder_policy,
3954 cross_origin_opener_policy,
3955 cross_origin_resource_policy,
3956 permissions_policy,
3957 service_worker_allowed,
3958 timing_allow_origin,
3959 x_content_type_options,
3960 tauri_custom_header
3961 );
3962 }
3963 }
3964
3965 impl ToTokens for SecurityConfig {
3966 fn to_tokens(&self, tokens: &mut TokenStream) {
3967 let csp = opt_lit(self.csp.as_ref());
3968 let dev_csp = opt_lit(self.dev_csp.as_ref());
3969 let freeze_prototype = self.freeze_prototype;
3970 let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
3971 let asset_protocol = &self.asset_protocol;
3972 let pattern = &self.pattern;
3973 let capabilities = vec_lit(&self.capabilities, identity);
3974 let headers = opt_lit(self.headers.as_ref());
3975
3976 literal_struct!(
3977 tokens,
3978 ::tauri::utils::config::SecurityConfig,
3979 csp,
3980 dev_csp,
3981 freeze_prototype,
3982 dangerous_disable_asset_csp_modification,
3983 asset_protocol,
3984 pattern,
3985 capabilities,
3986 headers
3987 );
3988 }
3989 }
3990
3991 impl ToTokens for TrayIconConfig {
3992 fn to_tokens(&self, tokens: &mut TokenStream) {
3993 tokens.append_all(quote!(#[allow(deprecated)]));
3995
3996 let id = opt_str_lit(self.id.as_ref());
3997 let icon_as_template = self.icon_as_template;
3998 #[allow(deprecated)]
3999 let menu_on_left_click = self.menu_on_left_click;
4000 let show_menu_on_left_click = self.show_menu_on_left_click;
4001 let icon_path = path_buf_lit(&self.icon_path);
4002 let title = opt_str_lit(self.title.as_ref());
4003 let tooltip = opt_str_lit(self.tooltip.as_ref());
4004 literal_struct!(
4005 tokens,
4006 ::tauri::utils::config::TrayIconConfig,
4007 id,
4008 icon_path,
4009 icon_as_template,
4010 menu_on_left_click,
4011 show_menu_on_left_click,
4012 title,
4013 tooltip
4014 );
4015 }
4016 }
4017
4018 impl ToTokens for FsScope {
4019 fn to_tokens(&self, tokens: &mut TokenStream) {
4020 let prefix = quote! { ::tauri::utils::config::FsScope };
4021
4022 tokens.append_all(match self {
4023 Self::AllowedPaths(allow) => {
4024 let allowed_paths = vec_lit(allow, path_buf_lit);
4025 quote! { #prefix::AllowedPaths(#allowed_paths) }
4026 }
4027 Self::Scope { allow, deny , require_literal_leading_dot} => {
4028 let allow = vec_lit(allow, path_buf_lit);
4029 let deny = vec_lit(deny, path_buf_lit);
4030 let require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref());
4031 quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
4032 }
4033 });
4034 }
4035 }
4036
4037 impl ToTokens for AssetProtocolConfig {
4038 fn to_tokens(&self, tokens: &mut TokenStream) {
4039 let scope = &self.scope;
4040 tokens.append_all(quote! { ::tauri::utils::config::AssetProtocolConfig { scope: #scope, ..Default::default() } })
4041 }
4042 }
4043
4044 impl ToTokens for AppConfig {
4045 fn to_tokens(&self, tokens: &mut TokenStream) {
4046 let windows = vec_lit(&self.windows, identity);
4047 let security = &self.security;
4048 let tray_icon = opt_lit(self.tray_icon.as_ref());
4049 let macos_private_api = self.macos_private_api;
4050 let with_global_tauri = self.with_global_tauri;
4051 let enable_gtk_app_id = self.enable_gtk_app_id;
4052
4053 literal_struct!(
4054 tokens,
4055 ::tauri::utils::config::AppConfig,
4056 windows,
4057 security,
4058 tray_icon,
4059 macos_private_api,
4060 with_global_tauri,
4061 enable_gtk_app_id
4062 );
4063 }
4064 }
4065
4066 impl ToTokens for PluginConfig {
4067 fn to_tokens(&self, tokens: &mut TokenStream) {
4068 let config = map_lit(
4069 quote! { ::std::collections::HashMap },
4070 &self.0,
4071 str_lit,
4072 json_value_lit,
4073 );
4074 tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
4075 }
4076 }
4077
4078 impl ToTokens for Config {
4079 fn to_tokens(&self, tokens: &mut TokenStream) {
4080 let schema = quote!(None);
4081 let product_name = opt_str_lit(self.product_name.as_ref());
4082 let main_binary_name = opt_str_lit(self.main_binary_name.as_ref());
4083 let version = opt_str_lit(self.version.as_ref());
4084 let identifier = str_lit(&self.identifier);
4085 let app = &self.app;
4086 let build = &self.build;
4087 let bundle = &self.bundle;
4088 let plugins = &self.plugins;
4089
4090 literal_struct!(
4091 tokens,
4092 ::tauri::utils::config::Config,
4093 schema,
4094 product_name,
4095 main_binary_name,
4096 version,
4097 identifier,
4098 app,
4099 build,
4100 bundle,
4101 plugins
4102 );
4103 }
4104 }
4105}
4106
4107#[cfg(test)]
4108mod test {
4109 use super::*;
4110
4111 #[test]
4114 fn test_defaults() {
4116 let a_config = AppConfig::default();
4118 let b_config = BuildConfig::default();
4120 let d_windows: Vec<WindowConfig> = vec![];
4122 let d_bundle = BundleConfig::default();
4124
4125 let app = AppConfig {
4127 windows: vec![],
4128 security: SecurityConfig {
4129 csp: None,
4130 dev_csp: None,
4131 freeze_prototype: false,
4132 dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
4133 asset_protocol: AssetProtocolConfig::default(),
4134 pattern: Default::default(),
4135 capabilities: Vec::new(),
4136 headers: None,
4137 },
4138 tray_icon: None,
4139 macos_private_api: false,
4140 with_global_tauri: false,
4141 enable_gtk_app_id: false,
4142 };
4143
4144 let build = BuildConfig {
4146 runner: None,
4147 dev_url: None,
4148 frontend_dist: None,
4149 before_dev_command: None,
4150 before_build_command: None,
4151 before_bundle_command: None,
4152 features: None,
4153 remove_unused_commands: false,
4154 additional_watch_folders: Vec::new(),
4155 };
4156
4157 let bundle = BundleConfig {
4159 active: false,
4160 targets: Default::default(),
4161 create_updater_artifacts: Default::default(),
4162 publisher: None,
4163 homepage: None,
4164 icon: Vec::new(),
4165 resources: None,
4166 copyright: None,
4167 category: None,
4168 file_associations: None,
4169 short_description: None,
4170 long_description: None,
4171 use_local_tools_dir: false,
4172 license: None,
4173 license_file: None,
4174 linux: Default::default(),
4175 macos: Default::default(),
4176 external_bin: None,
4177 windows: Default::default(),
4178 ios: Default::default(),
4179 android: Default::default(),
4180 };
4181
4182 assert_eq!(a_config, app);
4184 assert_eq!(b_config, build);
4185 assert_eq!(d_bundle, bundle);
4186 assert_eq!(d_windows, app.windows);
4187 }
4188
4189 #[test]
4190 fn parse_hex_color() {
4191 use super::Color;
4192
4193 assert_eq!(Color(255, 255, 255, 255), "fff".parse().unwrap());
4194 assert_eq!(Color(255, 255, 255, 255), "#fff".parse().unwrap());
4195 assert_eq!(Color(0, 0, 0, 255), "#000000".parse().unwrap());
4196 assert_eq!(Color(0, 0, 0, 255), "#000000ff".parse().unwrap());
4197 assert_eq!(Color(0, 255, 0, 255), "#00ff00ff".parse().unwrap());
4198 }
4199
4200 #[test]
4201 fn test_runner_config_string_format() {
4202 use super::RunnerConfig;
4203
4204 let json = r#""cargo""#;
4206 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4207
4208 assert_eq!(runner.cmd(), "cargo");
4209 assert_eq!(runner.cwd(), None);
4210 assert_eq!(runner.args(), None);
4211
4212 let serialized = serde_json::to_string(&runner).unwrap();
4214 assert_eq!(serialized, r#""cargo""#);
4215 }
4216
4217 #[test]
4218 fn test_runner_config_object_format_full() {
4219 use super::RunnerConfig;
4220
4221 let json = r#"{"cmd": "my_runner", "cwd": "/tmp/build", "args": ["--quiet", "--verbose"]}"#;
4223 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4224
4225 assert_eq!(runner.cmd(), "my_runner");
4226 assert_eq!(runner.cwd(), Some("/tmp/build"));
4227 assert_eq!(
4228 runner.args(),
4229 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4230 );
4231
4232 let serialized = serde_json::to_string(&runner).unwrap();
4234 let deserialized: RunnerConfig = serde_json::from_str(&serialized).unwrap();
4235 assert_eq!(runner, deserialized);
4236 }
4237
4238 #[test]
4239 fn test_runner_config_object_format_minimal() {
4240 use super::RunnerConfig;
4241
4242 let json = r#"{"cmd": "cross"}"#;
4244 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4245
4246 assert_eq!(runner.cmd(), "cross");
4247 assert_eq!(runner.cwd(), None);
4248 assert_eq!(runner.args(), None);
4249 }
4250
4251 #[test]
4252 fn test_runner_config_default() {
4253 use super::RunnerConfig;
4254
4255 let default_runner = RunnerConfig::default();
4256 assert_eq!(default_runner.cmd(), "cargo");
4257 assert_eq!(default_runner.cwd(), None);
4258 assert_eq!(default_runner.args(), None);
4259 }
4260
4261 #[test]
4262 fn test_runner_config_from_str() {
4263 use super::RunnerConfig;
4264
4265 let runner: RunnerConfig = "my_runner".into();
4267 assert_eq!(runner.cmd(), "my_runner");
4268 assert_eq!(runner.cwd(), None);
4269 assert_eq!(runner.args(), None);
4270 }
4271
4272 #[test]
4273 fn test_runner_config_from_string() {
4274 use super::RunnerConfig;
4275
4276 let runner: RunnerConfig = "another_runner".to_string().into();
4278 assert_eq!(runner.cmd(), "another_runner");
4279 assert_eq!(runner.cwd(), None);
4280 assert_eq!(runner.args(), None);
4281 }
4282
4283 #[test]
4284 fn test_runner_config_from_str_parse() {
4285 use super::RunnerConfig;
4286 use std::str::FromStr;
4287
4288 let runner = RunnerConfig::from_str("parsed_runner").unwrap();
4290 assert_eq!(runner.cmd(), "parsed_runner");
4291 assert_eq!(runner.cwd(), None);
4292 assert_eq!(runner.args(), None);
4293 }
4294
4295 #[test]
4296 fn test_runner_config_in_build_config() {
4297 use super::BuildConfig;
4298
4299 let json = r#"{"runner": "cargo"}"#;
4301 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4302
4303 let runner = build_config.runner.unwrap();
4304 assert_eq!(runner.cmd(), "cargo");
4305 assert_eq!(runner.cwd(), None);
4306 assert_eq!(runner.args(), None);
4307 }
4308
4309 #[test]
4310 fn test_runner_config_in_build_config_object() {
4311 use super::BuildConfig;
4312
4313 let json = r#"{"runner": {"cmd": "cross", "cwd": "/workspace", "args": ["--target", "x86_64-unknown-linux-gnu"]}}"#;
4315 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4316
4317 let runner = build_config.runner.unwrap();
4318 assert_eq!(runner.cmd(), "cross");
4319 assert_eq!(runner.cwd(), Some("/workspace"));
4320 assert_eq!(
4321 runner.args(),
4322 Some(
4323 &[
4324 "--target".to_string(),
4325 "x86_64-unknown-linux-gnu".to_string()
4326 ][..]
4327 )
4328 );
4329 }
4330
4331 #[test]
4332 fn test_runner_config_in_full_config() {
4333 use super::Config;
4334
4335 let json = r#"{
4337 "productName": "Test App",
4338 "version": "1.0.0",
4339 "identifier": "com.test.app",
4340 "build": {
4341 "runner": {
4342 "cmd": "my_custom_cargo",
4343 "cwd": "/tmp/build",
4344 "args": ["--quiet", "--verbose"]
4345 }
4346 }
4347 }"#;
4348
4349 let config: Config = serde_json::from_str(json).unwrap();
4350 let runner = config.build.runner.unwrap();
4351
4352 assert_eq!(runner.cmd(), "my_custom_cargo");
4353 assert_eq!(runner.cwd(), Some("/tmp/build"));
4354 assert_eq!(
4355 runner.args(),
4356 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4357 );
4358 }
4359
4360 #[test]
4361 fn test_runner_config_equality() {
4362 use super::RunnerConfig;
4363
4364 let runner1 = RunnerConfig::String("cargo".to_string());
4365 let runner2 = RunnerConfig::String("cargo".to_string());
4366 let runner3 = RunnerConfig::String("cross".to_string());
4367
4368 assert_eq!(runner1, runner2);
4369 assert_ne!(runner1, runner3);
4370
4371 let runner4 = RunnerConfig::Object {
4372 cmd: "cargo".to_string(),
4373 cwd: Some("/tmp".to_string()),
4374 args: Some(vec!["--quiet".to_string()]),
4375 };
4376 let runner5 = RunnerConfig::Object {
4377 cmd: "cargo".to_string(),
4378 cwd: Some("/tmp".to_string()),
4379 args: Some(vec!["--quiet".to_string()]),
4380 };
4381
4382 assert_eq!(runner4, runner5);
4383 assert_ne!(runner1, runner4);
4384 }
4385
4386 #[test]
4387 fn test_runner_config_untagged_serialization() {
4388 use super::RunnerConfig;
4389
4390 let string_runner = RunnerConfig::String("cargo".to_string());
4392 let string_json = serde_json::to_string(&string_runner).unwrap();
4393 assert_eq!(string_json, r#""cargo""#);
4394
4395 let object_runner = RunnerConfig::Object {
4397 cmd: "cross".to_string(),
4398 cwd: None,
4399 args: None,
4400 };
4401 let object_json = serde_json::to_string(&object_runner).unwrap();
4402 assert!(object_json.contains("\"cmd\":\"cross\""));
4403 assert!(object_json.contains("\"cwd\":null") || !object_json.contains("cwd"));
4405 assert!(object_json.contains("\"args\":null") || !object_json.contains("args"));
4406 }
4407}