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 "14.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 #[serde(default, alias = "fips-compliant")]
794 pub fips_compliant: bool,
795}
796
797#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
801#[cfg_attr(feature = "schema", derive(JsonSchema))]
802#[serde(rename_all = "camelCase", deny_unknown_fields)]
803pub enum NsisCompression {
804 Zlib,
806 Bzip2,
808 Lzma,
810 None,
812}
813
814impl Default for NsisCompression {
815 fn default() -> Self {
816 Self::Lzma
817 }
818}
819
820#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
822#[serde(rename_all = "camelCase", deny_unknown_fields)]
823#[cfg_attr(feature = "schema", derive(JsonSchema))]
824pub enum NSISInstallerMode {
825 CurrentUser,
831 PerMachine,
836 Both,
842}
843
844impl Default for NSISInstallerMode {
845 fn default() -> Self {
846 Self::CurrentUser
847 }
848}
849
850#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
852#[cfg_attr(feature = "schema", derive(JsonSchema))]
853#[serde(rename_all = "camelCase", deny_unknown_fields)]
854pub struct NsisConfig {
855 pub template: Option<PathBuf>,
857 #[serde(alias = "header-image")]
861 pub header_image: Option<PathBuf>,
862 #[serde(alias = "sidebar-image")]
866 pub sidebar_image: Option<PathBuf>,
867 #[serde(alias = "install-icon")]
869 pub installer_icon: Option<PathBuf>,
870 #[serde(default, alias = "install-mode")]
872 pub install_mode: NSISInstallerMode,
873 pub languages: Option<Vec<String>>,
879 pub custom_language_files: Option<HashMap<String, PathBuf>>,
886 #[serde(default, alias = "display-language-selector")]
889 pub display_language_selector: bool,
890 #[serde(default)]
894 pub compression: NsisCompression,
895 #[serde(alias = "start-menu-folder")]
904 pub start_menu_folder: Option<String>,
905 #[serde(alias = "installer-hooks")]
936 pub installer_hooks: Option<PathBuf>,
937 #[serde(alias = "minimum-webview2-version")]
941 pub minimum_webview2_version: Option<String>,
942}
943
944#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
949#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
950#[cfg_attr(feature = "schema", derive(JsonSchema))]
951pub enum WebviewInstallMode {
952 Skip,
954 DownloadBootstrapper {
958 #[serde(default = "default_true")]
960 silent: bool,
961 },
962 EmbedBootstrapper {
966 #[serde(default = "default_true")]
968 silent: bool,
969 },
970 OfflineInstaller {
974 #[serde(default = "default_true")]
976 silent: bool,
977 },
978 FixedRuntime {
981 path: PathBuf,
986 },
987}
988
989impl Default for WebviewInstallMode {
990 fn default() -> Self {
991 Self::DownloadBootstrapper { silent: true }
992 }
993}
994
995#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
997#[cfg_attr(feature = "schema", derive(JsonSchema))]
998#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
999pub enum CustomSignCommandConfig {
1000 Command(String),
1009 CommandWithOptions {
1014 cmd: String,
1016 args: Vec<String>,
1020 },
1021}
1022
1023#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1027#[cfg_attr(feature = "schema", derive(JsonSchema))]
1028#[serde(rename_all = "camelCase", deny_unknown_fields)]
1029pub struct WindowsConfig {
1030 #[serde(alias = "digest-algorithm")]
1033 pub digest_algorithm: Option<String>,
1034 #[serde(alias = "certificate-thumbprint")]
1036 pub certificate_thumbprint: Option<String>,
1037 #[serde(alias = "timestamp-url")]
1039 pub timestamp_url: Option<String>,
1040 #[serde(default)]
1043 pub tsp: bool,
1044 #[serde(default, alias = "webview-install-mode")]
1046 pub webview_install_mode: WebviewInstallMode,
1047 #[serde(default = "default_true", alias = "allow-downgrades")]
1053 pub allow_downgrades: bool,
1054 pub wix: Option<WixConfig>,
1056 pub nsis: Option<NsisConfig>,
1058 #[serde(alias = "sign-command")]
1066 pub sign_command: Option<CustomSignCommandConfig>,
1067}
1068
1069impl Default for WindowsConfig {
1070 fn default() -> Self {
1071 Self {
1072 digest_algorithm: None,
1073 certificate_thumbprint: None,
1074 timestamp_url: None,
1075 tsp: false,
1076 webview_install_mode: Default::default(),
1077 allow_downgrades: true,
1078 wix: None,
1079 nsis: None,
1080 sign_command: None,
1081 }
1082 }
1083}
1084
1085#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1087#[cfg_attr(feature = "schema", derive(JsonSchema))]
1088pub enum BundleTypeRole {
1089 #[default]
1091 Editor,
1092 Viewer,
1094 Shell,
1096 QLGenerator,
1098 None,
1100}
1101
1102impl Display for BundleTypeRole {
1103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1104 match self {
1105 Self::Editor => write!(f, "Editor"),
1106 Self::Viewer => write!(f, "Viewer"),
1107 Self::Shell => write!(f, "Shell"),
1108 Self::QLGenerator => write!(f, "QLGenerator"),
1109 Self::None => write!(f, "None"),
1110 }
1111 }
1112}
1113
1114#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1118#[cfg_attr(feature = "schema", derive(JsonSchema))]
1119pub enum HandlerRank {
1120 #[default]
1122 Default,
1123 Owner,
1125 Alternate,
1127 None,
1129}
1130
1131impl Display for HandlerRank {
1132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1133 match self {
1134 Self::Default => write!(f, "Default"),
1135 Self::Owner => write!(f, "Owner"),
1136 Self::Alternate => write!(f, "Alternate"),
1137 Self::None => write!(f, "None"),
1138 }
1139 }
1140}
1141
1142#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
1146#[cfg_attr(feature = "schema", derive(JsonSchema))]
1147pub struct AssociationExt(pub String);
1148
1149impl fmt::Display for AssociationExt {
1150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1151 write!(f, "{}", self.0)
1152 }
1153}
1154
1155impl<'d> serde::Deserialize<'d> for AssociationExt {
1156 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
1157 let ext = String::deserialize(deserializer)?;
1158 if let Some(ext) = ext.strip_prefix('.') {
1159 Ok(AssociationExt(ext.into()))
1160 } else {
1161 Ok(AssociationExt(ext))
1162 }
1163 }
1164}
1165
1166#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1168#[cfg_attr(feature = "schema", derive(JsonSchema))]
1169#[serde(rename_all = "camelCase", deny_unknown_fields)]
1170pub struct FileAssociation {
1171 pub ext: Vec<AssociationExt>,
1173 pub name: Option<String>,
1175 pub description: Option<String>,
1177 #[serde(default)]
1179 pub role: BundleTypeRole,
1180 #[serde(alias = "mime-type")]
1182 pub mime_type: Option<String>,
1183 #[serde(default)]
1185 pub rank: HandlerRank,
1186}
1187
1188#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1190#[cfg_attr(feature = "schema", derive(JsonSchema))]
1191#[serde(rename_all = "camelCase", deny_unknown_fields)]
1192pub struct DeepLinkProtocol {
1193 pub schemes: Vec<String>,
1195 pub name: Option<String>,
1197 #[serde(default)]
1199 pub role: BundleTypeRole,
1200}
1201
1202#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1205#[cfg_attr(feature = "schema", derive(JsonSchema))]
1206#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1207pub enum BundleResources {
1208 List(Vec<String>),
1210 Map(HashMap<String, String>),
1212}
1213
1214impl BundleResources {
1215 pub fn push(&mut self, path: impl Into<String>) {
1217 match self {
1218 Self::List(l) => l.push(path.into()),
1219 Self::Map(l) => {
1220 let path = path.into();
1221 l.insert(path.clone(), path);
1222 }
1223 }
1224 }
1225}
1226
1227#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1229#[cfg_attr(feature = "schema", derive(JsonSchema))]
1230#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1231pub enum Updater {
1232 String(V1Compatible),
1234 Bool(bool),
1237}
1238
1239impl Default for Updater {
1240 fn default() -> Self {
1241 Self::Bool(false)
1242 }
1243}
1244
1245#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1247#[cfg_attr(feature = "schema", derive(JsonSchema))]
1248#[serde(rename_all = "camelCase", deny_unknown_fields)]
1249pub enum V1Compatible {
1250 V1Compatible,
1252}
1253
1254#[skip_serializing_none]
1258#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1259#[cfg_attr(feature = "schema", derive(JsonSchema))]
1260#[serde(rename_all = "camelCase", deny_unknown_fields)]
1261pub struct BundleConfig {
1262 #[serde(default)]
1264 pub active: bool,
1265 #[serde(default)]
1267 pub targets: BundleTarget,
1268 #[serde(default)]
1269 pub create_updater_artifacts: Updater,
1271 pub publisher: Option<String>,
1276 pub homepage: Option<String>,
1281 #[serde(default)]
1283 pub icon: Vec<String>,
1284 pub resources: Option<BundleResources>,
1288 pub copyright: Option<String>,
1290 pub license: Option<String>,
1293 #[serde(alias = "license-file")]
1295 pub license_file: Option<PathBuf>,
1296 pub category: Option<String>,
1301 pub file_associations: Option<Vec<FileAssociation>>,
1303 #[serde(alias = "short-description")]
1305 pub short_description: Option<String>,
1306 #[serde(alias = "long-description")]
1308 pub long_description: Option<String>,
1309 #[serde(default, alias = "use-local-tools-dir")]
1317 pub use_local_tools_dir: bool,
1318 #[serde(alias = "external-bin")]
1330 pub external_bin: Option<Vec<String>>,
1331 #[serde(default)]
1333 pub windows: WindowsConfig,
1334 #[serde(default)]
1336 pub linux: LinuxConfig,
1337 #[serde(rename = "macOS", alias = "macos", default)]
1339 pub macos: MacConfig,
1340 #[serde(rename = "iOS", alias = "ios", default)]
1342 pub ios: IosConfig,
1343 #[serde(default)]
1345 pub android: AndroidConfig,
1346}
1347
1348#[derive(Debug, PartialEq, Eq, Serialize, Default, Clone, Copy)]
1350#[serde(rename_all = "camelCase", deny_unknown_fields)]
1351pub struct Color(pub u8, pub u8, pub u8, pub u8);
1352
1353impl From<Color> for (u8, u8, u8, u8) {
1354 fn from(value: Color) -> Self {
1355 (value.0, value.1, value.2, value.3)
1356 }
1357}
1358
1359impl From<Color> for (u8, u8, u8) {
1360 fn from(value: Color) -> Self {
1361 (value.0, value.1, value.2)
1362 }
1363}
1364
1365impl From<(u8, u8, u8, u8)> for Color {
1366 fn from(value: (u8, u8, u8, u8)) -> Self {
1367 Color(value.0, value.1, value.2, value.3)
1368 }
1369}
1370
1371impl From<(u8, u8, u8)> for Color {
1372 fn from(value: (u8, u8, u8)) -> Self {
1373 Color(value.0, value.1, value.2, 255)
1374 }
1375}
1376
1377impl From<Color> for [u8; 4] {
1378 fn from(value: Color) -> Self {
1379 [value.0, value.1, value.2, value.3]
1380 }
1381}
1382
1383impl From<Color> for [u8; 3] {
1384 fn from(value: Color) -> Self {
1385 [value.0, value.1, value.2]
1386 }
1387}
1388
1389impl From<[u8; 4]> for Color {
1390 fn from(value: [u8; 4]) -> Self {
1391 Color(value[0], value[1], value[2], value[3])
1392 }
1393}
1394
1395impl From<[u8; 3]> for Color {
1396 fn from(value: [u8; 3]) -> Self {
1397 Color(value[0], value[1], value[2], 255)
1398 }
1399}
1400
1401impl FromStr for Color {
1402 type Err = String;
1403 fn from_str(mut color: &str) -> Result<Self, Self::Err> {
1404 color = color.trim().strip_prefix('#').unwrap_or(color);
1405 let color = match color.len() {
1406 3 => color.chars()
1408 .flat_map(|c| std::iter::repeat(c).take(2))
1409 .chain(std::iter::repeat('f').take(2))
1410 .collect(),
1411 6 => format!("{color}FF"),
1412 8 => color.to_string(),
1413 _ => return Err("Invalid hex color length, must be either 3, 6 or 8, for example: #fff, #ffffff, or #ffffffff".into()),
1414 };
1415
1416 let r = u8::from_str_radix(&color[0..2], 16).map_err(|e| e.to_string())?;
1417 let g = u8::from_str_radix(&color[2..4], 16).map_err(|e| e.to_string())?;
1418 let b = u8::from_str_radix(&color[4..6], 16).map_err(|e| e.to_string())?;
1419 let a = u8::from_str_radix(&color[6..8], 16).map_err(|e| e.to_string())?;
1420
1421 Ok(Color(r, g, b, a))
1422 }
1423}
1424
1425fn default_alpha() -> u8 {
1426 255
1427}
1428
1429#[derive(Deserialize)]
1430#[cfg_attr(feature = "schema", derive(JsonSchema))]
1431#[serde(untagged)]
1432enum InnerColor {
1433 String(String),
1435 Rgb((u8, u8, u8)),
1437 Rgba((u8, u8, u8, u8)),
1439 RgbaObject {
1441 red: u8,
1442 green: u8,
1443 blue: u8,
1444 #[serde(default = "default_alpha")]
1445 alpha: u8,
1446 },
1447}
1448
1449impl<'de> Deserialize<'de> for Color {
1450 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1451 where
1452 D: Deserializer<'de>,
1453 {
1454 let color = InnerColor::deserialize(deserializer)?;
1455 let color = match color {
1456 InnerColor::String(string) => string.parse().map_err(serde::de::Error::custom)?,
1457 InnerColor::Rgb(rgb) => Color(rgb.0, rgb.1, rgb.2, 255),
1458 InnerColor::Rgba(rgb) => rgb.into(),
1459 InnerColor::RgbaObject {
1460 red,
1461 green,
1462 blue,
1463 alpha,
1464 } => Color(red, green, blue, alpha),
1465 };
1466
1467 Ok(color)
1468 }
1469}
1470
1471#[cfg(feature = "schema")]
1472impl schemars::JsonSchema for Color {
1473 fn schema_name() -> String {
1474 "Color".to_string()
1475 }
1476
1477 fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1478 let mut schema = schemars::schema_for!(InnerColor).schema;
1479 schema.metadata = None; let any_of = schema.subschemas().any_of.as_mut().unwrap();
1483 let schemars::schema::Schema::Object(str_schema) = any_of.first_mut().unwrap() else {
1484 unreachable!()
1485 };
1486 str_schema.string().pattern = Some("^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$".into());
1487
1488 schema.into()
1489 }
1490}
1491
1492#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1494#[cfg_attr(feature = "schema", derive(JsonSchema))]
1495#[serde(rename_all = "camelCase", deny_unknown_fields)]
1496pub enum BackgroundThrottlingPolicy {
1497 Disabled,
1499 Suspend,
1501 Throttle,
1503}
1504
1505#[skip_serializing_none]
1507#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1508#[cfg_attr(feature = "schema", derive(JsonSchema))]
1509#[serde(rename_all = "camelCase", deny_unknown_fields)]
1510pub struct WindowEffectsConfig {
1511 pub effects: Vec<WindowEffect>,
1514 pub state: Option<WindowEffectState>,
1516 pub radius: Option<f64>,
1518 pub color: Option<Color>,
1521}
1522
1523#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1526#[cfg_attr(feature = "schema", derive(JsonSchema))]
1527#[serde(rename_all = "camelCase", deny_unknown_fields)]
1528pub struct PreventOverflowMargin {
1529 pub width: u32,
1531 pub height: u32,
1533}
1534
1535#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1537#[cfg_attr(feature = "schema", derive(JsonSchema))]
1538#[serde(untagged)]
1539pub enum PreventOverflowConfig {
1540 Enable(bool),
1542 Margin(PreventOverflowMargin),
1545}
1546
1547#[skip_serializing_none]
1551#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
1552#[cfg_attr(feature = "schema", derive(JsonSchema))]
1553#[serde(rename_all = "camelCase", deny_unknown_fields)]
1554pub struct WindowConfig {
1555 #[serde(default = "default_window_label")]
1557 pub label: String,
1558 #[serde(default = "default_true")]
1563 pub create: bool,
1564 #[serde(default)]
1566 pub url: WebviewUrl,
1567 #[serde(alias = "user-agent")]
1569 pub user_agent: Option<String>,
1570 #[serde(default = "default_true", alias = "drag-drop-enabled")]
1574 pub drag_drop_enabled: bool,
1575 #[serde(default)]
1577 pub center: bool,
1578 pub x: Option<f64>,
1580 pub y: Option<f64>,
1582 #[serde(default = "default_width")]
1584 pub width: f64,
1585 #[serde(default = "default_height")]
1587 pub height: f64,
1588 #[serde(alias = "min-width")]
1590 pub min_width: Option<f64>,
1591 #[serde(alias = "min-height")]
1593 pub min_height: Option<f64>,
1594 #[serde(alias = "max-width")]
1596 pub max_width: Option<f64>,
1597 #[serde(alias = "max-height")]
1599 pub max_height: Option<f64>,
1600 #[serde(alias = "prevent-overflow")]
1606 pub prevent_overflow: Option<PreventOverflowConfig>,
1607 #[serde(default = "default_true")]
1609 pub resizable: bool,
1610 #[serde(default = "default_true")]
1618 pub maximizable: bool,
1619 #[serde(default = "default_true")]
1625 pub minimizable: bool,
1626 #[serde(default = "default_true")]
1634 pub closable: bool,
1635 #[serde(default = "default_title")]
1637 pub title: String,
1638 #[serde(default)]
1640 pub fullscreen: bool,
1641 #[serde(default = "default_true")]
1643 pub focus: bool,
1644 #[serde(default = "default_true")]
1646 pub focusable: bool,
1647 #[serde(default)]
1652 pub transparent: bool,
1653 #[serde(default)]
1655 pub maximized: bool,
1656 #[serde(default = "default_true")]
1658 pub visible: bool,
1659 #[serde(default = "default_true")]
1661 pub decorations: bool,
1662 #[serde(default, alias = "always-on-bottom")]
1664 pub always_on_bottom: bool,
1665 #[serde(default, alias = "always-on-top")]
1667 pub always_on_top: bool,
1668 #[serde(default, alias = "visible-on-all-workspaces")]
1674 pub visible_on_all_workspaces: bool,
1675 #[serde(default, alias = "content-protected")]
1677 pub content_protected: bool,
1678 #[serde(default, alias = "skip-taskbar")]
1680 pub skip_taskbar: bool,
1681 pub window_classname: Option<String>,
1683 pub theme: Option<crate::Theme>,
1685 #[serde(default, alias = "title-bar-style")]
1687 pub title_bar_style: TitleBarStyle,
1688 #[serde(default, alias = "traffic-light-position")]
1692 pub traffic_light_position: Option<LogicalPosition>,
1693 #[serde(default, alias = "hidden-title")]
1695 pub hidden_title: bool,
1696 #[serde(default, alias = "accept-first-mouse")]
1698 pub accept_first_mouse: bool,
1699 #[serde(default, alias = "tabbing-identifier")]
1706 pub tabbing_identifier: Option<String>,
1707 #[serde(default, alias = "additional-browser-args")]
1710 pub additional_browser_args: Option<String>,
1711 #[serde(default = "default_true")]
1721 pub shadow: bool,
1722 #[serde(default, alias = "window-effects")]
1731 pub window_effects: Option<WindowEffectsConfig>,
1732 #[serde(default)]
1738 pub incognito: bool,
1739 pub parent: Option<String>,
1751 #[serde(alias = "proxy-url")]
1759 pub proxy_url: Option<Url>,
1760 #[serde(default, alias = "zoom-hotkeys-enabled")]
1770 pub zoom_hotkeys_enabled: bool,
1771 #[serde(default, alias = "browser-extensions-enabled")]
1778 pub browser_extensions_enabled: bool,
1779
1780 #[serde(default, alias = "use-https-scheme")]
1790 pub use_https_scheme: bool,
1791 pub devtools: Option<bool>,
1801
1802 #[serde(alias = "background-color")]
1810 pub background_color: Option<Color>,
1811
1812 #[serde(default, alias = "background-throttling")]
1827 pub background_throttling: Option<BackgroundThrottlingPolicy>,
1828 #[serde(default, alias = "javascript-disabled")]
1830 pub javascript_disabled: bool,
1831 #[serde(default = "default_true", alias = "allow-link-preview")]
1834 pub allow_link_preview: bool,
1835 #[serde(
1840 default,
1841 alias = "disable-input-accessory-view",
1842 alias = "disable_input_accessory_view"
1843 )]
1844 pub disable_input_accessory_view: bool,
1845}
1846
1847impl Default for WindowConfig {
1848 fn default() -> Self {
1849 Self {
1850 label: default_window_label(),
1851 url: WebviewUrl::default(),
1852 create: true,
1853 user_agent: None,
1854 drag_drop_enabled: true,
1855 center: false,
1856 x: None,
1857 y: None,
1858 width: default_width(),
1859 height: default_height(),
1860 min_width: None,
1861 min_height: None,
1862 max_width: None,
1863 max_height: None,
1864 prevent_overflow: None,
1865 resizable: true,
1866 maximizable: true,
1867 minimizable: true,
1868 closable: true,
1869 title: default_title(),
1870 fullscreen: false,
1871 focus: false,
1872 focusable: true,
1873 transparent: false,
1874 maximized: false,
1875 visible: true,
1876 decorations: true,
1877 always_on_bottom: false,
1878 always_on_top: false,
1879 visible_on_all_workspaces: false,
1880 content_protected: false,
1881 skip_taskbar: false,
1882 window_classname: None,
1883 theme: None,
1884 title_bar_style: Default::default(),
1885 traffic_light_position: None,
1886 hidden_title: false,
1887 accept_first_mouse: false,
1888 tabbing_identifier: None,
1889 additional_browser_args: None,
1890 shadow: true,
1891 window_effects: None,
1892 incognito: false,
1893 parent: None,
1894 proxy_url: None,
1895 zoom_hotkeys_enabled: false,
1896 browser_extensions_enabled: false,
1897 use_https_scheme: false,
1898 devtools: None,
1899 background_color: None,
1900 background_throttling: None,
1901 javascript_disabled: false,
1902 allow_link_preview: true,
1903 disable_input_accessory_view: false,
1904 }
1905 }
1906}
1907
1908fn default_window_label() -> String {
1909 "main".to_string()
1910}
1911
1912fn default_width() -> f64 {
1913 800f64
1914}
1915
1916fn default_height() -> f64 {
1917 600f64
1918}
1919
1920fn default_title() -> String {
1921 "Tauri App".to_string()
1922}
1923
1924#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1927#[cfg_attr(feature = "schema", derive(JsonSchema))]
1928#[serde(rename_all = "camelCase", untagged)]
1929pub enum CspDirectiveSources {
1930 Inline(String),
1932 List(Vec<String>),
1934}
1935
1936impl Default for CspDirectiveSources {
1937 fn default() -> Self {
1938 Self::List(Vec::new())
1939 }
1940}
1941
1942impl From<CspDirectiveSources> for Vec<String> {
1943 fn from(sources: CspDirectiveSources) -> Self {
1944 match sources {
1945 CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
1946 CspDirectiveSources::List(l) => l,
1947 }
1948 }
1949}
1950
1951impl CspDirectiveSources {
1952 pub fn contains(&self, source: &str) -> bool {
1954 match self {
1955 Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
1956 Self::List(l) => l.contains(&source.into()),
1957 }
1958 }
1959
1960 pub fn push<S: AsRef<str>>(&mut self, source: S) {
1962 match self {
1963 Self::Inline(s) => {
1964 s.push(' ');
1965 s.push_str(source.as_ref());
1966 }
1967 Self::List(l) => {
1968 l.push(source.as_ref().to_string());
1969 }
1970 }
1971 }
1972
1973 pub fn extend(&mut self, sources: Vec<String>) {
1975 for s in sources {
1976 self.push(s);
1977 }
1978 }
1979}
1980
1981#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1984#[cfg_attr(feature = "schema", derive(JsonSchema))]
1985#[serde(rename_all = "camelCase", untagged)]
1986pub enum Csp {
1987 Policy(String),
1989 DirectiveMap(HashMap<String, CspDirectiveSources>),
1991}
1992
1993impl From<HashMap<String, CspDirectiveSources>> for Csp {
1994 fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
1995 Self::DirectiveMap(map)
1996 }
1997}
1998
1999impl From<Csp> for HashMap<String, CspDirectiveSources> {
2000 fn from(csp: Csp) -> Self {
2001 match csp {
2002 Csp::Policy(policy) => {
2003 let mut map = HashMap::new();
2004 for directive in policy.split(';') {
2005 let mut tokens = directive.trim().split(' ');
2006 if let Some(directive) = tokens.next() {
2007 let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
2008 map.insert(directive.to_string(), CspDirectiveSources::List(sources));
2009 }
2010 }
2011 map
2012 }
2013 Csp::DirectiveMap(m) => m,
2014 }
2015 }
2016}
2017
2018impl Display for Csp {
2019 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2020 match self {
2021 Self::Policy(s) => write!(f, "{s}"),
2022 Self::DirectiveMap(m) => {
2023 let len = m.len();
2024 let mut i = 0;
2025 for (directive, sources) in m {
2026 let sources: Vec<String> = sources.clone().into();
2027 write!(f, "{} {}", directive, sources.join(" "))?;
2028 i += 1;
2029 if i != len {
2030 write!(f, "; ")?;
2031 }
2032 }
2033 Ok(())
2034 }
2035 }
2036 }
2037}
2038
2039#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2041#[serde(untagged)]
2042#[cfg_attr(feature = "schema", derive(JsonSchema))]
2043pub enum DisabledCspModificationKind {
2044 Flag(bool),
2047 List(Vec<String>),
2049}
2050
2051impl DisabledCspModificationKind {
2052 pub fn can_modify(&self, directive: &str) -> bool {
2054 match self {
2055 Self::Flag(f) => !f,
2056 Self::List(l) => !l.contains(&directive.into()),
2057 }
2058 }
2059}
2060
2061impl Default for DisabledCspModificationKind {
2062 fn default() -> Self {
2063 Self::Flag(false)
2064 }
2065}
2066
2067#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2076#[serde(untagged)]
2077#[cfg_attr(feature = "schema", derive(JsonSchema))]
2078pub enum FsScope {
2079 AllowedPaths(Vec<PathBuf>),
2081 #[serde(rename_all = "camelCase")]
2083 Scope {
2084 #[serde(default)]
2086 allow: Vec<PathBuf>,
2087 #[serde(default)]
2090 deny: Vec<PathBuf>,
2091 #[serde(alias = "require-literal-leading-dot")]
2100 require_literal_leading_dot: Option<bool>,
2101 },
2102}
2103
2104impl Default for FsScope {
2105 fn default() -> Self {
2106 Self::AllowedPaths(Vec::new())
2107 }
2108}
2109
2110impl FsScope {
2111 pub fn allowed_paths(&self) -> &Vec<PathBuf> {
2113 match self {
2114 Self::AllowedPaths(p) => p,
2115 Self::Scope { allow, .. } => allow,
2116 }
2117 }
2118
2119 pub fn forbidden_paths(&self) -> Option<&Vec<PathBuf>> {
2121 match self {
2122 Self::AllowedPaths(_) => None,
2123 Self::Scope { deny, .. } => Some(deny),
2124 }
2125 }
2126}
2127
2128#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2132#[cfg_attr(feature = "schema", derive(JsonSchema))]
2133#[serde(rename_all = "camelCase", deny_unknown_fields)]
2134pub struct AssetProtocolConfig {
2135 #[serde(default)]
2137 pub scope: FsScope,
2138 #[serde(default)]
2140 pub enable: bool,
2141}
2142
2143#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2147#[cfg_attr(feature = "schema", derive(JsonSchema))]
2148#[serde(rename_all = "camelCase", untagged)]
2149pub enum HeaderSource {
2150 Inline(String),
2152 List(Vec<String>),
2154 Map(HashMap<String, String>),
2156}
2157
2158impl Display for HeaderSource {
2159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2160 match self {
2161 Self::Inline(s) => write!(f, "{s}"),
2162 Self::List(l) => write!(f, "{}", l.join(", ")),
2163 Self::Map(m) => {
2164 let len = m.len();
2165 let mut i = 0;
2166 for (key, value) in m {
2167 write!(f, "{key} {value}")?;
2168 i += 1;
2169 if i != len {
2170 write!(f, "; ")?;
2171 }
2172 }
2173 Ok(())
2174 }
2175 }
2176 }
2177}
2178
2179pub trait HeaderAddition {
2183 fn add_configured_headers(self, headers: Option<&HeaderConfig>) -> http::response::Builder;
2185}
2186
2187impl HeaderAddition for Builder {
2188 fn add_configured_headers(mut self, headers: Option<&HeaderConfig>) -> http::response::Builder {
2192 if let Some(headers) = headers {
2193 if let Some(value) = &headers.access_control_allow_credentials {
2195 self = self.header("Access-Control-Allow-Credentials", value.to_string());
2196 };
2197
2198 if let Some(value) = &headers.access_control_allow_headers {
2200 self = self.header("Access-Control-Allow-Headers", value.to_string());
2201 };
2202
2203 if let Some(value) = &headers.access_control_allow_methods {
2205 self = self.header("Access-Control-Allow-Methods", value.to_string());
2206 };
2207
2208 if let Some(value) = &headers.access_control_expose_headers {
2210 self = self.header("Access-Control-Expose-Headers", value.to_string());
2211 };
2212
2213 if let Some(value) = &headers.access_control_max_age {
2215 self = self.header("Access-Control-Max-Age", value.to_string());
2216 };
2217
2218 if let Some(value) = &headers.cross_origin_embedder_policy {
2220 self = self.header("Cross-Origin-Embedder-Policy", value.to_string());
2221 };
2222
2223 if let Some(value) = &headers.cross_origin_opener_policy {
2225 self = self.header("Cross-Origin-Opener-Policy", value.to_string());
2226 };
2227
2228 if let Some(value) = &headers.cross_origin_resource_policy {
2230 self = self.header("Cross-Origin-Resource-Policy", value.to_string());
2231 };
2232
2233 if let Some(value) = &headers.permissions_policy {
2235 self = self.header("Permission-Policy", value.to_string());
2236 };
2237
2238 if let Some(value) = &headers.service_worker_allowed {
2239 self = self.header("Service-Worker-Allowed", value.to_string());
2240 }
2241
2242 if let Some(value) = &headers.timing_allow_origin {
2244 self = self.header("Timing-Allow-Origin", value.to_string());
2245 };
2246
2247 if let Some(value) = &headers.x_content_type_options {
2249 self = self.header("X-Content-Type-Options", value.to_string());
2250 };
2251
2252 if let Some(value) = &headers.tauri_custom_header {
2254 self = self.header("Tauri-Custom-Header", value.to_string());
2256 };
2257 }
2258 self
2259 }
2260}
2261
2262#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2314#[cfg_attr(feature = "schema", derive(JsonSchema))]
2315#[serde(deny_unknown_fields)]
2316pub struct HeaderConfig {
2317 #[serde(rename = "Access-Control-Allow-Credentials")]
2322 pub access_control_allow_credentials: Option<HeaderSource>,
2323 #[serde(rename = "Access-Control-Allow-Headers")]
2331 pub access_control_allow_headers: Option<HeaderSource>,
2332 #[serde(rename = "Access-Control-Allow-Methods")]
2337 pub access_control_allow_methods: Option<HeaderSource>,
2338 #[serde(rename = "Access-Control-Expose-Headers")]
2344 pub access_control_expose_headers: Option<HeaderSource>,
2345 #[serde(rename = "Access-Control-Max-Age")]
2352 pub access_control_max_age: Option<HeaderSource>,
2353 #[serde(rename = "Cross-Origin-Embedder-Policy")]
2358 pub cross_origin_embedder_policy: Option<HeaderSource>,
2359 #[serde(rename = "Cross-Origin-Opener-Policy")]
2366 pub cross_origin_opener_policy: Option<HeaderSource>,
2367 #[serde(rename = "Cross-Origin-Resource-Policy")]
2372 pub cross_origin_resource_policy: Option<HeaderSource>,
2373 #[serde(rename = "Permissions-Policy")]
2378 pub permissions_policy: Option<HeaderSource>,
2379 #[serde(rename = "Service-Worker-Allowed")]
2389 pub service_worker_allowed: Option<HeaderSource>,
2390 #[serde(rename = "Timing-Allow-Origin")]
2396 pub timing_allow_origin: Option<HeaderSource>,
2397 #[serde(rename = "X-Content-Type-Options")]
2404 pub x_content_type_options: Option<HeaderSource>,
2405 #[serde(rename = "Tauri-Custom-Header")]
2410 pub tauri_custom_header: Option<HeaderSource>,
2411}
2412
2413impl HeaderConfig {
2414 pub fn new() -> Self {
2416 HeaderConfig {
2417 access_control_allow_credentials: None,
2418 access_control_allow_methods: None,
2419 access_control_allow_headers: None,
2420 access_control_expose_headers: None,
2421 access_control_max_age: None,
2422 cross_origin_embedder_policy: None,
2423 cross_origin_opener_policy: None,
2424 cross_origin_resource_policy: None,
2425 permissions_policy: None,
2426 service_worker_allowed: None,
2427 timing_allow_origin: None,
2428 x_content_type_options: None,
2429 tauri_custom_header: None,
2430 }
2431 }
2432}
2433
2434#[skip_serializing_none]
2438#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2439#[cfg_attr(feature = "schema", derive(JsonSchema))]
2440#[serde(rename_all = "camelCase", deny_unknown_fields)]
2441pub struct SecurityConfig {
2442 pub csp: Option<Csp>,
2448 #[serde(alias = "dev-csp")]
2453 pub dev_csp: Option<Csp>,
2454 #[serde(default, alias = "freeze-prototype")]
2456 pub freeze_prototype: bool,
2457 #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
2470 pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
2471 #[serde(default, alias = "asset-protocol")]
2473 pub asset_protocol: AssetProtocolConfig,
2474 #[serde(default)]
2476 pub pattern: PatternKind,
2477 #[serde(default)]
2500 pub capabilities: Vec<CapabilityEntry>,
2501 #[serde(default)]
2504 pub headers: Option<HeaderConfig>,
2505}
2506
2507#[derive(Debug, Clone, PartialEq, Serialize)]
2509#[cfg_attr(feature = "schema", derive(JsonSchema))]
2510#[serde(untagged)]
2511pub enum CapabilityEntry {
2512 Inlined(Capability),
2514 Reference(String),
2516}
2517
2518impl<'de> Deserialize<'de> for CapabilityEntry {
2519 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2520 where
2521 D: Deserializer<'de>,
2522 {
2523 UntaggedEnumVisitor::new()
2524 .string(|string| Ok(Self::Reference(string.to_owned())))
2525 .map(|map| map.deserialize::<Capability>().map(Self::Inlined))
2526 .deserialize(deserializer)
2527 }
2528}
2529
2530#[skip_serializing_none]
2532#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
2533#[serde(rename_all = "lowercase", tag = "use", content = "options")]
2534#[cfg_attr(feature = "schema", derive(JsonSchema))]
2535pub enum PatternKind {
2536 Brownfield,
2538 Isolation {
2540 dir: PathBuf,
2542 },
2543}
2544
2545impl Default for PatternKind {
2546 fn default() -> Self {
2547 Self::Brownfield
2548 }
2549}
2550
2551#[skip_serializing_none]
2555#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2556#[cfg_attr(feature = "schema", derive(JsonSchema))]
2557#[serde(rename_all = "camelCase", deny_unknown_fields)]
2558pub struct AppConfig {
2559 #[serde(default)]
2561 pub windows: Vec<WindowConfig>,
2562 #[serde(default)]
2564 pub security: SecurityConfig,
2565 #[serde(alias = "tray-icon")]
2567 pub tray_icon: Option<TrayIconConfig>,
2568 #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
2570 pub macos_private_api: bool,
2571 #[serde(default, alias = "with-global-tauri")]
2573 pub with_global_tauri: bool,
2574 #[serde(rename = "enableGTKAppId", alias = "enable-gtk-app-id", default)]
2576 pub enable_gtk_app_id: bool,
2577}
2578
2579impl AppConfig {
2580 pub fn all_features() -> Vec<&'static str> {
2582 vec![
2583 "tray-icon",
2584 "macos-private-api",
2585 "protocol-asset",
2586 "isolation",
2587 ]
2588 }
2589
2590 pub fn features(&self) -> Vec<&str> {
2592 let mut features = Vec::new();
2593 if self.tray_icon.is_some() {
2594 features.push("tray-icon");
2595 }
2596 if self.macos_private_api {
2597 features.push("macos-private-api");
2598 }
2599 if self.security.asset_protocol.enable {
2600 features.push("protocol-asset");
2601 }
2602
2603 if let PatternKind::Isolation { .. } = self.security.pattern {
2604 features.push("isolation");
2605 }
2606
2607 features.sort_unstable();
2608 features
2609 }
2610}
2611
2612#[skip_serializing_none]
2616#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2617#[cfg_attr(feature = "schema", derive(JsonSchema))]
2618#[serde(rename_all = "camelCase", deny_unknown_fields)]
2619pub struct TrayIconConfig {
2620 pub id: Option<String>,
2622 #[serde(alias = "icon-path")]
2628 pub icon_path: PathBuf,
2629 #[serde(default, alias = "icon-as-template")]
2631 pub icon_as_template: bool,
2632 #[serde(default = "default_true", alias = "menu-on-left-click")]
2638 #[deprecated(since = "2.2.0", note = "Use `show_menu_on_left_click` instead.")]
2639 pub menu_on_left_click: bool,
2640 #[serde(default = "default_true", alias = "show-menu-on-left-click")]
2646 pub show_menu_on_left_click: bool,
2647 pub title: Option<String>,
2649 pub tooltip: Option<String>,
2651}
2652
2653#[skip_serializing_none]
2655#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2656#[cfg_attr(feature = "schema", derive(JsonSchema))]
2657#[serde(rename_all = "camelCase", deny_unknown_fields)]
2658pub struct IosConfig {
2659 pub template: Option<PathBuf>,
2663 pub frameworks: Option<Vec<String>>,
2667 #[serde(alias = "development-team")]
2670 pub development_team: Option<String>,
2671 #[serde(alias = "bundle-version")]
2675 pub bundle_version: Option<String>,
2676 #[serde(
2680 alias = "minimum-system-version",
2681 default = "ios_minimum_system_version"
2682 )]
2683 pub minimum_system_version: String,
2684}
2685
2686impl Default for IosConfig {
2687 fn default() -> Self {
2688 Self {
2689 template: None,
2690 frameworks: None,
2691 development_team: None,
2692 bundle_version: None,
2693 minimum_system_version: ios_minimum_system_version(),
2694 }
2695 }
2696}
2697
2698#[skip_serializing_none]
2700#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2701#[cfg_attr(feature = "schema", derive(JsonSchema))]
2702#[serde(rename_all = "camelCase", deny_unknown_fields)]
2703pub struct AndroidConfig {
2704 #[serde(alias = "min-sdk-version", default = "default_min_sdk_version")]
2707 pub min_sdk_version: u32,
2708
2709 #[serde(alias = "version-code")]
2715 #[cfg_attr(feature = "schema", validate(range(min = 1, max = 2_100_000_000)))]
2716 pub version_code: Option<u32>,
2717}
2718
2719impl Default for AndroidConfig {
2720 fn default() -> Self {
2721 Self {
2722 min_sdk_version: default_min_sdk_version(),
2723 version_code: None,
2724 }
2725 }
2726}
2727
2728fn default_min_sdk_version() -> u32 {
2729 24
2730}
2731
2732#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2734#[cfg_attr(feature = "schema", derive(JsonSchema))]
2735#[serde(untagged, deny_unknown_fields)]
2736#[non_exhaustive]
2737pub enum FrontendDist {
2738 Url(Url),
2740 Directory(PathBuf),
2742 Files(Vec<PathBuf>),
2744}
2745
2746impl std::fmt::Display for FrontendDist {
2747 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2748 match self {
2749 Self::Url(url) => write!(f, "{url}"),
2750 Self::Directory(p) => write!(f, "{}", p.display()),
2751 Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
2752 }
2753 }
2754}
2755
2756#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2758#[cfg_attr(feature = "schema", derive(JsonSchema))]
2759#[serde(rename_all = "camelCase", untagged)]
2760pub enum BeforeDevCommand {
2761 Script(String),
2763 ScriptWithOptions {
2765 script: String,
2767 cwd: Option<String>,
2769 #[serde(default)]
2771 wait: bool,
2772 },
2773}
2774
2775#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2777#[cfg_attr(feature = "schema", derive(JsonSchema))]
2778#[serde(rename_all = "camelCase", untagged)]
2779pub enum HookCommand {
2780 Script(String),
2782 ScriptWithOptions {
2784 script: String,
2786 cwd: Option<String>,
2788 },
2789}
2790
2791#[skip_serializing_none]
2793#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2794#[cfg_attr(feature = "schema", derive(JsonSchema))]
2795#[serde(untagged)]
2796pub enum RunnerConfig {
2797 String(String),
2799 Object {
2801 cmd: String,
2803 cwd: Option<String>,
2805 args: Option<Vec<String>>,
2807 },
2808}
2809
2810impl Default for RunnerConfig {
2811 fn default() -> Self {
2812 RunnerConfig::String("cargo".to_string())
2813 }
2814}
2815
2816impl RunnerConfig {
2817 pub fn cmd(&self) -> &str {
2819 match self {
2820 RunnerConfig::String(cmd) => cmd,
2821 RunnerConfig::Object { cmd, .. } => cmd,
2822 }
2823 }
2824
2825 pub fn cwd(&self) -> Option<&str> {
2827 match self {
2828 RunnerConfig::String(_) => None,
2829 RunnerConfig::Object { cwd, .. } => cwd.as_deref(),
2830 }
2831 }
2832
2833 pub fn args(&self) -> Option<&[String]> {
2835 match self {
2836 RunnerConfig::String(_) => None,
2837 RunnerConfig::Object { args, .. } => args.as_deref(),
2838 }
2839 }
2840}
2841
2842impl std::str::FromStr for RunnerConfig {
2843 type Err = std::convert::Infallible;
2844
2845 fn from_str(s: &str) -> Result<Self, Self::Err> {
2846 Ok(RunnerConfig::String(s.to_string()))
2847 }
2848}
2849
2850impl From<&str> for RunnerConfig {
2851 fn from(s: &str) -> Self {
2852 RunnerConfig::String(s.to_string())
2853 }
2854}
2855
2856impl From<String> for RunnerConfig {
2857 fn from(s: String) -> Self {
2858 RunnerConfig::String(s)
2859 }
2860}
2861
2862#[skip_serializing_none]
2866#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
2867#[cfg_attr(feature = "schema", derive(JsonSchema))]
2868#[serde(rename_all = "camelCase", deny_unknown_fields)]
2869pub struct BuildConfig {
2870 pub runner: Option<RunnerConfig>,
2872 #[serde(alias = "dev-url")]
2880 pub dev_url: Option<Url>,
2881 #[serde(alias = "frontend-dist")]
2895 pub frontend_dist: Option<FrontendDist>,
2896 #[serde(alias = "before-dev-command")]
2900 pub before_dev_command: Option<BeforeDevCommand>,
2901 #[serde(alias = "before-build-command")]
2905 pub before_build_command: Option<HookCommand>,
2906 #[serde(alias = "before-bundle-command")]
2910 pub before_bundle_command: Option<HookCommand>,
2911 pub features: Option<Vec<String>>,
2913 #[serde(alias = "remove-unused-commands", default)]
2921 pub remove_unused_commands: bool,
2922 #[serde(alias = "additional-watch-directories", default)]
2924 pub additional_watch_folders: Vec<PathBuf>,
2925}
2926
2927#[derive(Debug, PartialEq, Eq)]
2928struct PackageVersion(String);
2929
2930impl<'d> serde::Deserialize<'d> for PackageVersion {
2931 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
2932 struct PackageVersionVisitor;
2933
2934 impl Visitor<'_> for PackageVersionVisitor {
2935 type Value = PackageVersion;
2936
2937 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2938 write!(
2939 formatter,
2940 "a semver string or a path to a package.json file"
2941 )
2942 }
2943
2944 fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
2945 let path = PathBuf::from(value);
2946 if path.exists() {
2947 let json_str = read_to_string(&path)
2948 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2949 let package_json: serde_json::Value = serde_json::from_str(&json_str)
2950 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
2951 if let Some(obj) = package_json.as_object() {
2952 let version = obj
2953 .get("version")
2954 .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
2955 .as_str()
2956 .ok_or_else(|| {
2957 DeError::custom(format!("`{} > version` must be a string", path.display()))
2958 })?;
2959 Ok(PackageVersion(
2960 Version::from_str(version)
2961 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2962 .to_string(),
2963 ))
2964 } else {
2965 Err(DeError::custom(
2966 "`package > version` value is not a path to a JSON object",
2967 ))
2968 }
2969 } else {
2970 Ok(PackageVersion(
2971 Version::from_str(value)
2972 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
2973 .to_string(),
2974 ))
2975 }
2976 }
2977 }
2978
2979 deserializer.deserialize_string(PackageVersionVisitor {})
2980 }
2981}
2982
2983fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
2984where
2985 D: Deserializer<'de>,
2986{
2987 Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
2988}
2989
2990#[skip_serializing_none]
3056#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
3057#[cfg_attr(feature = "schema", derive(JsonSchema))]
3058#[serde(rename_all = "camelCase", deny_unknown_fields)]
3059pub struct Config {
3060 #[serde(rename = "$schema")]
3062 pub schema: Option<String>,
3063 #[serde(alias = "product-name")]
3065 #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
3066 pub product_name: Option<String>,
3067 #[serde(alias = "main-binary-name")]
3081 pub main_binary_name: Option<String>,
3082 #[serde(deserialize_with = "version_deserializer", default)]
3098 pub version: Option<String>,
3099 pub identifier: String,
3105 #[serde(default)]
3107 pub app: AppConfig,
3108 #[serde(default)]
3110 pub build: BuildConfig,
3111 #[serde(default)]
3113 pub bundle: BundleConfig,
3114 #[serde(default)]
3116 pub plugins: PluginConfig,
3117}
3118
3119#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
3123#[cfg_attr(feature = "schema", derive(JsonSchema))]
3124pub struct PluginConfig(pub HashMap<String, JsonValue>);
3125
3126#[cfg(feature = "build")]
3132mod build {
3133 use super::*;
3134 use crate::{literal_struct, tokens::*};
3135 use proc_macro2::TokenStream;
3136 use quote::{quote, ToTokens, TokenStreamExt};
3137 use std::convert::identity;
3138
3139 impl ToTokens for WebviewUrl {
3140 fn to_tokens(&self, tokens: &mut TokenStream) {
3141 let prefix = quote! { ::tauri::utils::config::WebviewUrl };
3142
3143 tokens.append_all(match self {
3144 Self::App(path) => {
3145 let path = path_buf_lit(path);
3146 quote! { #prefix::App(#path) }
3147 }
3148 Self::External(url) => {
3149 let url = url_lit(url);
3150 quote! { #prefix::External(#url) }
3151 }
3152 Self::CustomProtocol(url) => {
3153 let url = url_lit(url);
3154 quote! { #prefix::CustomProtocol(#url) }
3155 }
3156 })
3157 }
3158 }
3159
3160 impl ToTokens for BackgroundThrottlingPolicy {
3161 fn to_tokens(&self, tokens: &mut TokenStream) {
3162 let prefix = quote! { ::tauri::utils::config::BackgroundThrottlingPolicy };
3163 tokens.append_all(match self {
3164 Self::Disabled => quote! { #prefix::Disabled },
3165 Self::Throttle => quote! { #prefix::Throttle },
3166 Self::Suspend => quote! { #prefix::Suspend },
3167 })
3168 }
3169 }
3170
3171 impl ToTokens for crate::Theme {
3172 fn to_tokens(&self, tokens: &mut TokenStream) {
3173 let prefix = quote! { ::tauri::utils::Theme };
3174
3175 tokens.append_all(match self {
3176 Self::Light => quote! { #prefix::Light },
3177 Self::Dark => quote! { #prefix::Dark },
3178 })
3179 }
3180 }
3181
3182 impl ToTokens for Color {
3183 fn to_tokens(&self, tokens: &mut TokenStream) {
3184 let Color(r, g, b, a) = self;
3185 tokens.append_all(quote! {::tauri::utils::config::Color(#r,#g,#b,#a)});
3186 }
3187 }
3188 impl ToTokens for WindowEffectsConfig {
3189 fn to_tokens(&self, tokens: &mut TokenStream) {
3190 let effects = vec_lit(self.effects.clone(), |d| d);
3191 let state = opt_lit(self.state.as_ref());
3192 let radius = opt_lit(self.radius.as_ref());
3193 let color = opt_lit(self.color.as_ref());
3194
3195 literal_struct!(
3196 tokens,
3197 ::tauri::utils::config::WindowEffectsConfig,
3198 effects,
3199 state,
3200 radius,
3201 color
3202 )
3203 }
3204 }
3205
3206 impl ToTokens for crate::TitleBarStyle {
3207 fn to_tokens(&self, tokens: &mut TokenStream) {
3208 let prefix = quote! { ::tauri::utils::TitleBarStyle };
3209
3210 tokens.append_all(match self {
3211 Self::Visible => quote! { #prefix::Visible },
3212 Self::Transparent => quote! { #prefix::Transparent },
3213 Self::Overlay => quote! { #prefix::Overlay },
3214 })
3215 }
3216 }
3217
3218 impl ToTokens for LogicalPosition {
3219 fn to_tokens(&self, tokens: &mut TokenStream) {
3220 let LogicalPosition { x, y } = self;
3221 literal_struct!(tokens, ::tauri::utils::config::LogicalPosition, x, y)
3222 }
3223 }
3224
3225 impl ToTokens for crate::WindowEffect {
3226 fn to_tokens(&self, tokens: &mut TokenStream) {
3227 let prefix = quote! { ::tauri::utils::WindowEffect };
3228
3229 #[allow(deprecated)]
3230 tokens.append_all(match self {
3231 WindowEffect::AppearanceBased => quote! { #prefix::AppearanceBased},
3232 WindowEffect::Light => quote! { #prefix::Light},
3233 WindowEffect::Dark => quote! { #prefix::Dark},
3234 WindowEffect::MediumLight => quote! { #prefix::MediumLight},
3235 WindowEffect::UltraDark => quote! { #prefix::UltraDark},
3236 WindowEffect::Titlebar => quote! { #prefix::Titlebar},
3237 WindowEffect::Selection => quote! { #prefix::Selection},
3238 WindowEffect::Menu => quote! { #prefix::Menu},
3239 WindowEffect::Popover => quote! { #prefix::Popover},
3240 WindowEffect::Sidebar => quote! { #prefix::Sidebar},
3241 WindowEffect::HeaderView => quote! { #prefix::HeaderView},
3242 WindowEffect::Sheet => quote! { #prefix::Sheet},
3243 WindowEffect::WindowBackground => quote! { #prefix::WindowBackground},
3244 WindowEffect::HudWindow => quote! { #prefix::HudWindow},
3245 WindowEffect::FullScreenUI => quote! { #prefix::FullScreenUI},
3246 WindowEffect::Tooltip => quote! { #prefix::Tooltip},
3247 WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
3248 WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
3249 WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
3250 WindowEffect::Mica => quote! { #prefix::Mica},
3251 WindowEffect::MicaDark => quote! { #prefix::MicaDark},
3252 WindowEffect::MicaLight => quote! { #prefix::MicaLight},
3253 WindowEffect::Blur => quote! { #prefix::Blur},
3254 WindowEffect::Acrylic => quote! { #prefix::Acrylic},
3255 WindowEffect::Tabbed => quote! { #prefix::Tabbed },
3256 WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
3257 WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
3258 })
3259 }
3260 }
3261
3262 impl ToTokens for crate::WindowEffectState {
3263 fn to_tokens(&self, tokens: &mut TokenStream) {
3264 let prefix = quote! { ::tauri::utils::WindowEffectState };
3265
3266 #[allow(deprecated)]
3267 tokens.append_all(match self {
3268 WindowEffectState::Active => quote! { #prefix::Active},
3269 WindowEffectState::FollowsWindowActiveState => quote! { #prefix::FollowsWindowActiveState},
3270 WindowEffectState::Inactive => quote! { #prefix::Inactive},
3271 })
3272 }
3273 }
3274
3275 impl ToTokens for PreventOverflowMargin {
3276 fn to_tokens(&self, tokens: &mut TokenStream) {
3277 let width = self.width;
3278 let height = self.height;
3279
3280 literal_struct!(
3281 tokens,
3282 ::tauri::utils::config::PreventOverflowMargin,
3283 width,
3284 height
3285 )
3286 }
3287 }
3288
3289 impl ToTokens for PreventOverflowConfig {
3290 fn to_tokens(&self, tokens: &mut TokenStream) {
3291 let prefix = quote! { ::tauri::utils::config::PreventOverflowConfig };
3292
3293 #[allow(deprecated)]
3294 tokens.append_all(match self {
3295 Self::Enable(enable) => quote! { #prefix::Enable(#enable) },
3296 Self::Margin(margin) => quote! { #prefix::Margin(#margin) },
3297 })
3298 }
3299 }
3300
3301 impl ToTokens for WindowConfig {
3302 fn to_tokens(&self, tokens: &mut TokenStream) {
3303 let label = str_lit(&self.label);
3304 let create = &self.create;
3305 let url = &self.url;
3306 let user_agent = opt_str_lit(self.user_agent.as_ref());
3307 let drag_drop_enabled = self.drag_drop_enabled;
3308 let center = self.center;
3309 let x = opt_lit(self.x.as_ref());
3310 let y = opt_lit(self.y.as_ref());
3311 let width = self.width;
3312 let height = self.height;
3313 let min_width = opt_lit(self.min_width.as_ref());
3314 let min_height = opt_lit(self.min_height.as_ref());
3315 let max_width = opt_lit(self.max_width.as_ref());
3316 let max_height = opt_lit(self.max_height.as_ref());
3317 let prevent_overflow = opt_lit(self.prevent_overflow.as_ref());
3318 let resizable = self.resizable;
3319 let maximizable = self.maximizable;
3320 let minimizable = self.minimizable;
3321 let closable = self.closable;
3322 let title = str_lit(&self.title);
3323 let proxy_url = opt_lit(self.proxy_url.as_ref().map(url_lit).as_ref());
3324 let fullscreen = self.fullscreen;
3325 let focus = self.focus;
3326 let focusable = self.focusable;
3327 let transparent = self.transparent;
3328 let maximized = self.maximized;
3329 let visible = self.visible;
3330 let decorations = self.decorations;
3331 let always_on_bottom = self.always_on_bottom;
3332 let always_on_top = self.always_on_top;
3333 let visible_on_all_workspaces = self.visible_on_all_workspaces;
3334 let content_protected = self.content_protected;
3335 let skip_taskbar = self.skip_taskbar;
3336 let window_classname = opt_str_lit(self.window_classname.as_ref());
3337 let theme = opt_lit(self.theme.as_ref());
3338 let title_bar_style = &self.title_bar_style;
3339 let traffic_light_position = opt_lit(self.traffic_light_position.as_ref());
3340 let hidden_title = self.hidden_title;
3341 let accept_first_mouse = self.accept_first_mouse;
3342 let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
3343 let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
3344 let shadow = self.shadow;
3345 let window_effects = opt_lit(self.window_effects.as_ref());
3346 let incognito = self.incognito;
3347 let parent = opt_str_lit(self.parent.as_ref());
3348 let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled;
3349 let browser_extensions_enabled = self.browser_extensions_enabled;
3350 let use_https_scheme = self.use_https_scheme;
3351 let devtools = opt_lit(self.devtools.as_ref());
3352 let background_color = opt_lit(self.background_color.as_ref());
3353 let background_throttling = opt_lit(self.background_throttling.as_ref());
3354 let javascript_disabled = self.javascript_disabled;
3355 let allow_link_preview = self.allow_link_preview;
3356 let disable_input_accessory_view = self.disable_input_accessory_view;
3357
3358 literal_struct!(
3359 tokens,
3360 ::tauri::utils::config::WindowConfig,
3361 label,
3362 url,
3363 create,
3364 user_agent,
3365 drag_drop_enabled,
3366 center,
3367 x,
3368 y,
3369 width,
3370 height,
3371 min_width,
3372 min_height,
3373 max_width,
3374 max_height,
3375 prevent_overflow,
3376 resizable,
3377 maximizable,
3378 minimizable,
3379 closable,
3380 title,
3381 proxy_url,
3382 fullscreen,
3383 focus,
3384 focusable,
3385 transparent,
3386 maximized,
3387 visible,
3388 decorations,
3389 always_on_bottom,
3390 always_on_top,
3391 visible_on_all_workspaces,
3392 content_protected,
3393 skip_taskbar,
3394 window_classname,
3395 theme,
3396 title_bar_style,
3397 traffic_light_position,
3398 hidden_title,
3399 accept_first_mouse,
3400 tabbing_identifier,
3401 additional_browser_args,
3402 shadow,
3403 window_effects,
3404 incognito,
3405 parent,
3406 zoom_hotkeys_enabled,
3407 browser_extensions_enabled,
3408 use_https_scheme,
3409 devtools,
3410 background_color,
3411 background_throttling,
3412 javascript_disabled,
3413 allow_link_preview,
3414 disable_input_accessory_view
3415 );
3416 }
3417 }
3418
3419 impl ToTokens for PatternKind {
3420 fn to_tokens(&self, tokens: &mut TokenStream) {
3421 let prefix = quote! { ::tauri::utils::config::PatternKind };
3422
3423 tokens.append_all(match self {
3424 Self::Brownfield => quote! { #prefix::Brownfield },
3425 #[cfg(not(feature = "isolation"))]
3426 Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
3427 #[cfg(feature = "isolation")]
3428 Self::Isolation { dir } => {
3429 let dir = path_buf_lit(dir);
3430 quote! { #prefix::Isolation { dir: #dir } }
3431 }
3432 })
3433 }
3434 }
3435
3436 impl ToTokens for WebviewInstallMode {
3437 fn to_tokens(&self, tokens: &mut TokenStream) {
3438 let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
3439
3440 tokens.append_all(match self {
3441 Self::Skip => quote! { #prefix::Skip },
3442 Self::DownloadBootstrapper { silent } => {
3443 quote! { #prefix::DownloadBootstrapper { silent: #silent } }
3444 }
3445 Self::EmbedBootstrapper { silent } => {
3446 quote! { #prefix::EmbedBootstrapper { silent: #silent } }
3447 }
3448 Self::OfflineInstaller { silent } => {
3449 quote! { #prefix::OfflineInstaller { silent: #silent } }
3450 }
3451 Self::FixedRuntime { path } => {
3452 let path = path_buf_lit(path);
3453 quote! { #prefix::FixedRuntime { path: #path } }
3454 }
3455 })
3456 }
3457 }
3458
3459 impl ToTokens for WindowsConfig {
3460 fn to_tokens(&self, tokens: &mut TokenStream) {
3461 let webview_install_mode = &self.webview_install_mode;
3462 tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
3463 webview_install_mode: #webview_install_mode,
3464 ..Default::default()
3465 }})
3466 }
3467 }
3468
3469 impl ToTokens for BundleConfig {
3470 fn to_tokens(&self, tokens: &mut TokenStream) {
3471 let publisher = quote!(None);
3472 let homepage = quote!(None);
3473 let icon = vec_lit(&self.icon, str_lit);
3474 let active = self.active;
3475 let targets = quote!(Default::default());
3476 let create_updater_artifacts = quote!(Default::default());
3477 let resources = quote!(None);
3478 let copyright = quote!(None);
3479 let category = quote!(None);
3480 let file_associations = quote!(None);
3481 let short_description = quote!(None);
3482 let long_description = quote!(None);
3483 let use_local_tools_dir = self.use_local_tools_dir;
3484 let external_bin = opt_vec_lit(self.external_bin.as_ref(), str_lit);
3485 let windows = &self.windows;
3486 let license = opt_str_lit(self.license.as_ref());
3487 let license_file = opt_lit(self.license_file.as_ref().map(path_buf_lit).as_ref());
3488 let linux = quote!(Default::default());
3489 let macos = quote!(Default::default());
3490 let ios = quote!(Default::default());
3491 let android = quote!(Default::default());
3492
3493 literal_struct!(
3494 tokens,
3495 ::tauri::utils::config::BundleConfig,
3496 active,
3497 publisher,
3498 homepage,
3499 icon,
3500 targets,
3501 create_updater_artifacts,
3502 resources,
3503 copyright,
3504 category,
3505 license,
3506 license_file,
3507 file_associations,
3508 short_description,
3509 long_description,
3510 use_local_tools_dir,
3511 external_bin,
3512 windows,
3513 linux,
3514 macos,
3515 ios,
3516 android
3517 );
3518 }
3519 }
3520
3521 impl ToTokens for FrontendDist {
3522 fn to_tokens(&self, tokens: &mut TokenStream) {
3523 let prefix = quote! { ::tauri::utils::config::FrontendDist };
3524
3525 tokens.append_all(match self {
3526 Self::Url(url) => {
3527 let url = url_lit(url);
3528 quote! { #prefix::Url(#url) }
3529 }
3530 Self::Directory(path) => {
3531 let path = path_buf_lit(path);
3532 quote! { #prefix::Directory(#path) }
3533 }
3534 Self::Files(files) => {
3535 let files = vec_lit(files, path_buf_lit);
3536 quote! { #prefix::Files(#files) }
3537 }
3538 })
3539 }
3540 }
3541
3542 impl ToTokens for RunnerConfig {
3543 fn to_tokens(&self, tokens: &mut TokenStream) {
3544 let prefix = quote! { ::tauri::utils::config::RunnerConfig };
3545
3546 tokens.append_all(match self {
3547 Self::String(cmd) => {
3548 let cmd = cmd.as_str();
3549 quote!(#prefix::String(#cmd.into()))
3550 }
3551 Self::Object { cmd, cwd, args } => {
3552 let cmd = cmd.as_str();
3553 let cwd = opt_str_lit(cwd.as_ref());
3554 let args = opt_lit(args.as_ref().map(|v| vec_lit(v, str_lit)).as_ref());
3555 quote!(#prefix::Object {
3556 cmd: #cmd.into(),
3557 cwd: #cwd,
3558 args: #args,
3559 })
3560 }
3561 })
3562 }
3563 }
3564
3565 impl ToTokens for BuildConfig {
3566 fn to_tokens(&self, tokens: &mut TokenStream) {
3567 let dev_url = opt_lit(self.dev_url.as_ref().map(url_lit).as_ref());
3568 let frontend_dist = opt_lit(self.frontend_dist.as_ref());
3569 let runner = opt_lit(self.runner.as_ref());
3570 let before_dev_command = quote!(None);
3571 let before_build_command = quote!(None);
3572 let before_bundle_command = quote!(None);
3573 let features = quote!(None);
3574 let remove_unused_commands = quote!(false);
3575 let additional_watch_folders = quote!(Vec::new());
3576
3577 literal_struct!(
3578 tokens,
3579 ::tauri::utils::config::BuildConfig,
3580 runner,
3581 dev_url,
3582 frontend_dist,
3583 before_dev_command,
3584 before_build_command,
3585 before_bundle_command,
3586 features,
3587 remove_unused_commands,
3588 additional_watch_folders
3589 );
3590 }
3591 }
3592
3593 impl ToTokens for CspDirectiveSources {
3594 fn to_tokens(&self, tokens: &mut TokenStream) {
3595 let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
3596
3597 tokens.append_all(match self {
3598 Self::Inline(sources) => {
3599 let sources = sources.as_str();
3600 quote!(#prefix::Inline(#sources.into()))
3601 }
3602 Self::List(list) => {
3603 let list = vec_lit(list, str_lit);
3604 quote!(#prefix::List(#list))
3605 }
3606 })
3607 }
3608 }
3609
3610 impl ToTokens for Csp {
3611 fn to_tokens(&self, tokens: &mut TokenStream) {
3612 let prefix = quote! { ::tauri::utils::config::Csp };
3613
3614 tokens.append_all(match self {
3615 Self::Policy(policy) => {
3616 let policy = policy.as_str();
3617 quote!(#prefix::Policy(#policy.into()))
3618 }
3619 Self::DirectiveMap(list) => {
3620 let map = map_lit(
3621 quote! { ::std::collections::HashMap },
3622 list,
3623 str_lit,
3624 identity,
3625 );
3626 quote!(#prefix::DirectiveMap(#map))
3627 }
3628 })
3629 }
3630 }
3631
3632 impl ToTokens for DisabledCspModificationKind {
3633 fn to_tokens(&self, tokens: &mut TokenStream) {
3634 let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
3635
3636 tokens.append_all(match self {
3637 Self::Flag(flag) => {
3638 quote! { #prefix::Flag(#flag) }
3639 }
3640 Self::List(directives) => {
3641 let directives = vec_lit(directives, str_lit);
3642 quote! { #prefix::List(#directives) }
3643 }
3644 });
3645 }
3646 }
3647
3648 impl ToTokens for CapabilityEntry {
3649 fn to_tokens(&self, tokens: &mut TokenStream) {
3650 let prefix = quote! { ::tauri::utils::config::CapabilityEntry };
3651
3652 tokens.append_all(match self {
3653 Self::Inlined(capability) => {
3654 quote! { #prefix::Inlined(#capability) }
3655 }
3656 Self::Reference(id) => {
3657 let id = str_lit(id);
3658 quote! { #prefix::Reference(#id) }
3659 }
3660 });
3661 }
3662 }
3663
3664 impl ToTokens for HeaderSource {
3665 fn to_tokens(&self, tokens: &mut TokenStream) {
3666 let prefix = quote! { ::tauri::utils::config::HeaderSource };
3667
3668 tokens.append_all(match self {
3669 Self::Inline(s) => {
3670 let line = s.as_str();
3671 quote!(#prefix::Inline(#line.into()))
3672 }
3673 Self::List(l) => {
3674 let list = vec_lit(l, str_lit);
3675 quote!(#prefix::List(#list))
3676 }
3677 Self::Map(m) => {
3678 let map = map_lit(quote! { ::std::collections::HashMap }, m, str_lit, str_lit);
3679 quote!(#prefix::Map(#map))
3680 }
3681 })
3682 }
3683 }
3684
3685 impl ToTokens for HeaderConfig {
3686 fn to_tokens(&self, tokens: &mut TokenStream) {
3687 let access_control_allow_credentials =
3688 opt_lit(self.access_control_allow_credentials.as_ref());
3689 let access_control_allow_headers = opt_lit(self.access_control_allow_headers.as_ref());
3690 let access_control_allow_methods = opt_lit(self.access_control_allow_methods.as_ref());
3691 let access_control_expose_headers = opt_lit(self.access_control_expose_headers.as_ref());
3692 let access_control_max_age = opt_lit(self.access_control_max_age.as_ref());
3693 let cross_origin_embedder_policy = opt_lit(self.cross_origin_embedder_policy.as_ref());
3694 let cross_origin_opener_policy = opt_lit(self.cross_origin_opener_policy.as_ref());
3695 let cross_origin_resource_policy = opt_lit(self.cross_origin_resource_policy.as_ref());
3696 let permissions_policy = opt_lit(self.permissions_policy.as_ref());
3697 let service_worker_allowed = opt_lit(self.service_worker_allowed.as_ref());
3698 let timing_allow_origin = opt_lit(self.timing_allow_origin.as_ref());
3699 let x_content_type_options = opt_lit(self.x_content_type_options.as_ref());
3700 let tauri_custom_header = opt_lit(self.tauri_custom_header.as_ref());
3701
3702 literal_struct!(
3703 tokens,
3704 ::tauri::utils::config::HeaderConfig,
3705 access_control_allow_credentials,
3706 access_control_allow_headers,
3707 access_control_allow_methods,
3708 access_control_expose_headers,
3709 access_control_max_age,
3710 cross_origin_embedder_policy,
3711 cross_origin_opener_policy,
3712 cross_origin_resource_policy,
3713 permissions_policy,
3714 service_worker_allowed,
3715 timing_allow_origin,
3716 x_content_type_options,
3717 tauri_custom_header
3718 );
3719 }
3720 }
3721
3722 impl ToTokens for SecurityConfig {
3723 fn to_tokens(&self, tokens: &mut TokenStream) {
3724 let csp = opt_lit(self.csp.as_ref());
3725 let dev_csp = opt_lit(self.dev_csp.as_ref());
3726 let freeze_prototype = self.freeze_prototype;
3727 let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
3728 let asset_protocol = &self.asset_protocol;
3729 let pattern = &self.pattern;
3730 let capabilities = vec_lit(&self.capabilities, identity);
3731 let headers = opt_lit(self.headers.as_ref());
3732
3733 literal_struct!(
3734 tokens,
3735 ::tauri::utils::config::SecurityConfig,
3736 csp,
3737 dev_csp,
3738 freeze_prototype,
3739 dangerous_disable_asset_csp_modification,
3740 asset_protocol,
3741 pattern,
3742 capabilities,
3743 headers
3744 );
3745 }
3746 }
3747
3748 impl ToTokens for TrayIconConfig {
3749 fn to_tokens(&self, tokens: &mut TokenStream) {
3750 tokens.append_all(quote!(#[allow(deprecated)]));
3752
3753 let id = opt_str_lit(self.id.as_ref());
3754 let icon_as_template = self.icon_as_template;
3755 #[allow(deprecated)]
3756 let menu_on_left_click = self.menu_on_left_click;
3757 let show_menu_on_left_click = self.show_menu_on_left_click;
3758 let icon_path = path_buf_lit(&self.icon_path);
3759 let title = opt_str_lit(self.title.as_ref());
3760 let tooltip = opt_str_lit(self.tooltip.as_ref());
3761 literal_struct!(
3762 tokens,
3763 ::tauri::utils::config::TrayIconConfig,
3764 id,
3765 icon_path,
3766 icon_as_template,
3767 menu_on_left_click,
3768 show_menu_on_left_click,
3769 title,
3770 tooltip
3771 );
3772 }
3773 }
3774
3775 impl ToTokens for FsScope {
3776 fn to_tokens(&self, tokens: &mut TokenStream) {
3777 let prefix = quote! { ::tauri::utils::config::FsScope };
3778
3779 tokens.append_all(match self {
3780 Self::AllowedPaths(allow) => {
3781 let allowed_paths = vec_lit(allow, path_buf_lit);
3782 quote! { #prefix::AllowedPaths(#allowed_paths) }
3783 }
3784 Self::Scope { allow, deny , require_literal_leading_dot} => {
3785 let allow = vec_lit(allow, path_buf_lit);
3786 let deny = vec_lit(deny, path_buf_lit);
3787 let require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref());
3788 quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
3789 }
3790 });
3791 }
3792 }
3793
3794 impl ToTokens for AssetProtocolConfig {
3795 fn to_tokens(&self, tokens: &mut TokenStream) {
3796 let scope = &self.scope;
3797 tokens.append_all(quote! { ::tauri::utils::config::AssetProtocolConfig { scope: #scope, ..Default::default() } })
3798 }
3799 }
3800
3801 impl ToTokens for AppConfig {
3802 fn to_tokens(&self, tokens: &mut TokenStream) {
3803 let windows = vec_lit(&self.windows, identity);
3804 let security = &self.security;
3805 let tray_icon = opt_lit(self.tray_icon.as_ref());
3806 let macos_private_api = self.macos_private_api;
3807 let with_global_tauri = self.with_global_tauri;
3808 let enable_gtk_app_id = self.enable_gtk_app_id;
3809
3810 literal_struct!(
3811 tokens,
3812 ::tauri::utils::config::AppConfig,
3813 windows,
3814 security,
3815 tray_icon,
3816 macos_private_api,
3817 with_global_tauri,
3818 enable_gtk_app_id
3819 );
3820 }
3821 }
3822
3823 impl ToTokens for PluginConfig {
3824 fn to_tokens(&self, tokens: &mut TokenStream) {
3825 let config = map_lit(
3826 quote! { ::std::collections::HashMap },
3827 &self.0,
3828 str_lit,
3829 json_value_lit,
3830 );
3831 tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
3832 }
3833 }
3834
3835 impl ToTokens for Config {
3836 fn to_tokens(&self, tokens: &mut TokenStream) {
3837 let schema = quote!(None);
3838 let product_name = opt_str_lit(self.product_name.as_ref());
3839 let main_binary_name = opt_str_lit(self.main_binary_name.as_ref());
3840 let version = opt_str_lit(self.version.as_ref());
3841 let identifier = str_lit(&self.identifier);
3842 let app = &self.app;
3843 let build = &self.build;
3844 let bundle = &self.bundle;
3845 let plugins = &self.plugins;
3846
3847 literal_struct!(
3848 tokens,
3849 ::tauri::utils::config::Config,
3850 schema,
3851 product_name,
3852 main_binary_name,
3853 version,
3854 identifier,
3855 app,
3856 build,
3857 bundle,
3858 plugins
3859 );
3860 }
3861 }
3862}
3863
3864#[cfg(test)]
3865mod test {
3866 use super::*;
3867
3868 #[test]
3871 fn test_defaults() {
3873 let a_config = AppConfig::default();
3875 let b_config = BuildConfig::default();
3877 let d_windows: Vec<WindowConfig> = vec![];
3879 let d_bundle = BundleConfig::default();
3881
3882 let app = AppConfig {
3884 windows: vec![],
3885 security: SecurityConfig {
3886 csp: None,
3887 dev_csp: None,
3888 freeze_prototype: false,
3889 dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
3890 asset_protocol: AssetProtocolConfig::default(),
3891 pattern: Default::default(),
3892 capabilities: Vec::new(),
3893 headers: None,
3894 },
3895 tray_icon: None,
3896 macos_private_api: false,
3897 with_global_tauri: false,
3898 enable_gtk_app_id: false,
3899 };
3900
3901 let build = BuildConfig {
3903 runner: None,
3904 dev_url: None,
3905 frontend_dist: None,
3906 before_dev_command: None,
3907 before_build_command: None,
3908 before_bundle_command: None,
3909 features: None,
3910 remove_unused_commands: false,
3911 additional_watch_folders: Vec::new(),
3912 };
3913
3914 let bundle = BundleConfig {
3916 active: false,
3917 targets: Default::default(),
3918 create_updater_artifacts: Default::default(),
3919 publisher: None,
3920 homepage: None,
3921 icon: Vec::new(),
3922 resources: None,
3923 copyright: None,
3924 category: None,
3925 file_associations: None,
3926 short_description: None,
3927 long_description: None,
3928 use_local_tools_dir: false,
3929 license: None,
3930 license_file: None,
3931 linux: Default::default(),
3932 macos: Default::default(),
3933 external_bin: None,
3934 windows: Default::default(),
3935 ios: Default::default(),
3936 android: Default::default(),
3937 };
3938
3939 assert_eq!(a_config, app);
3941 assert_eq!(b_config, build);
3942 assert_eq!(d_bundle, bundle);
3943 assert_eq!(d_windows, app.windows);
3944 }
3945
3946 #[test]
3947 fn parse_hex_color() {
3948 use super::Color;
3949
3950 assert_eq!(Color(255, 255, 255, 255), "fff".parse().unwrap());
3951 assert_eq!(Color(255, 255, 255, 255), "#fff".parse().unwrap());
3952 assert_eq!(Color(0, 0, 0, 255), "#000000".parse().unwrap());
3953 assert_eq!(Color(0, 0, 0, 255), "#000000ff".parse().unwrap());
3954 assert_eq!(Color(0, 255, 0, 255), "#00ff00ff".parse().unwrap());
3955 }
3956
3957 #[test]
3958 fn test_runner_config_string_format() {
3959 use super::RunnerConfig;
3960
3961 let json = r#""cargo""#;
3963 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
3964
3965 assert_eq!(runner.cmd(), "cargo");
3966 assert_eq!(runner.cwd(), None);
3967 assert_eq!(runner.args(), None);
3968
3969 let serialized = serde_json::to_string(&runner).unwrap();
3971 assert_eq!(serialized, r#""cargo""#);
3972 }
3973
3974 #[test]
3975 fn test_runner_config_object_format_full() {
3976 use super::RunnerConfig;
3977
3978 let json = r#"{"cmd": "my_runner", "cwd": "/tmp/build", "args": ["--quiet", "--verbose"]}"#;
3980 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
3981
3982 assert_eq!(runner.cmd(), "my_runner");
3983 assert_eq!(runner.cwd(), Some("/tmp/build"));
3984 assert_eq!(
3985 runner.args(),
3986 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
3987 );
3988
3989 let serialized = serde_json::to_string(&runner).unwrap();
3991 let deserialized: RunnerConfig = serde_json::from_str(&serialized).unwrap();
3992 assert_eq!(runner, deserialized);
3993 }
3994
3995 #[test]
3996 fn test_runner_config_object_format_minimal() {
3997 use super::RunnerConfig;
3998
3999 let json = r#"{"cmd": "cross"}"#;
4001 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4002
4003 assert_eq!(runner.cmd(), "cross");
4004 assert_eq!(runner.cwd(), None);
4005 assert_eq!(runner.args(), None);
4006 }
4007
4008 #[test]
4009 fn test_runner_config_default() {
4010 use super::RunnerConfig;
4011
4012 let default_runner = RunnerConfig::default();
4013 assert_eq!(default_runner.cmd(), "cargo");
4014 assert_eq!(default_runner.cwd(), None);
4015 assert_eq!(default_runner.args(), None);
4016 }
4017
4018 #[test]
4019 fn test_runner_config_from_str() {
4020 use super::RunnerConfig;
4021
4022 let runner: RunnerConfig = "my_runner".into();
4024 assert_eq!(runner.cmd(), "my_runner");
4025 assert_eq!(runner.cwd(), None);
4026 assert_eq!(runner.args(), None);
4027 }
4028
4029 #[test]
4030 fn test_runner_config_from_string() {
4031 use super::RunnerConfig;
4032
4033 let runner: RunnerConfig = "another_runner".to_string().into();
4035 assert_eq!(runner.cmd(), "another_runner");
4036 assert_eq!(runner.cwd(), None);
4037 assert_eq!(runner.args(), None);
4038 }
4039
4040 #[test]
4041 fn test_runner_config_from_str_parse() {
4042 use super::RunnerConfig;
4043 use std::str::FromStr;
4044
4045 let runner = RunnerConfig::from_str("parsed_runner").unwrap();
4047 assert_eq!(runner.cmd(), "parsed_runner");
4048 assert_eq!(runner.cwd(), None);
4049 assert_eq!(runner.args(), None);
4050 }
4051
4052 #[test]
4053 fn test_runner_config_in_build_config() {
4054 use super::BuildConfig;
4055
4056 let json = r#"{"runner": "cargo"}"#;
4058 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4059
4060 let runner = build_config.runner.unwrap();
4061 assert_eq!(runner.cmd(), "cargo");
4062 assert_eq!(runner.cwd(), None);
4063 assert_eq!(runner.args(), None);
4064 }
4065
4066 #[test]
4067 fn test_runner_config_in_build_config_object() {
4068 use super::BuildConfig;
4069
4070 let json = r#"{"runner": {"cmd": "cross", "cwd": "/workspace", "args": ["--target", "x86_64-unknown-linux-gnu"]}}"#;
4072 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4073
4074 let runner = build_config.runner.unwrap();
4075 assert_eq!(runner.cmd(), "cross");
4076 assert_eq!(runner.cwd(), Some("/workspace"));
4077 assert_eq!(
4078 runner.args(),
4079 Some(
4080 &[
4081 "--target".to_string(),
4082 "x86_64-unknown-linux-gnu".to_string()
4083 ][..]
4084 )
4085 );
4086 }
4087
4088 #[test]
4089 fn test_runner_config_in_full_config() {
4090 use super::Config;
4091
4092 let json = r#"{
4094 "productName": "Test App",
4095 "version": "1.0.0",
4096 "identifier": "com.test.app",
4097 "build": {
4098 "runner": {
4099 "cmd": "my_custom_cargo",
4100 "cwd": "/tmp/build",
4101 "args": ["--quiet", "--verbose"]
4102 }
4103 }
4104 }"#;
4105
4106 let config: Config = serde_json::from_str(json).unwrap();
4107 let runner = config.build.runner.unwrap();
4108
4109 assert_eq!(runner.cmd(), "my_custom_cargo");
4110 assert_eq!(runner.cwd(), Some("/tmp/build"));
4111 assert_eq!(
4112 runner.args(),
4113 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4114 );
4115 }
4116
4117 #[test]
4118 fn test_runner_config_equality() {
4119 use super::RunnerConfig;
4120
4121 let runner1 = RunnerConfig::String("cargo".to_string());
4122 let runner2 = RunnerConfig::String("cargo".to_string());
4123 let runner3 = RunnerConfig::String("cross".to_string());
4124
4125 assert_eq!(runner1, runner2);
4126 assert_ne!(runner1, runner3);
4127
4128 let runner4 = RunnerConfig::Object {
4129 cmd: "cargo".to_string(),
4130 cwd: Some("/tmp".to_string()),
4131 args: Some(vec!["--quiet".to_string()]),
4132 };
4133 let runner5 = RunnerConfig::Object {
4134 cmd: "cargo".to_string(),
4135 cwd: Some("/tmp".to_string()),
4136 args: Some(vec!["--quiet".to_string()]),
4137 };
4138
4139 assert_eq!(runner4, runner5);
4140 assert_ne!(runner1, runner4);
4141 }
4142
4143 #[test]
4144 fn test_runner_config_untagged_serialization() {
4145 use super::RunnerConfig;
4146
4147 let string_runner = RunnerConfig::String("cargo".to_string());
4149 let string_json = serde_json::to_string(&string_runner).unwrap();
4150 assert_eq!(string_json, r#""cargo""#);
4151
4152 let object_runner = RunnerConfig::Object {
4154 cmd: "cross".to_string(),
4155 cwd: None,
4156 args: None,
4157 };
4158 let object_json = serde_json::to_string(&object_runner).unwrap();
4159 assert!(object_json.contains("\"cmd\":\"cross\""));
4160 assert!(object_json.contains("\"cwd\":null") || !object_json.contains("cwd"));
4162 assert!(object_json.contains("\"args\":null") || !object_json.contains("args"));
4163 }
4164}