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(
645 deserialize_with = "de_macos_minimum_system_version",
646 default = "macos_minimum_system_version",
647 alias = "minimum-system-version"
648 )]
649 pub minimum_system_version: Option<String>,
650 #[serde(alias = "exception-domain")]
653 pub exception_domain: Option<String>,
654 #[serde(alias = "signing-identity")]
656 pub signing_identity: Option<String>,
657 #[serde(alias = "hardened-runtime", default = "default_true")]
659 pub hardened_runtime: bool,
660 #[serde(alias = "provider-short-name")]
662 pub provider_short_name: Option<String>,
663 pub entitlements: Option<String>,
665 #[serde(default)]
667 pub dmg: DmgConfig,
668}
669
670impl Default for MacConfig {
671 fn default() -> Self {
672 Self {
673 frameworks: None,
674 files: HashMap::new(),
675 bundle_version: None,
676 bundle_name: None,
677 minimum_system_version: macos_minimum_system_version(),
678 exception_domain: None,
679 signing_identity: None,
680 hardened_runtime: true,
681 provider_short_name: None,
682 entitlements: None,
683 dmg: Default::default(),
684 }
685 }
686}
687
688fn macos_minimum_system_version() -> Option<String> {
689 Some("10.13".into())
690}
691
692fn ios_minimum_system_version() -> String {
693 "13.0".into()
694}
695
696#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
700#[cfg_attr(feature = "schema", derive(JsonSchema))]
701#[serde(rename_all = "camelCase", deny_unknown_fields)]
702pub struct WixLanguageConfig {
703 #[serde(alias = "locale-path")]
705 pub locale_path: Option<String>,
706}
707
708#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
710#[cfg_attr(feature = "schema", derive(JsonSchema))]
711#[serde(untagged)]
712pub enum WixLanguage {
713 One(String),
715 List(Vec<String>),
717 Localized(HashMap<String, WixLanguageConfig>),
719}
720
721impl Default for WixLanguage {
722 fn default() -> Self {
723 Self::One("en-US".into())
724 }
725}
726
727#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
731#[cfg_attr(feature = "schema", derive(JsonSchema))]
732#[serde(rename_all = "camelCase", deny_unknown_fields)]
733pub struct WixConfig {
734 pub version: Option<String>,
743 #[serde(alias = "upgrade-code")]
752 pub upgrade_code: Option<uuid::Uuid>,
753 #[serde(default)]
755 pub language: WixLanguage,
756 pub template: Option<PathBuf>,
758 #[serde(default, alias = "fragment-paths")]
760 pub fragment_paths: Vec<PathBuf>,
761 #[serde(default, alias = "component-group-refs")]
763 pub component_group_refs: Vec<String>,
764 #[serde(default, alias = "component-refs")]
766 pub component_refs: Vec<String>,
767 #[serde(default, alias = "feature-group-refs")]
769 pub feature_group_refs: Vec<String>,
770 #[serde(default, alias = "feature-refs")]
772 pub feature_refs: Vec<String>,
773 #[serde(default, alias = "merge-refs")]
775 pub merge_refs: Vec<String>,
776 #[serde(default, alias = "enable-elevated-update-task")]
778 pub enable_elevated_update_task: bool,
779 #[serde(alias = "banner-path")]
784 pub banner_path: Option<PathBuf>,
785 #[serde(alias = "dialog-image-path")]
790 pub dialog_image_path: Option<PathBuf>,
791}
792
793#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
797#[cfg_attr(feature = "schema", derive(JsonSchema))]
798#[serde(rename_all = "camelCase", deny_unknown_fields)]
799pub enum NsisCompression {
800 Zlib,
802 Bzip2,
804 Lzma,
806 None,
808}
809
810impl Default for NsisCompression {
811 fn default() -> Self {
812 Self::Lzma
813 }
814}
815
816#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
818#[serde(rename_all = "camelCase", deny_unknown_fields)]
819#[cfg_attr(feature = "schema", derive(JsonSchema))]
820pub enum NSISInstallerMode {
821 CurrentUser,
827 PerMachine,
832 Both,
838}
839
840impl Default for NSISInstallerMode {
841 fn default() -> Self {
842 Self::CurrentUser
843 }
844}
845
846#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
848#[cfg_attr(feature = "schema", derive(JsonSchema))]
849#[serde(rename_all = "camelCase", deny_unknown_fields)]
850pub struct NsisConfig {
851 pub template: Option<PathBuf>,
853 #[serde(alias = "header-image")]
857 pub header_image: Option<PathBuf>,
858 #[serde(alias = "sidebar-image")]
862 pub sidebar_image: Option<PathBuf>,
863 #[serde(alias = "install-icon")]
865 pub installer_icon: Option<PathBuf>,
866 #[serde(default, alias = "install-mode")]
868 pub install_mode: NSISInstallerMode,
869 pub languages: Option<Vec<String>>,
875 pub custom_language_files: Option<HashMap<String, PathBuf>>,
882 #[serde(default, alias = "display-language-selector")]
885 pub display_language_selector: bool,
886 #[serde(default)]
890 pub compression: NsisCompression,
891 #[serde(alias = "start-menu-folder")]
900 pub start_menu_folder: Option<String>,
901 #[serde(alias = "installer-hooks")]
932 pub installer_hooks: Option<PathBuf>,
933 #[serde(alias = "minimum-webview2-version")]
937 pub minimum_webview2_version: Option<String>,
938}
939
940#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
945#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
946#[cfg_attr(feature = "schema", derive(JsonSchema))]
947pub enum WebviewInstallMode {
948 Skip,
950 DownloadBootstrapper {
954 #[serde(default = "default_true")]
956 silent: bool,
957 },
958 EmbedBootstrapper {
962 #[serde(default = "default_true")]
964 silent: bool,
965 },
966 OfflineInstaller {
970 #[serde(default = "default_true")]
972 silent: bool,
973 },
974 FixedRuntime {
977 path: PathBuf,
982 },
983}
984
985impl Default for WebviewInstallMode {
986 fn default() -> Self {
987 Self::DownloadBootstrapper { silent: true }
988 }
989}
990
991#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
993#[cfg_attr(feature = "schema", derive(JsonSchema))]
994#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
995pub enum CustomSignCommandConfig {
996 Command(String),
1005 CommandWithOptions {
1010 cmd: String,
1012 args: Vec<String>,
1016 },
1017}
1018
1019#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1023#[cfg_attr(feature = "schema", derive(JsonSchema))]
1024#[serde(rename_all = "camelCase", deny_unknown_fields)]
1025pub struct WindowsConfig {
1026 #[serde(alias = "digest-algorithm")]
1029 pub digest_algorithm: Option<String>,
1030 #[serde(alias = "certificate-thumbprint")]
1032 pub certificate_thumbprint: Option<String>,
1033 #[serde(alias = "timestamp-url")]
1035 pub timestamp_url: Option<String>,
1036 #[serde(default)]
1039 pub tsp: bool,
1040 #[serde(default, alias = "webview-install-mode")]
1042 pub webview_install_mode: WebviewInstallMode,
1043 #[serde(default = "default_true", alias = "allow-downgrades")]
1049 pub allow_downgrades: bool,
1050 pub wix: Option<WixConfig>,
1052 pub nsis: Option<NsisConfig>,
1054 #[serde(alias = "sign-command")]
1062 pub sign_command: Option<CustomSignCommandConfig>,
1063}
1064
1065impl Default for WindowsConfig {
1066 fn default() -> Self {
1067 Self {
1068 digest_algorithm: None,
1069 certificate_thumbprint: None,
1070 timestamp_url: None,
1071 tsp: false,
1072 webview_install_mode: Default::default(),
1073 allow_downgrades: true,
1074 wix: None,
1075 nsis: None,
1076 sign_command: None,
1077 }
1078 }
1079}
1080
1081#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1083#[cfg_attr(feature = "schema", derive(JsonSchema))]
1084pub enum BundleTypeRole {
1085 #[default]
1087 Editor,
1088 Viewer,
1090 Shell,
1092 QLGenerator,
1094 None,
1096}
1097
1098impl Display for BundleTypeRole {
1099 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1100 match self {
1101 Self::Editor => write!(f, "Editor"),
1102 Self::Viewer => write!(f, "Viewer"),
1103 Self::Shell => write!(f, "Shell"),
1104 Self::QLGenerator => write!(f, "QLGenerator"),
1105 Self::None => write!(f, "None"),
1106 }
1107 }
1108}
1109
1110#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1114#[cfg_attr(feature = "schema", derive(JsonSchema))]
1115pub enum HandlerRank {
1116 #[default]
1118 Default,
1119 Owner,
1121 Alternate,
1123 None,
1125}
1126
1127impl Display for HandlerRank {
1128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1129 match self {
1130 Self::Default => write!(f, "Default"),
1131 Self::Owner => write!(f, "Owner"),
1132 Self::Alternate => write!(f, "Alternate"),
1133 Self::None => write!(f, "None"),
1134 }
1135 }
1136}
1137
1138#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
1142#[cfg_attr(feature = "schema", derive(JsonSchema))]
1143pub struct AssociationExt(pub String);
1144
1145impl fmt::Display for AssociationExt {
1146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1147 write!(f, "{}", self.0)
1148 }
1149}
1150
1151impl<'d> serde::Deserialize<'d> for AssociationExt {
1152 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
1153 let ext = String::deserialize(deserializer)?;
1154 if let Some(ext) = ext.strip_prefix('.') {
1155 Ok(AssociationExt(ext.into()))
1156 } else {
1157 Ok(AssociationExt(ext))
1158 }
1159 }
1160}
1161
1162#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1164#[cfg_attr(feature = "schema", derive(JsonSchema))]
1165#[serde(rename_all = "camelCase", deny_unknown_fields)]
1166pub struct FileAssociation {
1167 pub ext: Vec<AssociationExt>,
1169 pub name: Option<String>,
1171 pub description: Option<String>,
1173 #[serde(default)]
1175 pub role: BundleTypeRole,
1176 #[serde(alias = "mime-type")]
1178 pub mime_type: Option<String>,
1179 #[serde(default)]
1181 pub rank: HandlerRank,
1182}
1183
1184#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1186#[cfg_attr(feature = "schema", derive(JsonSchema))]
1187#[serde(rename_all = "camelCase", deny_unknown_fields)]
1188pub struct DeepLinkProtocol {
1189 pub schemes: Vec<String>,
1191 pub name: Option<String>,
1193 #[serde(default)]
1195 pub role: BundleTypeRole,
1196}
1197
1198#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1201#[cfg_attr(feature = "schema", derive(JsonSchema))]
1202#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1203pub enum BundleResources {
1204 List(Vec<String>),
1206 Map(HashMap<String, String>),
1208}
1209
1210impl BundleResources {
1211 pub fn push(&mut self, path: impl Into<String>) {
1213 match self {
1214 Self::List(l) => l.push(path.into()),
1215 Self::Map(l) => {
1216 let path = path.into();
1217 l.insert(path.clone(), path);
1218 }
1219 }
1220 }
1221}
1222
1223#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1225#[cfg_attr(feature = "schema", derive(JsonSchema))]
1226#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1227pub enum Updater {
1228 String(V1Compatible),
1230 Bool(bool),
1233}
1234
1235impl Default for Updater {
1236 fn default() -> Self {
1237 Self::Bool(false)
1238 }
1239}
1240
1241#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1243#[cfg_attr(feature = "schema", derive(JsonSchema))]
1244#[serde(rename_all = "camelCase", deny_unknown_fields)]
1245pub enum V1Compatible {
1246 V1Compatible,
1248}
1249
1250#[skip_serializing_none]
1254#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1255#[cfg_attr(feature = "schema", derive(JsonSchema))]
1256#[serde(rename_all = "camelCase", deny_unknown_fields)]
1257pub struct BundleConfig {
1258 #[serde(default)]
1260 pub active: bool,
1261 #[serde(default)]
1263 pub targets: BundleTarget,
1264 #[serde(default)]
1265 pub create_updater_artifacts: Updater,
1267 pub publisher: Option<String>,
1272 pub homepage: Option<String>,
1277 #[serde(default)]
1279 pub icon: Vec<String>,
1280 pub resources: Option<BundleResources>,
1284 pub copyright: Option<String>,
1286 pub license: Option<String>,
1289 #[serde(alias = "license-file")]
1291 pub license_file: Option<PathBuf>,
1292 pub category: Option<String>,
1297 pub file_associations: Option<Vec<FileAssociation>>,
1299 #[serde(alias = "short-description")]
1301 pub short_description: Option<String>,
1302 #[serde(alias = "long-description")]
1304 pub long_description: Option<String>,
1305 #[serde(default, alias = "use-local-tools-dir")]
1313 pub use_local_tools_dir: bool,
1314 #[serde(alias = "external-bin")]
1326 pub external_bin: Option<Vec<String>>,
1327 #[serde(default)]
1329 pub windows: WindowsConfig,
1330 #[serde(default)]
1332 pub linux: LinuxConfig,
1333 #[serde(rename = "macOS", alias = "macos", default)]
1335 pub macos: MacConfig,
1336 #[serde(rename = "iOS", alias = "ios", default)]
1338 pub ios: IosConfig,
1339 #[serde(default)]
1341 pub android: AndroidConfig,
1342}
1343
1344#[derive(Debug, PartialEq, Eq, Serialize, Default, Clone, Copy)]
1346#[serde(rename_all = "camelCase", deny_unknown_fields)]
1347pub struct Color(pub u8, pub u8, pub u8, pub u8);
1348
1349impl From<Color> for (u8, u8, u8, u8) {
1350 fn from(value: Color) -> Self {
1351 (value.0, value.1, value.2, value.3)
1352 }
1353}
1354
1355impl From<Color> for (u8, u8, u8) {
1356 fn from(value: Color) -> Self {
1357 (value.0, value.1, value.2)
1358 }
1359}
1360
1361impl From<(u8, u8, u8, u8)> for Color {
1362 fn from(value: (u8, u8, u8, u8)) -> Self {
1363 Color(value.0, value.1, value.2, value.3)
1364 }
1365}
1366
1367impl From<(u8, u8, u8)> for Color {
1368 fn from(value: (u8, u8, u8)) -> Self {
1369 Color(value.0, value.1, value.2, 255)
1370 }
1371}
1372
1373impl From<Color> for [u8; 4] {
1374 fn from(value: Color) -> Self {
1375 [value.0, value.1, value.2, value.3]
1376 }
1377}
1378
1379impl From<Color> for [u8; 3] {
1380 fn from(value: Color) -> Self {
1381 [value.0, value.1, value.2]
1382 }
1383}
1384
1385impl From<[u8; 4]> for Color {
1386 fn from(value: [u8; 4]) -> Self {
1387 Color(value[0], value[1], value[2], value[3])
1388 }
1389}
1390
1391impl From<[u8; 3]> for Color {
1392 fn from(value: [u8; 3]) -> Self {
1393 Color(value[0], value[1], value[2], 255)
1394 }
1395}
1396
1397impl FromStr for Color {
1398 type Err = String;
1399 fn from_str(mut color: &str) -> Result<Self, Self::Err> {
1400 color = color.trim().strip_prefix('#').unwrap_or(color);
1401 let color = match color.len() {
1402 3 => color.chars()
1404 .flat_map(|c| std::iter::repeat(c).take(2))
1405 .chain(std::iter::repeat('f').take(2))
1406 .collect(),
1407 6 => format!("{color}FF"),
1408 8 => color.to_string(),
1409 _ => return Err("Invalid hex color length, must be either 3, 6 or 8, for example: #fff, #ffffff, or #ffffffff".into()),
1410 };
1411
1412 let r = u8::from_str_radix(&color[0..2], 16).map_err(|e| e.to_string())?;
1413 let g = u8::from_str_radix(&color[2..4], 16).map_err(|e| e.to_string())?;
1414 let b = u8::from_str_radix(&color[4..6], 16).map_err(|e| e.to_string())?;
1415 let a = u8::from_str_radix(&color[6..8], 16).map_err(|e| e.to_string())?;
1416
1417 Ok(Color(r, g, b, a))
1418 }
1419}
1420
1421fn default_alpha() -> u8 {
1422 255
1423}
1424
1425#[derive(Deserialize)]
1426#[cfg_attr(feature = "schema", derive(JsonSchema))]
1427#[serde(untagged)]
1428enum InnerColor {
1429 String(String),
1431 Rgb((u8, u8, u8)),
1433 Rgba((u8, u8, u8, u8)),
1435 RgbaObject {
1437 red: u8,
1438 green: u8,
1439 blue: u8,
1440 #[serde(default = "default_alpha")]
1441 alpha: u8,
1442 },
1443}
1444
1445impl<'de> Deserialize<'de> for Color {
1446 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1447 where
1448 D: Deserializer<'de>,
1449 {
1450 let color = InnerColor::deserialize(deserializer)?;
1451 let color = match color {
1452 InnerColor::String(string) => string.parse().map_err(serde::de::Error::custom)?,
1453 InnerColor::Rgb(rgb) => Color(rgb.0, rgb.1, rgb.2, 255),
1454 InnerColor::Rgba(rgb) => rgb.into(),
1455 InnerColor::RgbaObject {
1456 red,
1457 green,
1458 blue,
1459 alpha,
1460 } => Color(red, green, blue, alpha),
1461 };
1462
1463 Ok(color)
1464 }
1465}
1466
1467#[cfg(feature = "schema")]
1468impl schemars::JsonSchema for Color {
1469 fn schema_name() -> String {
1470 "Color".to_string()
1471 }
1472
1473 fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1474 let mut schema = schemars::schema_for!(InnerColor).schema;
1475 schema.metadata = None; let any_of = schema.subschemas().any_of.as_mut().unwrap();
1479 let schemars::schema::Schema::Object(str_schema) = any_of.first_mut().unwrap() else {
1480 unreachable!()
1481 };
1482 str_schema.string().pattern = Some("^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$".into());
1483
1484 schema.into()
1485 }
1486}
1487
1488#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1490#[cfg_attr(feature = "schema", derive(JsonSchema))]
1491#[serde(rename_all = "camelCase", deny_unknown_fields)]
1492pub enum BackgroundThrottlingPolicy {
1493 Disabled,
1495 Suspend,
1497 Throttle,
1499}
1500
1501#[skip_serializing_none]
1503#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1504#[cfg_attr(feature = "schema", derive(JsonSchema))]
1505#[serde(rename_all = "camelCase", deny_unknown_fields)]
1506pub struct WindowEffectsConfig {
1507 pub effects: Vec<WindowEffect>,
1510 pub state: Option<WindowEffectState>,
1512 pub radius: Option<f64>,
1514 pub color: Option<Color>,
1517}
1518
1519#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1522#[cfg_attr(feature = "schema", derive(JsonSchema))]
1523#[serde(rename_all = "camelCase", deny_unknown_fields)]
1524pub struct PreventOverflowMargin {
1525 pub width: u32,
1527 pub height: u32,
1529}
1530
1531#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1533#[cfg_attr(feature = "schema", derive(JsonSchema))]
1534#[serde(untagged)]
1535pub enum PreventOverflowConfig {
1536 Enable(bool),
1538 Margin(PreventOverflowMargin),
1541}
1542
1543#[skip_serializing_none]
1547#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
1548#[cfg_attr(feature = "schema", derive(JsonSchema))]
1549#[serde(rename_all = "camelCase", deny_unknown_fields)]
1550pub struct WindowConfig {
1551 #[serde(default = "default_window_label")]
1553 pub label: String,
1554 #[serde(default = "default_true")]
1559 pub create: bool,
1560 #[serde(default)]
1562 pub url: WebviewUrl,
1563 #[serde(alias = "user-agent")]
1565 pub user_agent: Option<String>,
1566 #[serde(default = "default_true", alias = "drag-drop-enabled")]
1570 pub drag_drop_enabled: bool,
1571 #[serde(default)]
1573 pub center: bool,
1574 pub x: Option<f64>,
1576 pub y: Option<f64>,
1578 #[serde(default = "default_width")]
1580 pub width: f64,
1581 #[serde(default = "default_height")]
1583 pub height: f64,
1584 #[serde(alias = "min-width")]
1586 pub min_width: Option<f64>,
1587 #[serde(alias = "min-height")]
1589 pub min_height: Option<f64>,
1590 #[serde(alias = "max-width")]
1592 pub max_width: Option<f64>,
1593 #[serde(alias = "max-height")]
1595 pub max_height: Option<f64>,
1596 #[serde(alias = "prevent-overflow")]
1602 pub prevent_overflow: Option<PreventOverflowConfig>,
1603 #[serde(default = "default_true")]
1605 pub resizable: bool,
1606 #[serde(default = "default_true")]
1614 pub maximizable: bool,
1615 #[serde(default = "default_true")]
1621 pub minimizable: bool,
1622 #[serde(default = "default_true")]
1630 pub closable: bool,
1631 #[serde(default = "default_title")]
1633 pub title: String,
1634 #[serde(default)]
1636 pub fullscreen: bool,
1637 #[serde(default = "default_true")]
1639 pub focus: bool,
1640 #[serde(default)]
1645 pub transparent: bool,
1646 #[serde(default)]
1648 pub maximized: bool,
1649 #[serde(default = "default_true")]
1651 pub visible: bool,
1652 #[serde(default = "default_true")]
1654 pub decorations: bool,
1655 #[serde(default, alias = "always-on-bottom")]
1657 pub always_on_bottom: bool,
1658 #[serde(default, alias = "always-on-top")]
1660 pub always_on_top: bool,
1661 #[serde(default, alias = "visible-on-all-workspaces")]
1667 pub visible_on_all_workspaces: bool,
1668 #[serde(default, alias = "content-protected")]
1670 pub content_protected: bool,
1671 #[serde(default, alias = "skip-taskbar")]
1673 pub skip_taskbar: bool,
1674 pub window_classname: Option<String>,
1676 pub theme: Option<crate::Theme>,
1678 #[serde(default, alias = "title-bar-style")]
1680 pub title_bar_style: TitleBarStyle,
1681 #[serde(default, alias = "traffic-light-position")]
1685 pub traffic_light_position: Option<LogicalPosition>,
1686 #[serde(default, alias = "hidden-title")]
1688 pub hidden_title: bool,
1689 #[serde(default, alias = "accept-first-mouse")]
1691 pub accept_first_mouse: bool,
1692 #[serde(default, alias = "tabbing-identifier")]
1699 pub tabbing_identifier: Option<String>,
1700 #[serde(default, alias = "additional-browser-args")]
1703 pub additional_browser_args: Option<String>,
1704 #[serde(default = "default_true")]
1714 pub shadow: bool,
1715 #[serde(default, alias = "window-effects")]
1724 pub window_effects: Option<WindowEffectsConfig>,
1725 #[serde(default)]
1731 pub incognito: bool,
1732 pub parent: Option<String>,
1744 #[serde(alias = "proxy-url")]
1752 pub proxy_url: Option<Url>,
1753 #[serde(default, alias = "zoom-hotkeys-enabled")]
1763 pub zoom_hotkeys_enabled: bool,
1764 #[serde(default, alias = "browser-extensions-enabled")]
1771 pub browser_extensions_enabled: bool,
1772
1773 #[serde(default, alias = "use-https-scheme")]
1783 pub use_https_scheme: bool,
1784 pub devtools: Option<bool>,
1794
1795 #[serde(alias = "background-color")]
1803 pub background_color: Option<Color>,
1804
1805 #[serde(default, alias = "background-throttling")]
1820 pub background_throttling: Option<BackgroundThrottlingPolicy>,
1821 #[serde(default, alias = "javascript-disabled")]
1823 pub javascript_disabled: bool,
1824 #[serde(default = "default_true", alias = "allow-link-preview")]
1827 pub allow_link_preview: bool,
1828 #[serde(
1833 default,
1834 alias = "disable-input-accessory-view",
1835 alias = "disable_input_accessory_view"
1836 )]
1837 pub disable_input_accessory_view: bool,
1838}
1839
1840impl Default for WindowConfig {
1841 fn default() -> Self {
1842 Self {
1843 label: default_window_label(),
1844 url: WebviewUrl::default(),
1845 create: true,
1846 user_agent: None,
1847 drag_drop_enabled: true,
1848 center: false,
1849 x: None,
1850 y: None,
1851 width: default_width(),
1852 height: default_height(),
1853 min_width: None,
1854 min_height: None,
1855 max_width: None,
1856 max_height: None,
1857 prevent_overflow: None,
1858 resizable: true,
1859 maximizable: true,
1860 minimizable: true,
1861 closable: true,
1862 title: default_title(),
1863 fullscreen: false,
1864 focus: false,
1865 transparent: false,
1866 maximized: false,
1867 visible: true,
1868 decorations: true,
1869 always_on_bottom: false,
1870 always_on_top: false,
1871 visible_on_all_workspaces: false,
1872 content_protected: false,
1873 skip_taskbar: false,
1874 window_classname: None,
1875 theme: None,
1876 title_bar_style: Default::default(),
1877 traffic_light_position: None,
1878 hidden_title: false,
1879 accept_first_mouse: false,
1880 tabbing_identifier: None,
1881 additional_browser_args: None,
1882 shadow: true,
1883 window_effects: None,
1884 incognito: false,
1885 parent: None,
1886 proxy_url: None,
1887 zoom_hotkeys_enabled: false,
1888 browser_extensions_enabled: false,
1889 use_https_scheme: false,
1890 devtools: None,
1891 background_color: None,
1892 background_throttling: None,
1893 javascript_disabled: false,
1894 allow_link_preview: true,
1895 disable_input_accessory_view: false,
1896 }
1897 }
1898}
1899
1900fn default_window_label() -> String {
1901 "main".to_string()
1902}
1903
1904fn default_width() -> f64 {
1905 800f64
1906}
1907
1908fn default_height() -> f64 {
1909 600f64
1910}
1911
1912fn default_title() -> String {
1913 "Tauri App".to_string()
1914}
1915
1916#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1919#[cfg_attr(feature = "schema", derive(JsonSchema))]
1920#[serde(rename_all = "camelCase", untagged)]
1921pub enum CspDirectiveSources {
1922 Inline(String),
1924 List(Vec<String>),
1926}
1927
1928impl Default for CspDirectiveSources {
1929 fn default() -> Self {
1930 Self::List(Vec::new())
1931 }
1932}
1933
1934impl From<CspDirectiveSources> for Vec<String> {
1935 fn from(sources: CspDirectiveSources) -> Self {
1936 match sources {
1937 CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
1938 CspDirectiveSources::List(l) => l,
1939 }
1940 }
1941}
1942
1943impl CspDirectiveSources {
1944 pub fn contains(&self, source: &str) -> bool {
1946 match self {
1947 Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
1948 Self::List(l) => l.contains(&source.into()),
1949 }
1950 }
1951
1952 pub fn push<S: AsRef<str>>(&mut self, source: S) {
1954 match self {
1955 Self::Inline(s) => {
1956 s.push(' ');
1957 s.push_str(source.as_ref());
1958 }
1959 Self::List(l) => {
1960 l.push(source.as_ref().to_string());
1961 }
1962 }
1963 }
1964
1965 pub fn extend(&mut self, sources: Vec<String>) {
1967 for s in sources {
1968 self.push(s);
1969 }
1970 }
1971}
1972
1973#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1976#[cfg_attr(feature = "schema", derive(JsonSchema))]
1977#[serde(rename_all = "camelCase", untagged)]
1978pub enum Csp {
1979 Policy(String),
1981 DirectiveMap(HashMap<String, CspDirectiveSources>),
1983}
1984
1985impl From<HashMap<String, CspDirectiveSources>> for Csp {
1986 fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
1987 Self::DirectiveMap(map)
1988 }
1989}
1990
1991impl From<Csp> for HashMap<String, CspDirectiveSources> {
1992 fn from(csp: Csp) -> Self {
1993 match csp {
1994 Csp::Policy(policy) => {
1995 let mut map = HashMap::new();
1996 for directive in policy.split(';') {
1997 let mut tokens = directive.trim().split(' ');
1998 if let Some(directive) = tokens.next() {
1999 let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
2000 map.insert(directive.to_string(), CspDirectiveSources::List(sources));
2001 }
2002 }
2003 map
2004 }
2005 Csp::DirectiveMap(m) => m,
2006 }
2007 }
2008}
2009
2010impl Display for Csp {
2011 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2012 match self {
2013 Self::Policy(s) => write!(f, "{s}"),
2014 Self::DirectiveMap(m) => {
2015 let len = m.len();
2016 let mut i = 0;
2017 for (directive, sources) in m {
2018 let sources: Vec<String> = sources.clone().into();
2019 write!(f, "{} {}", directive, sources.join(" "))?;
2020 i += 1;
2021 if i != len {
2022 write!(f, "; ")?;
2023 }
2024 }
2025 Ok(())
2026 }
2027 }
2028 }
2029}
2030
2031#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2033#[serde(untagged)]
2034#[cfg_attr(feature = "schema", derive(JsonSchema))]
2035pub enum DisabledCspModificationKind {
2036 Flag(bool),
2039 List(Vec<String>),
2041}
2042
2043impl DisabledCspModificationKind {
2044 pub fn can_modify(&self, directive: &str) -> bool {
2046 match self {
2047 Self::Flag(f) => !f,
2048 Self::List(l) => !l.contains(&directive.into()),
2049 }
2050 }
2051}
2052
2053impl Default for DisabledCspModificationKind {
2054 fn default() -> Self {
2055 Self::Flag(false)
2056 }
2057}
2058
2059#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2068#[serde(untagged)]
2069#[cfg_attr(feature = "schema", derive(JsonSchema))]
2070pub enum FsScope {
2071 AllowedPaths(Vec<PathBuf>),
2073 #[serde(rename_all = "camelCase")]
2075 Scope {
2076 #[serde(default)]
2078 allow: Vec<PathBuf>,
2079 #[serde(default)]
2082 deny: Vec<PathBuf>,
2083 #[serde(alias = "require-literal-leading-dot")]
2092 require_literal_leading_dot: Option<bool>,
2093 },
2094}
2095
2096impl Default for FsScope {
2097 fn default() -> Self {
2098 Self::AllowedPaths(Vec::new())
2099 }
2100}
2101
2102impl FsScope {
2103 pub fn allowed_paths(&self) -> &Vec<PathBuf> {
2105 match self {
2106 Self::AllowedPaths(p) => p,
2107 Self::Scope { allow, .. } => allow,
2108 }
2109 }
2110
2111 pub fn forbidden_paths(&self) -> Option<&Vec<PathBuf>> {
2113 match self {
2114 Self::AllowedPaths(_) => None,
2115 Self::Scope { deny, .. } => Some(deny),
2116 }
2117 }
2118}
2119
2120#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2124#[cfg_attr(feature = "schema", derive(JsonSchema))]
2125#[serde(rename_all = "camelCase", deny_unknown_fields)]
2126pub struct AssetProtocolConfig {
2127 #[serde(default)]
2129 pub scope: FsScope,
2130 #[serde(default)]
2132 pub enable: bool,
2133}
2134
2135#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2139#[cfg_attr(feature = "schema", derive(JsonSchema))]
2140#[serde(rename_all = "camelCase", untagged)]
2141pub enum HeaderSource {
2142 Inline(String),
2144 List(Vec<String>),
2146 Map(HashMap<String, String>),
2148}
2149
2150impl Display for HeaderSource {
2151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2152 match self {
2153 Self::Inline(s) => write!(f, "{s}"),
2154 Self::List(l) => write!(f, "{}", l.join(", ")),
2155 Self::Map(m) => {
2156 let len = m.len();
2157 let mut i = 0;
2158 for (key, value) in m {
2159 write!(f, "{key} {value}")?;
2160 i += 1;
2161 if i != len {
2162 write!(f, "; ")?;
2163 }
2164 }
2165 Ok(())
2166 }
2167 }
2168 }
2169}
2170
2171pub trait HeaderAddition {
2175 fn add_configured_headers(self, headers: Option<&HeaderConfig>) -> http::response::Builder;
2177}
2178
2179impl HeaderAddition for Builder {
2180 fn add_configured_headers(mut self, headers: Option<&HeaderConfig>) -> http::response::Builder {
2184 if let Some(headers) = headers {
2185 if let Some(value) = &headers.access_control_allow_credentials {
2187 self = self.header("Access-Control-Allow-Credentials", value.to_string());
2188 };
2189
2190 if let Some(value) = &headers.access_control_allow_headers {
2192 self = self.header("Access-Control-Allow-Headers", value.to_string());
2193 };
2194
2195 if let Some(value) = &headers.access_control_allow_methods {
2197 self = self.header("Access-Control-Allow-Methods", value.to_string());
2198 };
2199
2200 if let Some(value) = &headers.access_control_expose_headers {
2202 self = self.header("Access-Control-Expose-Headers", value.to_string());
2203 };
2204
2205 if let Some(value) = &headers.access_control_max_age {
2207 self = self.header("Access-Control-Max-Age", value.to_string());
2208 };
2209
2210 if let Some(value) = &headers.cross_origin_embedder_policy {
2212 self = self.header("Cross-Origin-Embedder-Policy", value.to_string());
2213 };
2214
2215 if let Some(value) = &headers.cross_origin_opener_policy {
2217 self = self.header("Cross-Origin-Opener-Policy", value.to_string());
2218 };
2219
2220 if let Some(value) = &headers.cross_origin_resource_policy {
2222 self = self.header("Cross-Origin-Resource-Policy", value.to_string());
2223 };
2224
2225 if let Some(value) = &headers.permissions_policy {
2227 self = self.header("Permission-Policy", value.to_string());
2228 };
2229
2230 if let Some(value) = &headers.service_worker_allowed {
2231 self = self.header("Service-Worker-Allowed", value.to_string());
2232 }
2233
2234 if let Some(value) = &headers.timing_allow_origin {
2236 self = self.header("Timing-Allow-Origin", value.to_string());
2237 };
2238
2239 if let Some(value) = &headers.x_content_type_options {
2241 self = self.header("X-Content-Type-Options", value.to_string());
2242 };
2243
2244 if let Some(value) = &headers.tauri_custom_header {
2246 self = self.header("Tauri-Custom-Header", value.to_string());
2248 };
2249 }
2250 self
2251 }
2252}
2253
2254#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2306#[cfg_attr(feature = "schema", derive(JsonSchema))]
2307#[serde(deny_unknown_fields)]
2308pub struct HeaderConfig {
2309 #[serde(rename = "Access-Control-Allow-Credentials")]
2314 pub access_control_allow_credentials: Option<HeaderSource>,
2315 #[serde(rename = "Access-Control-Allow-Headers")]
2323 pub access_control_allow_headers: Option<HeaderSource>,
2324 #[serde(rename = "Access-Control-Allow-Methods")]
2329 pub access_control_allow_methods: Option<HeaderSource>,
2330 #[serde(rename = "Access-Control-Expose-Headers")]
2336 pub access_control_expose_headers: Option<HeaderSource>,
2337 #[serde(rename = "Access-Control-Max-Age")]
2344 pub access_control_max_age: Option<HeaderSource>,
2345 #[serde(rename = "Cross-Origin-Embedder-Policy")]
2350 pub cross_origin_embedder_policy: Option<HeaderSource>,
2351 #[serde(rename = "Cross-Origin-Opener-Policy")]
2358 pub cross_origin_opener_policy: Option<HeaderSource>,
2359 #[serde(rename = "Cross-Origin-Resource-Policy")]
2364 pub cross_origin_resource_policy: Option<HeaderSource>,
2365 #[serde(rename = "Permissions-Policy")]
2370 pub permissions_policy: Option<HeaderSource>,
2371 #[serde(rename = "Service-Worker-Allowed")]
2381 pub service_worker_allowed: Option<HeaderSource>,
2382 #[serde(rename = "Timing-Allow-Origin")]
2388 pub timing_allow_origin: Option<HeaderSource>,
2389 #[serde(rename = "X-Content-Type-Options")]
2396 pub x_content_type_options: Option<HeaderSource>,
2397 #[serde(rename = "Tauri-Custom-Header")]
2402 pub tauri_custom_header: Option<HeaderSource>,
2403}
2404
2405impl HeaderConfig {
2406 pub fn new() -> Self {
2408 HeaderConfig {
2409 access_control_allow_credentials: None,
2410 access_control_allow_methods: None,
2411 access_control_allow_headers: None,
2412 access_control_expose_headers: None,
2413 access_control_max_age: None,
2414 cross_origin_embedder_policy: None,
2415 cross_origin_opener_policy: None,
2416 cross_origin_resource_policy: None,
2417 permissions_policy: None,
2418 service_worker_allowed: None,
2419 timing_allow_origin: None,
2420 x_content_type_options: None,
2421 tauri_custom_header: None,
2422 }
2423 }
2424}
2425
2426#[skip_serializing_none]
2430#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2431#[cfg_attr(feature = "schema", derive(JsonSchema))]
2432#[serde(rename_all = "camelCase", deny_unknown_fields)]
2433pub struct SecurityConfig {
2434 pub csp: Option<Csp>,
2440 #[serde(alias = "dev-csp")]
2445 pub dev_csp: Option<Csp>,
2446 #[serde(default, alias = "freeze-prototype")]
2448 pub freeze_prototype: bool,
2449 #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
2462 pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
2463 #[serde(default, alias = "asset-protocol")]
2465 pub asset_protocol: AssetProtocolConfig,
2466 #[serde(default)]
2468 pub pattern: PatternKind,
2469 #[serde(default)]
2473 pub capabilities: Vec<CapabilityEntry>,
2474 #[serde(default)]
2477 pub headers: Option<HeaderConfig>,
2478}
2479
2480#[derive(Debug, Clone, PartialEq, Serialize)]
2482#[cfg_attr(feature = "schema", derive(JsonSchema))]
2483#[serde(untagged)]
2484pub enum CapabilityEntry {
2485 Inlined(Capability),
2487 Reference(String),
2489}
2490
2491impl<'de> Deserialize<'de> for CapabilityEntry {
2492 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2493 where
2494 D: Deserializer<'de>,
2495 {
2496 UntaggedEnumVisitor::new()
2497 .string(|string| Ok(Self::Reference(string.to_owned())))
2498 .map(|map| map.deserialize::<Capability>().map(Self::Inlined))
2499 .deserialize(deserializer)
2500 }
2501}
2502
2503#[skip_serializing_none]
2505#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
2506#[serde(rename_all = "lowercase", tag = "use", content = "options")]
2507#[cfg_attr(feature = "schema", derive(JsonSchema))]
2508pub enum PatternKind {
2509 Brownfield,
2511 Isolation {
2513 dir: PathBuf,
2515 },
2516}
2517
2518impl Default for PatternKind {
2519 fn default() -> Self {
2520 Self::Brownfield
2521 }
2522}
2523
2524#[skip_serializing_none]
2528#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2529#[cfg_attr(feature = "schema", derive(JsonSchema))]
2530#[serde(rename_all = "camelCase", deny_unknown_fields)]
2531pub struct AppConfig {
2532 #[serde(default)]
2534 pub windows: Vec<WindowConfig>,
2535 #[serde(default)]
2537 pub security: SecurityConfig,
2538 #[serde(alias = "tray-icon")]
2540 pub tray_icon: Option<TrayIconConfig>,
2541 #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
2543 pub macos_private_api: bool,
2544 #[serde(default, alias = "with-global-tauri")]
2546 pub with_global_tauri: bool,
2547 #[serde(rename = "enableGTKAppId", alias = "enable-gtk-app-id", default)]
2549 pub enable_gtk_app_id: bool,
2550}
2551
2552impl AppConfig {
2553 pub fn all_features() -> Vec<&'static str> {
2555 vec![
2556 "tray-icon",
2557 "macos-private-api",
2558 "protocol-asset",
2559 "isolation",
2560 ]
2561 }
2562
2563 pub fn features(&self) -> Vec<&str> {
2565 let mut features = Vec::new();
2566 if self.tray_icon.is_some() {
2567 features.push("tray-icon");
2568 }
2569 if self.macos_private_api {
2570 features.push("macos-private-api");
2571 }
2572 if self.security.asset_protocol.enable {
2573 features.push("protocol-asset");
2574 }
2575
2576 if let PatternKind::Isolation { .. } = self.security.pattern {
2577 features.push("isolation");
2578 }
2579
2580 features.sort_unstable();
2581 features
2582 }
2583}
2584
2585#[skip_serializing_none]
2589#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2590#[cfg_attr(feature = "schema", derive(JsonSchema))]
2591#[serde(rename_all = "camelCase", deny_unknown_fields)]
2592pub struct TrayIconConfig {
2593 pub id: Option<String>,
2595 #[serde(alias = "icon-path")]
2601 pub icon_path: PathBuf,
2602 #[serde(default, alias = "icon-as-template")]
2604 pub icon_as_template: bool,
2605 #[serde(default = "default_true", alias = "menu-on-left-click")]
2611 #[deprecated(since = "2.2.0", note = "Use `show_menu_on_left_click` instead.")]
2612 pub menu_on_left_click: bool,
2613 #[serde(default = "default_true", alias = "show-menu-on-left-click")]
2619 pub show_menu_on_left_click: bool,
2620 pub title: Option<String>,
2622 pub tooltip: Option<String>,
2624}
2625
2626#[skip_serializing_none]
2628#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2629#[cfg_attr(feature = "schema", derive(JsonSchema))]
2630#[serde(rename_all = "camelCase", deny_unknown_fields)]
2631pub struct IosConfig {
2632 pub template: Option<PathBuf>,
2636 pub frameworks: Option<Vec<String>>,
2640 #[serde(alias = "development-team")]
2643 pub development_team: Option<String>,
2644 #[serde(alias = "bundle-version")]
2648 pub bundle_version: Option<String>,
2649 #[serde(
2653 alias = "minimum-system-version",
2654 default = "ios_minimum_system_version"
2655 )]
2656 pub minimum_system_version: String,
2657}
2658
2659impl Default for IosConfig {
2660 fn default() -> Self {
2661 Self {
2662 template: None,
2663 frameworks: None,
2664 development_team: None,
2665 bundle_version: None,
2666 minimum_system_version: ios_minimum_system_version(),
2667 }
2668 }
2669}
2670
2671#[skip_serializing_none]
2673#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2674#[cfg_attr(feature = "schema", derive(JsonSchema))]
2675#[serde(rename_all = "camelCase", deny_unknown_fields)]
2676pub struct AndroidConfig {
2677 #[serde(alias = "min-sdk-version", default = "default_min_sdk_version")]
2680 pub min_sdk_version: u32,
2681
2682 #[serde(alias = "version-code")]
2688 #[cfg_attr(feature = "schema", validate(range(min = 1, max = 2_100_000_000)))]
2689 pub version_code: Option<u32>,
2690}
2691
2692impl Default for AndroidConfig {
2693 fn default() -> Self {
2694 Self {
2695 min_sdk_version: default_min_sdk_version(),
2696 version_code: None,
2697 }
2698 }
2699}
2700
2701fn default_min_sdk_version() -> u32 {
2702 24
2703}
2704
2705#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2707#[cfg_attr(feature = "schema", derive(JsonSchema))]
2708#[serde(untagged, deny_unknown_fields)]
2709#[non_exhaustive]
2710pub enum FrontendDist {
2711 Url(Url),
2713 Directory(PathBuf),
2715 Files(Vec<PathBuf>),
2717}
2718
2719impl std::fmt::Display for FrontendDist {
2720 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2721 match self {
2722 Self::Url(url) => write!(f, "{url}"),
2723 Self::Directory(p) => write!(f, "{}", p.display()),
2724 Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
2725 }
2726 }
2727}
2728
2729#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2731#[cfg_attr(feature = "schema", derive(JsonSchema))]
2732#[serde(rename_all = "camelCase", untagged)]
2733pub enum BeforeDevCommand {
2734 Script(String),
2736 ScriptWithOptions {
2738 script: String,
2740 cwd: Option<String>,
2742 #[serde(default)]
2744 wait: bool,
2745 },
2746}
2747
2748#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2750#[cfg_attr(feature = "schema", derive(JsonSchema))]
2751#[serde(rename_all = "camelCase", untagged)]
2752pub enum HookCommand {
2753 Script(String),
2755 ScriptWithOptions {
2757 script: String,
2759 cwd: Option<String>,
2761 },
2762}
2763
2764#[skip_serializing_none]
2766#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2767#[cfg_attr(feature = "schema", derive(JsonSchema))]
2768#[serde(untagged)]
2769pub enum RunnerConfig {
2770 String(String),
2772 Object {
2774 cmd: String,
2776 cwd: Option<String>,
2778 args: Option<Vec<String>>,
2780 },
2781}
2782
2783impl Default for RunnerConfig {
2784 fn default() -> Self {
2785 RunnerConfig::String("cargo".to_string())
2786 }
2787}
2788
2789impl RunnerConfig {
2790 pub fn cmd(&self) -> &str {
2792 match self {
2793 RunnerConfig::String(cmd) => cmd,
2794 RunnerConfig::Object { cmd, .. } => cmd,
2795 }
2796 }
2797
2798 pub fn cwd(&self) -> Option<&str> {
2800 match self {
2801 RunnerConfig::String(_) => None,
2802 RunnerConfig::Object { cwd, .. } => cwd.as_deref(),
2803 }
2804 }
2805
2806 pub fn args(&self) -> Option<&[String]> {
2808 match self {
2809 RunnerConfig::String(_) => None,
2810 RunnerConfig::Object { args, .. } => args.as_deref(),
2811 }
2812 }
2813}
2814
2815impl std::str::FromStr for RunnerConfig {
2816 type Err = std::convert::Infallible;
2817
2818 fn from_str(s: &str) -> Result<Self, Self::Err> {
2819 Ok(RunnerConfig::String(s.to_string()))
2820 }
2821}
2822
2823impl From<&str> for RunnerConfig {
2824 fn from(s: &str) -> Self {
2825 RunnerConfig::String(s.to_string())
2826 }
2827}
2828
2829impl From<String> for RunnerConfig {
2830 fn from(s: String) -> Self {
2831 RunnerConfig::String(s)
2832 }
2833}
2834
2835#[skip_serializing_none]
2839#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
2840#[cfg_attr(feature = "schema", derive(JsonSchema))]
2841#[serde(rename_all = "camelCase", deny_unknown_fields)]
2842pub struct BuildConfig {
2843 pub runner: Option<RunnerConfig>,
2845 #[serde(alias = "dev-url")]
2853 pub dev_url: Option<Url>,
2854 #[serde(alias = "frontend-dist")]
2868 pub frontend_dist: Option<FrontendDist>,
2869 #[serde(alias = "before-dev-command")]
2873 pub before_dev_command: Option<BeforeDevCommand>,
2874 #[serde(alias = "before-build-command")]
2878 pub before_build_command: Option<HookCommand>,
2879 #[serde(alias = "before-bundle-command")]
2883 pub before_bundle_command: Option<HookCommand>,
2884 pub features: Option<Vec<String>>,
2886 #[serde(alias = "remove-unused-commands", default)]
2894 pub remove_unused_commands: bool,
2895}
2896
2897#[derive(Debug, PartialEq, Eq)]
2898struct PackageVersion(String);
2899
2900impl<'d> serde::Deserialize<'d> for PackageVersion {
2901 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
2902 struct PackageVersionVisitor;
2903
2904 impl Visitor<'_> for PackageVersionVisitor {
2905 type Value = PackageVersion;
2906
2907 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2908 write!(
2909 formatter,
2910 "a semver string or a path to a package.json file"
2911 )
2912 }
2913
2914 fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
2915 let path = PathBuf::from(value);
2916 if path.exists() {
2917 let json_str = read_to_string(&path)
2918 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2919 let package_json: serde_json::Value = serde_json::from_str(&json_str)
2920 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2921 if let Some(obj) = package_json.as_object() {
2922 let version = obj
2923 .get("version")
2924 .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
2925 .as_str()
2926 .ok_or_else(|| {
2927 DeError::custom(format!("`{} > version` must be a string", path.display()))
2928 })?;
2929 Ok(PackageVersion(
2930 Version::from_str(version)
2931 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2932 .to_string(),
2933 ))
2934 } else {
2935 Err(DeError::custom(
2936 "`package > version` value is not a path to a JSON object",
2937 ))
2938 }
2939 } else {
2940 Ok(PackageVersion(
2941 Version::from_str(value)
2942 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2943 .to_string(),
2944 ))
2945 }
2946 }
2947 }
2948
2949 deserializer.deserialize_string(PackageVersionVisitor {})
2950 }
2951}
2952
2953fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
2954where
2955 D: Deserializer<'de>,
2956{
2957 Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
2958}
2959
2960#[skip_serializing_none]
3026#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
3027#[cfg_attr(feature = "schema", derive(JsonSchema))]
3028#[serde(rename_all = "camelCase", deny_unknown_fields)]
3029pub struct Config {
3030 #[serde(rename = "$schema")]
3032 pub schema: Option<String>,
3033 #[serde(alias = "product-name")]
3035 #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
3036 pub product_name: Option<String>,
3037 #[serde(alias = "main-binary-name")]
3051 pub main_binary_name: Option<String>,
3052 #[serde(deserialize_with = "version_deserializer", default)]
3068 pub version: Option<String>,
3069 pub identifier: String,
3075 #[serde(default)]
3077 pub app: AppConfig,
3078 #[serde(default)]
3080 pub build: BuildConfig,
3081 #[serde(default)]
3083 pub bundle: BundleConfig,
3084 #[serde(default)]
3086 pub plugins: PluginConfig,
3087}
3088
3089#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
3093#[cfg_attr(feature = "schema", derive(JsonSchema))]
3094pub struct PluginConfig(pub HashMap<String, JsonValue>);
3095
3096#[cfg(feature = "build")]
3102mod build {
3103 use super::*;
3104 use crate::{literal_struct, tokens::*};
3105 use proc_macro2::TokenStream;
3106 use quote::{quote, ToTokens, TokenStreamExt};
3107 use std::convert::identity;
3108
3109 impl ToTokens for WebviewUrl {
3110 fn to_tokens(&self, tokens: &mut TokenStream) {
3111 let prefix = quote! { ::tauri::utils::config::WebviewUrl };
3112
3113 tokens.append_all(match self {
3114 Self::App(path) => {
3115 let path = path_buf_lit(path);
3116 quote! { #prefix::App(#path) }
3117 }
3118 Self::External(url) => {
3119 let url = url_lit(url);
3120 quote! { #prefix::External(#url) }
3121 }
3122 Self::CustomProtocol(url) => {
3123 let url = url_lit(url);
3124 quote! { #prefix::CustomProtocol(#url) }
3125 }
3126 })
3127 }
3128 }
3129
3130 impl ToTokens for BackgroundThrottlingPolicy {
3131 fn to_tokens(&self, tokens: &mut TokenStream) {
3132 let prefix = quote! { ::tauri::utils::config::BackgroundThrottlingPolicy };
3133 tokens.append_all(match self {
3134 Self::Disabled => quote! { #prefix::Disabled },
3135 Self::Throttle => quote! { #prefix::Throttle },
3136 Self::Suspend => quote! { #prefix::Suspend },
3137 })
3138 }
3139 }
3140
3141 impl ToTokens for crate::Theme {
3142 fn to_tokens(&self, tokens: &mut TokenStream) {
3143 let prefix = quote! { ::tauri::utils::Theme };
3144
3145 tokens.append_all(match self {
3146 Self::Light => quote! { #prefix::Light },
3147 Self::Dark => quote! { #prefix::Dark },
3148 })
3149 }
3150 }
3151
3152 impl ToTokens for Color {
3153 fn to_tokens(&self, tokens: &mut TokenStream) {
3154 let Color(r, g, b, a) = self;
3155 tokens.append_all(quote! {::tauri::utils::config::Color(#r,#g,#b,#a)});
3156 }
3157 }
3158 impl ToTokens for WindowEffectsConfig {
3159 fn to_tokens(&self, tokens: &mut TokenStream) {
3160 let effects = vec_lit(self.effects.clone(), |d| d);
3161 let state = opt_lit(self.state.as_ref());
3162 let radius = opt_lit(self.radius.as_ref());
3163 let color = opt_lit(self.color.as_ref());
3164
3165 literal_struct!(
3166 tokens,
3167 ::tauri::utils::config::WindowEffectsConfig,
3168 effects,
3169 state,
3170 radius,
3171 color
3172 )
3173 }
3174 }
3175
3176 impl ToTokens for crate::TitleBarStyle {
3177 fn to_tokens(&self, tokens: &mut TokenStream) {
3178 let prefix = quote! { ::tauri::utils::TitleBarStyle };
3179
3180 tokens.append_all(match self {
3181 Self::Visible => quote! { #prefix::Visible },
3182 Self::Transparent => quote! { #prefix::Transparent },
3183 Self::Overlay => quote! { #prefix::Overlay },
3184 })
3185 }
3186 }
3187
3188 impl ToTokens for LogicalPosition {
3189 fn to_tokens(&self, tokens: &mut TokenStream) {
3190 let LogicalPosition { x, y } = self;
3191 literal_struct!(tokens, ::tauri::utils::config::LogicalPosition, x, y)
3192 }
3193 }
3194
3195 impl ToTokens for crate::WindowEffect {
3196 fn to_tokens(&self, tokens: &mut TokenStream) {
3197 let prefix = quote! { ::tauri::utils::WindowEffect };
3198
3199 #[allow(deprecated)]
3200 tokens.append_all(match self {
3201 WindowEffect::AppearanceBased => quote! { #prefix::AppearanceBased},
3202 WindowEffect::Light => quote! { #prefix::Light},
3203 WindowEffect::Dark => quote! { #prefix::Dark},
3204 WindowEffect::MediumLight => quote! { #prefix::MediumLight},
3205 WindowEffect::UltraDark => quote! { #prefix::UltraDark},
3206 WindowEffect::Titlebar => quote! { #prefix::Titlebar},
3207 WindowEffect::Selection => quote! { #prefix::Selection},
3208 WindowEffect::Menu => quote! { #prefix::Menu},
3209 WindowEffect::Popover => quote! { #prefix::Popover},
3210 WindowEffect::Sidebar => quote! { #prefix::Sidebar},
3211 WindowEffect::HeaderView => quote! { #prefix::HeaderView},
3212 WindowEffect::Sheet => quote! { #prefix::Sheet},
3213 WindowEffect::WindowBackground => quote! { #prefix::WindowBackground},
3214 WindowEffect::HudWindow => quote! { #prefix::HudWindow},
3215 WindowEffect::FullScreenUI => quote! { #prefix::FullScreenUI},
3216 WindowEffect::Tooltip => quote! { #prefix::Tooltip},
3217 WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
3218 WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
3219 WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
3220 WindowEffect::Mica => quote! { #prefix::Mica},
3221 WindowEffect::MicaDark => quote! { #prefix::MicaDark},
3222 WindowEffect::MicaLight => quote! { #prefix::MicaLight},
3223 WindowEffect::Blur => quote! { #prefix::Blur},
3224 WindowEffect::Acrylic => quote! { #prefix::Acrylic},
3225 WindowEffect::Tabbed => quote! { #prefix::Tabbed },
3226 WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
3227 WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
3228 })
3229 }
3230 }
3231
3232 impl ToTokens for crate::WindowEffectState {
3233 fn to_tokens(&self, tokens: &mut TokenStream) {
3234 let prefix = quote! { ::tauri::utils::WindowEffectState };
3235
3236 #[allow(deprecated)]
3237 tokens.append_all(match self {
3238 WindowEffectState::Active => quote! { #prefix::Active},
3239 WindowEffectState::FollowsWindowActiveState => quote! { #prefix::FollowsWindowActiveState},
3240 WindowEffectState::Inactive => quote! { #prefix::Inactive},
3241 })
3242 }
3243 }
3244
3245 impl ToTokens for PreventOverflowMargin {
3246 fn to_tokens(&self, tokens: &mut TokenStream) {
3247 let width = self.width;
3248 let height = self.height;
3249
3250 literal_struct!(
3251 tokens,
3252 ::tauri::utils::config::PreventOverflowMargin,
3253 width,
3254 height
3255 )
3256 }
3257 }
3258
3259 impl ToTokens for PreventOverflowConfig {
3260 fn to_tokens(&self, tokens: &mut TokenStream) {
3261 let prefix = quote! { ::tauri::utils::config::PreventOverflowConfig };
3262
3263 #[allow(deprecated)]
3264 tokens.append_all(match self {
3265 Self::Enable(enable) => quote! { #prefix::Enable(#enable) },
3266 Self::Margin(margin) => quote! { #prefix::Margin(#margin) },
3267 })
3268 }
3269 }
3270
3271 impl ToTokens for WindowConfig {
3272 fn to_tokens(&self, tokens: &mut TokenStream) {
3273 let label = str_lit(&self.label);
3274 let create = &self.create;
3275 let url = &self.url;
3276 let user_agent = opt_str_lit(self.user_agent.as_ref());
3277 let drag_drop_enabled = self.drag_drop_enabled;
3278 let center = self.center;
3279 let x = opt_lit(self.x.as_ref());
3280 let y = opt_lit(self.y.as_ref());
3281 let width = self.width;
3282 let height = self.height;
3283 let min_width = opt_lit(self.min_width.as_ref());
3284 let min_height = opt_lit(self.min_height.as_ref());
3285 let max_width = opt_lit(self.max_width.as_ref());
3286 let max_height = opt_lit(self.max_height.as_ref());
3287 let prevent_overflow = opt_lit(self.prevent_overflow.as_ref());
3288 let resizable = self.resizable;
3289 let maximizable = self.maximizable;
3290 let minimizable = self.minimizable;
3291 let closable = self.closable;
3292 let title = str_lit(&self.title);
3293 let proxy_url = opt_lit(self.proxy_url.as_ref().map(url_lit).as_ref());
3294 let fullscreen = self.fullscreen;
3295 let focus = self.focus;
3296 let transparent = self.transparent;
3297 let maximized = self.maximized;
3298 let visible = self.visible;
3299 let decorations = self.decorations;
3300 let always_on_bottom = self.always_on_bottom;
3301 let always_on_top = self.always_on_top;
3302 let visible_on_all_workspaces = self.visible_on_all_workspaces;
3303 let content_protected = self.content_protected;
3304 let skip_taskbar = self.skip_taskbar;
3305 let window_classname = opt_str_lit(self.window_classname.as_ref());
3306 let theme = opt_lit(self.theme.as_ref());
3307 let title_bar_style = &self.title_bar_style;
3308 let traffic_light_position = opt_lit(self.traffic_light_position.as_ref());
3309 let hidden_title = self.hidden_title;
3310 let accept_first_mouse = self.accept_first_mouse;
3311 let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
3312 let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
3313 let shadow = self.shadow;
3314 let window_effects = opt_lit(self.window_effects.as_ref());
3315 let incognito = self.incognito;
3316 let parent = opt_str_lit(self.parent.as_ref());
3317 let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled;
3318 let browser_extensions_enabled = self.browser_extensions_enabled;
3319 let use_https_scheme = self.use_https_scheme;
3320 let devtools = opt_lit(self.devtools.as_ref());
3321 let background_color = opt_lit(self.background_color.as_ref());
3322 let background_throttling = opt_lit(self.background_throttling.as_ref());
3323 let javascript_disabled = self.javascript_disabled;
3324 let allow_link_preview = self.allow_link_preview;
3325 let disable_input_accessory_view = self.disable_input_accessory_view;
3326
3327 literal_struct!(
3328 tokens,
3329 ::tauri::utils::config::WindowConfig,
3330 label,
3331 url,
3332 create,
3333 user_agent,
3334 drag_drop_enabled,
3335 center,
3336 x,
3337 y,
3338 width,
3339 height,
3340 min_width,
3341 min_height,
3342 max_width,
3343 max_height,
3344 prevent_overflow,
3345 resizable,
3346 maximizable,
3347 minimizable,
3348 closable,
3349 title,
3350 proxy_url,
3351 fullscreen,
3352 focus,
3353 transparent,
3354 maximized,
3355 visible,
3356 decorations,
3357 always_on_bottom,
3358 always_on_top,
3359 visible_on_all_workspaces,
3360 content_protected,
3361 skip_taskbar,
3362 window_classname,
3363 theme,
3364 title_bar_style,
3365 traffic_light_position,
3366 hidden_title,
3367 accept_first_mouse,
3368 tabbing_identifier,
3369 additional_browser_args,
3370 shadow,
3371 window_effects,
3372 incognito,
3373 parent,
3374 zoom_hotkeys_enabled,
3375 browser_extensions_enabled,
3376 use_https_scheme,
3377 devtools,
3378 background_color,
3379 background_throttling,
3380 javascript_disabled,
3381 allow_link_preview,
3382 disable_input_accessory_view
3383 );
3384 }
3385 }
3386
3387 impl ToTokens for PatternKind {
3388 fn to_tokens(&self, tokens: &mut TokenStream) {
3389 let prefix = quote! { ::tauri::utils::config::PatternKind };
3390
3391 tokens.append_all(match self {
3392 Self::Brownfield => quote! { #prefix::Brownfield },
3393 #[cfg(not(feature = "isolation"))]
3394 Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
3395 #[cfg(feature = "isolation")]
3396 Self::Isolation { dir } => {
3397 let dir = path_buf_lit(dir);
3398 quote! { #prefix::Isolation { dir: #dir } }
3399 }
3400 })
3401 }
3402 }
3403
3404 impl ToTokens for WebviewInstallMode {
3405 fn to_tokens(&self, tokens: &mut TokenStream) {
3406 let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
3407
3408 tokens.append_all(match self {
3409 Self::Skip => quote! { #prefix::Skip },
3410 Self::DownloadBootstrapper { silent } => {
3411 quote! { #prefix::DownloadBootstrapper { silent: #silent } }
3412 }
3413 Self::EmbedBootstrapper { silent } => {
3414 quote! { #prefix::EmbedBootstrapper { silent: #silent } }
3415 }
3416 Self::OfflineInstaller { silent } => {
3417 quote! { #prefix::OfflineInstaller { silent: #silent } }
3418 }
3419 Self::FixedRuntime { path } => {
3420 let path = path_buf_lit(path);
3421 quote! { #prefix::FixedRuntime { path: #path } }
3422 }
3423 })
3424 }
3425 }
3426
3427 impl ToTokens for WindowsConfig {
3428 fn to_tokens(&self, tokens: &mut TokenStream) {
3429 let webview_install_mode = &self.webview_install_mode;
3430 tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
3431 webview_install_mode: #webview_install_mode,
3432 ..Default::default()
3433 }})
3434 }
3435 }
3436
3437 impl ToTokens for BundleConfig {
3438 fn to_tokens(&self, tokens: &mut TokenStream) {
3439 let publisher = quote!(None);
3440 let homepage = quote!(None);
3441 let icon = vec_lit(&self.icon, str_lit);
3442 let active = self.active;
3443 let targets = quote!(Default::default());
3444 let create_updater_artifacts = quote!(Default::default());
3445 let resources = quote!(None);
3446 let copyright = quote!(None);
3447 let category = quote!(None);
3448 let file_associations = quote!(None);
3449 let short_description = quote!(None);
3450 let long_description = quote!(None);
3451 let use_local_tools_dir = self.use_local_tools_dir;
3452 let external_bin = opt_vec_lit(self.external_bin.as_ref(), str_lit);
3453 let windows = &self.windows;
3454 let license = opt_str_lit(self.license.as_ref());
3455 let license_file = opt_lit(self.license_file.as_ref().map(path_buf_lit).as_ref());
3456 let linux = quote!(Default::default());
3457 let macos = quote!(Default::default());
3458 let ios = quote!(Default::default());
3459 let android = quote!(Default::default());
3460
3461 literal_struct!(
3462 tokens,
3463 ::tauri::utils::config::BundleConfig,
3464 active,
3465 publisher,
3466 homepage,
3467 icon,
3468 targets,
3469 create_updater_artifacts,
3470 resources,
3471 copyright,
3472 category,
3473 license,
3474 license_file,
3475 file_associations,
3476 short_description,
3477 long_description,
3478 use_local_tools_dir,
3479 external_bin,
3480 windows,
3481 linux,
3482 macos,
3483 ios,
3484 android
3485 );
3486 }
3487 }
3488
3489 impl ToTokens for FrontendDist {
3490 fn to_tokens(&self, tokens: &mut TokenStream) {
3491 let prefix = quote! { ::tauri::utils::config::FrontendDist };
3492
3493 tokens.append_all(match self {
3494 Self::Url(url) => {
3495 let url = url_lit(url);
3496 quote! { #prefix::Url(#url) }
3497 }
3498 Self::Directory(path) => {
3499 let path = path_buf_lit(path);
3500 quote! { #prefix::Directory(#path) }
3501 }
3502 Self::Files(files) => {
3503 let files = vec_lit(files, path_buf_lit);
3504 quote! { #prefix::Files(#files) }
3505 }
3506 })
3507 }
3508 }
3509
3510 impl ToTokens for RunnerConfig {
3511 fn to_tokens(&self, tokens: &mut TokenStream) {
3512 let prefix = quote! { ::tauri::utils::config::RunnerConfig };
3513
3514 tokens.append_all(match self {
3515 Self::String(cmd) => {
3516 let cmd = cmd.as_str();
3517 quote!(#prefix::String(#cmd.into()))
3518 }
3519 Self::Object { cmd, cwd, args } => {
3520 let cmd = cmd.as_str();
3521 let cwd = opt_str_lit(cwd.as_ref());
3522 let args = opt_lit(args.as_ref().map(|v| vec_lit(v, str_lit)).as_ref());
3523 quote!(#prefix::Object {
3524 cmd: #cmd.into(),
3525 cwd: #cwd,
3526 args: #args,
3527 })
3528 }
3529 })
3530 }
3531 }
3532
3533 impl ToTokens for BuildConfig {
3534 fn to_tokens(&self, tokens: &mut TokenStream) {
3535 let dev_url = opt_lit(self.dev_url.as_ref().map(url_lit).as_ref());
3536 let frontend_dist = opt_lit(self.frontend_dist.as_ref());
3537 let runner = opt_lit(self.runner.as_ref());
3538 let before_dev_command = quote!(None);
3539 let before_build_command = quote!(None);
3540 let before_bundle_command = quote!(None);
3541 let features = quote!(None);
3542 let remove_unused_commands = quote!(false);
3543
3544 literal_struct!(
3545 tokens,
3546 ::tauri::utils::config::BuildConfig,
3547 runner,
3548 dev_url,
3549 frontend_dist,
3550 before_dev_command,
3551 before_build_command,
3552 before_bundle_command,
3553 features,
3554 remove_unused_commands
3555 );
3556 }
3557 }
3558
3559 impl ToTokens for CspDirectiveSources {
3560 fn to_tokens(&self, tokens: &mut TokenStream) {
3561 let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
3562
3563 tokens.append_all(match self {
3564 Self::Inline(sources) => {
3565 let sources = sources.as_str();
3566 quote!(#prefix::Inline(#sources.into()))
3567 }
3568 Self::List(list) => {
3569 let list = vec_lit(list, str_lit);
3570 quote!(#prefix::List(#list))
3571 }
3572 })
3573 }
3574 }
3575
3576 impl ToTokens for Csp {
3577 fn to_tokens(&self, tokens: &mut TokenStream) {
3578 let prefix = quote! { ::tauri::utils::config::Csp };
3579
3580 tokens.append_all(match self {
3581 Self::Policy(policy) => {
3582 let policy = policy.as_str();
3583 quote!(#prefix::Policy(#policy.into()))
3584 }
3585 Self::DirectiveMap(list) => {
3586 let map = map_lit(
3587 quote! { ::std::collections::HashMap },
3588 list,
3589 str_lit,
3590 identity,
3591 );
3592 quote!(#prefix::DirectiveMap(#map))
3593 }
3594 })
3595 }
3596 }
3597
3598 impl ToTokens for DisabledCspModificationKind {
3599 fn to_tokens(&self, tokens: &mut TokenStream) {
3600 let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
3601
3602 tokens.append_all(match self {
3603 Self::Flag(flag) => {
3604 quote! { #prefix::Flag(#flag) }
3605 }
3606 Self::List(directives) => {
3607 let directives = vec_lit(directives, str_lit);
3608 quote! { #prefix::List(#directives) }
3609 }
3610 });
3611 }
3612 }
3613
3614 impl ToTokens for CapabilityEntry {
3615 fn to_tokens(&self, tokens: &mut TokenStream) {
3616 let prefix = quote! { ::tauri::utils::config::CapabilityEntry };
3617
3618 tokens.append_all(match self {
3619 Self::Inlined(capability) => {
3620 quote! { #prefix::Inlined(#capability) }
3621 }
3622 Self::Reference(id) => {
3623 let id = str_lit(id);
3624 quote! { #prefix::Reference(#id) }
3625 }
3626 });
3627 }
3628 }
3629
3630 impl ToTokens for HeaderSource {
3631 fn to_tokens(&self, tokens: &mut TokenStream) {
3632 let prefix = quote! { ::tauri::utils::config::HeaderSource };
3633
3634 tokens.append_all(match self {
3635 Self::Inline(s) => {
3636 let line = s.as_str();
3637 quote!(#prefix::Inline(#line.into()))
3638 }
3639 Self::List(l) => {
3640 let list = vec_lit(l, str_lit);
3641 quote!(#prefix::List(#list))
3642 }
3643 Self::Map(m) => {
3644 let map = map_lit(quote! { ::std::collections::HashMap }, m, str_lit, str_lit);
3645 quote!(#prefix::Map(#map))
3646 }
3647 })
3648 }
3649 }
3650
3651 impl ToTokens for HeaderConfig {
3652 fn to_tokens(&self, tokens: &mut TokenStream) {
3653 let access_control_allow_credentials =
3654 opt_lit(self.access_control_allow_credentials.as_ref());
3655 let access_control_allow_headers = opt_lit(self.access_control_allow_headers.as_ref());
3656 let access_control_allow_methods = opt_lit(self.access_control_allow_methods.as_ref());
3657 let access_control_expose_headers = opt_lit(self.access_control_expose_headers.as_ref());
3658 let access_control_max_age = opt_lit(self.access_control_max_age.as_ref());
3659 let cross_origin_embedder_policy = opt_lit(self.cross_origin_embedder_policy.as_ref());
3660 let cross_origin_opener_policy = opt_lit(self.cross_origin_opener_policy.as_ref());
3661 let cross_origin_resource_policy = opt_lit(self.cross_origin_resource_policy.as_ref());
3662 let permissions_policy = opt_lit(self.permissions_policy.as_ref());
3663 let service_worker_allowed = opt_lit(self.service_worker_allowed.as_ref());
3664 let timing_allow_origin = opt_lit(self.timing_allow_origin.as_ref());
3665 let x_content_type_options = opt_lit(self.x_content_type_options.as_ref());
3666 let tauri_custom_header = opt_lit(self.tauri_custom_header.as_ref());
3667
3668 literal_struct!(
3669 tokens,
3670 ::tauri::utils::config::HeaderConfig,
3671 access_control_allow_credentials,
3672 access_control_allow_headers,
3673 access_control_allow_methods,
3674 access_control_expose_headers,
3675 access_control_max_age,
3676 cross_origin_embedder_policy,
3677 cross_origin_opener_policy,
3678 cross_origin_resource_policy,
3679 permissions_policy,
3680 service_worker_allowed,
3681 timing_allow_origin,
3682 x_content_type_options,
3683 tauri_custom_header
3684 );
3685 }
3686 }
3687
3688 impl ToTokens for SecurityConfig {
3689 fn to_tokens(&self, tokens: &mut TokenStream) {
3690 let csp = opt_lit(self.csp.as_ref());
3691 let dev_csp = opt_lit(self.dev_csp.as_ref());
3692 let freeze_prototype = self.freeze_prototype;
3693 let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
3694 let asset_protocol = &self.asset_protocol;
3695 let pattern = &self.pattern;
3696 let capabilities = vec_lit(&self.capabilities, identity);
3697 let headers = opt_lit(self.headers.as_ref());
3698
3699 literal_struct!(
3700 tokens,
3701 ::tauri::utils::config::SecurityConfig,
3702 csp,
3703 dev_csp,
3704 freeze_prototype,
3705 dangerous_disable_asset_csp_modification,
3706 asset_protocol,
3707 pattern,
3708 capabilities,
3709 headers
3710 );
3711 }
3712 }
3713
3714 impl ToTokens for TrayIconConfig {
3715 fn to_tokens(&self, tokens: &mut TokenStream) {
3716 tokens.append_all(quote!(#[allow(deprecated)]));
3718
3719 let id = opt_str_lit(self.id.as_ref());
3720 let icon_as_template = self.icon_as_template;
3721 #[allow(deprecated)]
3722 let menu_on_left_click = self.menu_on_left_click;
3723 let show_menu_on_left_click = self.show_menu_on_left_click;
3724 let icon_path = path_buf_lit(&self.icon_path);
3725 let title = opt_str_lit(self.title.as_ref());
3726 let tooltip = opt_str_lit(self.tooltip.as_ref());
3727 literal_struct!(
3728 tokens,
3729 ::tauri::utils::config::TrayIconConfig,
3730 id,
3731 icon_path,
3732 icon_as_template,
3733 menu_on_left_click,
3734 show_menu_on_left_click,
3735 title,
3736 tooltip
3737 );
3738 }
3739 }
3740
3741 impl ToTokens for FsScope {
3742 fn to_tokens(&self, tokens: &mut TokenStream) {
3743 let prefix = quote! { ::tauri::utils::config::FsScope };
3744
3745 tokens.append_all(match self {
3746 Self::AllowedPaths(allow) => {
3747 let allowed_paths = vec_lit(allow, path_buf_lit);
3748 quote! { #prefix::AllowedPaths(#allowed_paths) }
3749 }
3750 Self::Scope { allow, deny , require_literal_leading_dot} => {
3751 let allow = vec_lit(allow, path_buf_lit);
3752 let deny = vec_lit(deny, path_buf_lit);
3753 let require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref());
3754 quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
3755 }
3756 });
3757 }
3758 }
3759
3760 impl ToTokens for AssetProtocolConfig {
3761 fn to_tokens(&self, tokens: &mut TokenStream) {
3762 let scope = &self.scope;
3763 tokens.append_all(quote! { ::tauri::utils::config::AssetProtocolConfig { scope: #scope, ..Default::default() } })
3764 }
3765 }
3766
3767 impl ToTokens for AppConfig {
3768 fn to_tokens(&self, tokens: &mut TokenStream) {
3769 let windows = vec_lit(&self.windows, identity);
3770 let security = &self.security;
3771 let tray_icon = opt_lit(self.tray_icon.as_ref());
3772 let macos_private_api = self.macos_private_api;
3773 let with_global_tauri = self.with_global_tauri;
3774 let enable_gtk_app_id = self.enable_gtk_app_id;
3775
3776 literal_struct!(
3777 tokens,
3778 ::tauri::utils::config::AppConfig,
3779 windows,
3780 security,
3781 tray_icon,
3782 macos_private_api,
3783 with_global_tauri,
3784 enable_gtk_app_id
3785 );
3786 }
3787 }
3788
3789 impl ToTokens for PluginConfig {
3790 fn to_tokens(&self, tokens: &mut TokenStream) {
3791 let config = map_lit(
3792 quote! { ::std::collections::HashMap },
3793 &self.0,
3794 str_lit,
3795 json_value_lit,
3796 );
3797 tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
3798 }
3799 }
3800
3801 impl ToTokens for Config {
3802 fn to_tokens(&self, tokens: &mut TokenStream) {
3803 let schema = quote!(None);
3804 let product_name = opt_str_lit(self.product_name.as_ref());
3805 let main_binary_name = opt_str_lit(self.main_binary_name.as_ref());
3806 let version = opt_str_lit(self.version.as_ref());
3807 let identifier = str_lit(&self.identifier);
3808 let app = &self.app;
3809 let build = &self.build;
3810 let bundle = &self.bundle;
3811 let plugins = &self.plugins;
3812
3813 literal_struct!(
3814 tokens,
3815 ::tauri::utils::config::Config,
3816 schema,
3817 product_name,
3818 main_binary_name,
3819 version,
3820 identifier,
3821 app,
3822 build,
3823 bundle,
3824 plugins
3825 );
3826 }
3827 }
3828}
3829
3830#[cfg(test)]
3831mod test {
3832 use super::*;
3833
3834 #[test]
3837 fn test_defaults() {
3839 let a_config = AppConfig::default();
3841 let b_config = BuildConfig::default();
3843 let d_windows: Vec<WindowConfig> = vec![];
3845 let d_bundle = BundleConfig::default();
3847
3848 let app = AppConfig {
3850 windows: vec![],
3851 security: SecurityConfig {
3852 csp: None,
3853 dev_csp: None,
3854 freeze_prototype: false,
3855 dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
3856 asset_protocol: AssetProtocolConfig::default(),
3857 pattern: Default::default(),
3858 capabilities: Vec::new(),
3859 headers: None,
3860 },
3861 tray_icon: None,
3862 macos_private_api: false,
3863 with_global_tauri: false,
3864 enable_gtk_app_id: false,
3865 };
3866
3867 let build = BuildConfig {
3869 runner: None,
3870 dev_url: None,
3871 frontend_dist: None,
3872 before_dev_command: None,
3873 before_build_command: None,
3874 before_bundle_command: None,
3875 features: None,
3876 remove_unused_commands: false,
3877 };
3878
3879 let bundle = BundleConfig {
3881 active: false,
3882 targets: Default::default(),
3883 create_updater_artifacts: Default::default(),
3884 publisher: None,
3885 homepage: None,
3886 icon: Vec::new(),
3887 resources: None,
3888 copyright: None,
3889 category: None,
3890 file_associations: None,
3891 short_description: None,
3892 long_description: None,
3893 use_local_tools_dir: false,
3894 license: None,
3895 license_file: None,
3896 linux: Default::default(),
3897 macos: Default::default(),
3898 external_bin: None,
3899 windows: Default::default(),
3900 ios: Default::default(),
3901 android: Default::default(),
3902 };
3903
3904 assert_eq!(a_config, app);
3906 assert_eq!(b_config, build);
3907 assert_eq!(d_bundle, bundle);
3908 assert_eq!(d_windows, app.windows);
3909 }
3910
3911 #[test]
3912 fn parse_hex_color() {
3913 use super::Color;
3914
3915 assert_eq!(Color(255, 255, 255, 255), "fff".parse().unwrap());
3916 assert_eq!(Color(255, 255, 255, 255), "#fff".parse().unwrap());
3917 assert_eq!(Color(0, 0, 0, 255), "#000000".parse().unwrap());
3918 assert_eq!(Color(0, 0, 0, 255), "#000000ff".parse().unwrap());
3919 assert_eq!(Color(0, 255, 0, 255), "#00ff00ff".parse().unwrap());
3920 }
3921
3922 #[test]
3923 fn test_runner_config_string_format() {
3924 use super::RunnerConfig;
3925
3926 let json = r#""cargo""#;
3928 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
3929
3930 assert_eq!(runner.cmd(), "cargo");
3931 assert_eq!(runner.cwd(), None);
3932 assert_eq!(runner.args(), None);
3933
3934 let serialized = serde_json::to_string(&runner).unwrap();
3936 assert_eq!(serialized, r#""cargo""#);
3937 }
3938
3939 #[test]
3940 fn test_runner_config_object_format_full() {
3941 use super::RunnerConfig;
3942
3943 let json = r#"{"cmd": "my_runner", "cwd": "/tmp/build", "args": ["--quiet", "--verbose"]}"#;
3945 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
3946
3947 assert_eq!(runner.cmd(), "my_runner");
3948 assert_eq!(runner.cwd(), Some("/tmp/build"));
3949 assert_eq!(
3950 runner.args(),
3951 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
3952 );
3953
3954 let serialized = serde_json::to_string(&runner).unwrap();
3956 let deserialized: RunnerConfig = serde_json::from_str(&serialized).unwrap();
3957 assert_eq!(runner, deserialized);
3958 }
3959
3960 #[test]
3961 fn test_runner_config_object_format_minimal() {
3962 use super::RunnerConfig;
3963
3964 let json = r#"{"cmd": "cross"}"#;
3966 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
3967
3968 assert_eq!(runner.cmd(), "cross");
3969 assert_eq!(runner.cwd(), None);
3970 assert_eq!(runner.args(), None);
3971 }
3972
3973 #[test]
3974 fn test_runner_config_default() {
3975 use super::RunnerConfig;
3976
3977 let default_runner = RunnerConfig::default();
3978 assert_eq!(default_runner.cmd(), "cargo");
3979 assert_eq!(default_runner.cwd(), None);
3980 assert_eq!(default_runner.args(), None);
3981 }
3982
3983 #[test]
3984 fn test_runner_config_from_str() {
3985 use super::RunnerConfig;
3986
3987 let runner: RunnerConfig = "my_runner".into();
3989 assert_eq!(runner.cmd(), "my_runner");
3990 assert_eq!(runner.cwd(), None);
3991 assert_eq!(runner.args(), None);
3992 }
3993
3994 #[test]
3995 fn test_runner_config_from_string() {
3996 use super::RunnerConfig;
3997
3998 let runner: RunnerConfig = "another_runner".to_string().into();
4000 assert_eq!(runner.cmd(), "another_runner");
4001 assert_eq!(runner.cwd(), None);
4002 assert_eq!(runner.args(), None);
4003 }
4004
4005 #[test]
4006 fn test_runner_config_from_str_parse() {
4007 use super::RunnerConfig;
4008 use std::str::FromStr;
4009
4010 let runner = RunnerConfig::from_str("parsed_runner").unwrap();
4012 assert_eq!(runner.cmd(), "parsed_runner");
4013 assert_eq!(runner.cwd(), None);
4014 assert_eq!(runner.args(), None);
4015 }
4016
4017 #[test]
4018 fn test_runner_config_in_build_config() {
4019 use super::BuildConfig;
4020
4021 let json = r#"{"runner": "cargo"}"#;
4023 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4024
4025 let runner = build_config.runner.unwrap();
4026 assert_eq!(runner.cmd(), "cargo");
4027 assert_eq!(runner.cwd(), None);
4028 assert_eq!(runner.args(), None);
4029 }
4030
4031 #[test]
4032 fn test_runner_config_in_build_config_object() {
4033 use super::BuildConfig;
4034
4035 let json = r#"{"runner": {"cmd": "cross", "cwd": "/workspace", "args": ["--target", "x86_64-unknown-linux-gnu"]}}"#;
4037 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4038
4039 let runner = build_config.runner.unwrap();
4040 assert_eq!(runner.cmd(), "cross");
4041 assert_eq!(runner.cwd(), Some("/workspace"));
4042 assert_eq!(
4043 runner.args(),
4044 Some(
4045 &[
4046 "--target".to_string(),
4047 "x86_64-unknown-linux-gnu".to_string()
4048 ][..]
4049 )
4050 );
4051 }
4052
4053 #[test]
4054 fn test_runner_config_in_full_config() {
4055 use super::Config;
4056
4057 let json = r#"{
4059 "productName": "Test App",
4060 "version": "1.0.0",
4061 "identifier": "com.test.app",
4062 "build": {
4063 "runner": {
4064 "cmd": "my_custom_cargo",
4065 "cwd": "/tmp/build",
4066 "args": ["--quiet", "--verbose"]
4067 }
4068 }
4069 }"#;
4070
4071 let config: Config = serde_json::from_str(json).unwrap();
4072 let runner = config.build.runner.unwrap();
4073
4074 assert_eq!(runner.cmd(), "my_custom_cargo");
4075 assert_eq!(runner.cwd(), Some("/tmp/build"));
4076 assert_eq!(
4077 runner.args(),
4078 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4079 );
4080 }
4081
4082 #[test]
4083 fn test_runner_config_equality() {
4084 use super::RunnerConfig;
4085
4086 let runner1 = RunnerConfig::String("cargo".to_string());
4087 let runner2 = RunnerConfig::String("cargo".to_string());
4088 let runner3 = RunnerConfig::String("cross".to_string());
4089
4090 assert_eq!(runner1, runner2);
4091 assert_ne!(runner1, runner3);
4092
4093 let runner4 = RunnerConfig::Object {
4094 cmd: "cargo".to_string(),
4095 cwd: Some("/tmp".to_string()),
4096 args: Some(vec!["--quiet".to_string()]),
4097 };
4098 let runner5 = RunnerConfig::Object {
4099 cmd: "cargo".to_string(),
4100 cwd: Some("/tmp".to_string()),
4101 args: Some(vec!["--quiet".to_string()]),
4102 };
4103
4104 assert_eq!(runner4, runner5);
4105 assert_ne!(runner1, runner4);
4106 }
4107
4108 #[test]
4109 fn test_runner_config_untagged_serialization() {
4110 use super::RunnerConfig;
4111
4112 let string_runner = RunnerConfig::String("cargo".to_string());
4114 let string_json = serde_json::to_string(&string_runner).unwrap();
4115 assert_eq!(string_json, r#""cargo""#);
4116
4117 let object_runner = RunnerConfig::Object {
4119 cmd: "cross".to_string(),
4120 cwd: None,
4121 args: None,
4122 };
4123 let object_json = serde_json::to_string(&object_runner).unwrap();
4124 assert!(object_json.contains("\"cmd\":\"cross\""));
4125 assert!(object_json.contains("\"cwd\":null") || !object_json.contains("cwd"));
4127 assert!(object_json.contains("\"args\":null") || !object_json.contains("args"));
4128 }
4129}