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, Default)]
212pub enum BundleTarget {
213 #[default]
215 All,
216 List(Vec<BundleType>),
218 One(BundleType),
220}
221
222#[cfg(feature = "schema")]
223impl schemars::JsonSchema for BundleTarget {
224 fn schema_name() -> std::string::String {
225 "BundleTarget".to_owned()
226 }
227
228 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
229 let any_of = vec![
230 schemars::schema::SchemaObject {
231 const_value: Some("all".into()),
232 metadata: Some(Box::new(schemars::schema::Metadata {
233 description: Some("Bundle all targets.".to_owned()),
234 ..Default::default()
235 })),
236 ..Default::default()
237 }
238 .into(),
239 add_description(
240 gen.subschema_for::<Vec<BundleType>>(),
241 "A list of bundle targets.",
242 ),
243 add_description(gen.subschema_for::<BundleType>(), "A single bundle target."),
244 ];
245
246 schemars::schema::SchemaObject {
247 subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
248 any_of: Some(any_of),
249 ..Default::default()
250 })),
251 metadata: Some(Box::new(schemars::schema::Metadata {
252 description: Some("Targets to bundle. Each value is case insensitive.".to_owned()),
253 ..Default::default()
254 })),
255 ..Default::default()
256 }
257 .into()
258 }
259}
260
261impl Serialize for BundleTarget {
262 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
263 where
264 S: Serializer,
265 {
266 match self {
267 Self::All => serializer.serialize_str("all"),
268 Self::List(l) => l.serialize(serializer),
269 Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
270 }
271 }
272}
273
274impl<'de> Deserialize<'de> for BundleTarget {
275 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
276 where
277 D: Deserializer<'de>,
278 {
279 #[derive(Deserialize, Serialize)]
280 #[serde(untagged)]
281 pub enum BundleTargetInner {
282 List(Vec<BundleType>),
283 One(BundleType),
284 All(String),
285 }
286
287 match BundleTargetInner::deserialize(deserializer)? {
288 BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
289 BundleTargetInner::All(t) => Err(DeError::custom(format!(
290 "invalid bundle type {t}, expected one of `all`, {}",
291 BundleType::all()
292 .iter()
293 .map(|b| format!("`{b}`"))
294 .collect::<Vec<_>>()
295 .join(", ")
296 ))),
297 BundleTargetInner::List(l) => Ok(Self::List(l)),
298 BundleTargetInner::One(t) => Ok(Self::One(t)),
299 }
300 }
301}
302
303impl BundleTarget {
304 #[allow(dead_code)]
306 pub fn to_vec(&self) -> Vec<BundleType> {
307 match self {
308 Self::All => BundleType::all().to_vec(),
309 Self::List(list) => list.clone(),
310 Self::One(i) => vec![i.clone()],
311 }
312 }
313}
314
315#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
319#[cfg_attr(feature = "schema", derive(JsonSchema))]
320#[serde(rename_all = "camelCase", deny_unknown_fields)]
321pub struct AppImageConfig {
322 #[serde(default, alias = "bundle-media-framework")]
325 pub bundle_media_framework: bool,
326 #[serde(default)]
328 pub files: HashMap<PathBuf, PathBuf>,
329}
330
331#[skip_serializing_none]
335#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
336#[cfg_attr(feature = "schema", derive(JsonSchema))]
337#[serde(rename_all = "camelCase", deny_unknown_fields)]
338pub struct DebConfig {
339 pub depends: Option<Vec<String>>,
341 pub recommends: Option<Vec<String>>,
343 pub provides: Option<Vec<String>>,
345 pub conflicts: Option<Vec<String>>,
347 pub replaces: Option<Vec<String>>,
349 #[serde(default)]
351 pub files: HashMap<PathBuf, PathBuf>,
352 pub section: Option<String>,
354 pub priority: Option<String>,
357 pub changelog: Option<PathBuf>,
360 #[serde(alias = "desktop-template")]
364 pub desktop_template: Option<PathBuf>,
365 #[serde(alias = "pre-install-script")]
368 pub pre_install_script: Option<PathBuf>,
369 #[serde(alias = "post-install-script")]
372 pub post_install_script: Option<PathBuf>,
373 #[serde(alias = "pre-remove-script")]
376 pub pre_remove_script: Option<PathBuf>,
377 #[serde(alias = "post-remove-script")]
380 pub post_remove_script: Option<PathBuf>,
381}
382
383#[skip_serializing_none]
387#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
388#[cfg_attr(feature = "schema", derive(JsonSchema))]
389#[serde(rename_all = "camelCase", deny_unknown_fields)]
390pub struct LinuxConfig {
391 #[serde(default)]
393 pub appimage: AppImageConfig,
394 #[serde(default)]
396 pub deb: DebConfig,
397 #[serde(default)]
399 pub rpm: RpmConfig,
400}
401
402#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
404#[cfg_attr(feature = "schema", derive(JsonSchema))]
405#[serde(rename_all = "camelCase", deny_unknown_fields, tag = "type")]
406#[non_exhaustive]
407pub enum RpmCompression {
408 Gzip {
410 level: u32,
412 },
413 Zstd {
415 level: i32,
417 },
418 Xz {
420 level: u32,
422 },
423 Bzip2 {
425 level: u32,
427 },
428 None,
430}
431
432#[skip_serializing_none]
434#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
435#[cfg_attr(feature = "schema", derive(JsonSchema))]
436#[serde(rename_all = "camelCase", deny_unknown_fields)]
437pub struct RpmConfig {
438 pub depends: Option<Vec<String>>,
440 pub recommends: Option<Vec<String>>,
442 pub provides: Option<Vec<String>>,
444 pub conflicts: Option<Vec<String>>,
447 pub obsoletes: Option<Vec<String>>,
450 #[serde(default = "default_release")]
452 pub release: String,
453 #[serde(default)]
455 pub epoch: u32,
456 #[serde(default)]
458 pub files: HashMap<PathBuf, PathBuf>,
459 #[serde(alias = "desktop-template")]
463 pub desktop_template: Option<PathBuf>,
464 #[serde(alias = "pre-install-script")]
467 pub pre_install_script: Option<PathBuf>,
468 #[serde(alias = "post-install-script")]
471 pub post_install_script: Option<PathBuf>,
472 #[serde(alias = "pre-remove-script")]
475 pub pre_remove_script: Option<PathBuf>,
476 #[serde(alias = "post-remove-script")]
479 pub post_remove_script: Option<PathBuf>,
480 pub compression: Option<RpmCompression>,
482}
483
484impl Default for RpmConfig {
485 fn default() -> Self {
486 Self {
487 depends: None,
488 recommends: None,
489 provides: None,
490 conflicts: None,
491 obsoletes: None,
492 release: default_release(),
493 epoch: 0,
494 files: Default::default(),
495 desktop_template: None,
496 pre_install_script: None,
497 post_install_script: None,
498 pre_remove_script: None,
499 post_remove_script: None,
500 compression: None,
501 }
502 }
503}
504
505fn default_release() -> String {
506 "1".into()
507}
508
509#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
511#[cfg_attr(feature = "schema", derive(JsonSchema))]
512#[serde(rename_all = "camelCase", deny_unknown_fields)]
513pub struct Position {
514 pub x: u32,
516 pub y: u32,
518}
519
520#[derive(Default, Debug, PartialEq, Clone, Deserialize, Serialize)]
522#[cfg_attr(feature = "schema", derive(JsonSchema))]
523#[serde(rename_all = "camelCase", deny_unknown_fields)]
524pub struct LogicalPosition {
525 pub x: f64,
527 pub y: f64,
529}
530
531#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
533#[cfg_attr(feature = "schema", derive(JsonSchema))]
534#[serde(rename_all = "camelCase", deny_unknown_fields)]
535pub struct Size {
536 pub width: u32,
538 pub height: u32,
540}
541
542#[skip_serializing_none]
546#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
547#[cfg_attr(feature = "schema", derive(JsonSchema))]
548#[serde(rename_all = "camelCase", deny_unknown_fields)]
549pub struct DmgConfig {
550 pub background: Option<PathBuf>,
552 pub window_position: Option<Position>,
554 #[serde(default = "dmg_window_size", alias = "window-size")]
556 pub window_size: Size,
557 #[serde(default = "dmg_app_position", alias = "app-position")]
559 pub app_position: Position,
560 #[serde(
562 default = "dmg_application_folder_position",
563 alias = "application-folder-position"
564 )]
565 pub application_folder_position: Position,
566}
567
568impl Default for DmgConfig {
569 fn default() -> Self {
570 Self {
571 background: None,
572 window_position: None,
573 window_size: dmg_window_size(),
574 app_position: dmg_app_position(),
575 application_folder_position: dmg_application_folder_position(),
576 }
577 }
578}
579
580fn dmg_window_size() -> Size {
581 Size {
582 width: 660,
583 height: 400,
584 }
585}
586
587fn dmg_app_position() -> Position {
588 Position { x: 180, y: 170 }
589}
590
591fn dmg_application_folder_position() -> Position {
592 Position { x: 480, y: 170 }
593}
594
595fn de_macos_minimum_system_version<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
596where
597 D: Deserializer<'de>,
598{
599 let version = Option::<String>::deserialize(deserializer)?;
600 match version {
601 Some(v) if v.is_empty() => Ok(macos_minimum_system_version()),
602 e => Ok(e),
603 }
604}
605
606#[skip_serializing_none]
610#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
611#[cfg_attr(feature = "schema", derive(JsonSchema))]
612#[serde(rename_all = "camelCase", deny_unknown_fields)]
613pub struct MacConfig {
614 pub frameworks: Option<Vec<String>>,
618 #[serde(default)]
620 pub files: HashMap<PathBuf, PathBuf>,
621 #[serde(alias = "bundle-version")]
625 pub bundle_version: Option<String>,
626 #[serde(alias = "bundle-name")]
632 pub bundle_name: Option<String>,
633 #[serde(
642 deserialize_with = "de_macos_minimum_system_version",
643 default = "macos_minimum_system_version",
644 alias = "minimum-system-version"
645 )]
646 pub minimum_system_version: Option<String>,
647 #[serde(alias = "exception-domain")]
650 pub exception_domain: Option<String>,
651 #[serde(alias = "signing-identity")]
653 pub signing_identity: Option<String>,
654 #[serde(alias = "hardened-runtime", default = "default_true")]
656 pub hardened_runtime: bool,
657 #[serde(alias = "provider-short-name")]
659 pub provider_short_name: Option<String>,
660 pub entitlements: Option<String>,
662 #[serde(alias = "info-plist")]
666 pub info_plist: Option<PathBuf>,
667 #[serde(default)]
669 pub dmg: DmgConfig,
670}
671
672impl Default for MacConfig {
673 fn default() -> Self {
674 Self {
675 frameworks: None,
676 files: HashMap::new(),
677 bundle_version: None,
678 bundle_name: None,
679 minimum_system_version: macos_minimum_system_version(),
680 exception_domain: None,
681 signing_identity: None,
682 hardened_runtime: true,
683 provider_short_name: None,
684 entitlements: None,
685 info_plist: None,
686 dmg: Default::default(),
687 }
688 }
689}
690
691fn macos_minimum_system_version() -> Option<String> {
692 Some("10.13".into())
693}
694
695fn ios_minimum_system_version() -> String {
696 "14.0".into()
697}
698
699#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
703#[cfg_attr(feature = "schema", derive(JsonSchema))]
704#[serde(rename_all = "camelCase", deny_unknown_fields)]
705pub struct WixLanguageConfig {
706 #[serde(alias = "locale-path")]
708 pub locale_path: Option<String>,
709}
710
711#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
713#[cfg_attr(feature = "schema", derive(JsonSchema))]
714#[serde(untagged)]
715pub enum WixLanguage {
716 One(String),
718 List(Vec<String>),
720 Localized(HashMap<String, WixLanguageConfig>),
722}
723
724impl Default for WixLanguage {
725 fn default() -> Self {
726 Self::One("en-US".into())
727 }
728}
729
730#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
734#[cfg_attr(feature = "schema", derive(JsonSchema))]
735#[serde(rename_all = "camelCase", deny_unknown_fields)]
736pub struct WixConfig {
737 pub version: Option<String>,
746 #[serde(alias = "upgrade-code")]
755 pub upgrade_code: Option<uuid::Uuid>,
756 #[serde(default)]
758 pub language: WixLanguage,
759 pub template: Option<PathBuf>,
761 #[serde(default, alias = "fragment-paths")]
763 pub fragment_paths: Vec<PathBuf>,
764 #[serde(default, alias = "component-group-refs")]
766 pub component_group_refs: Vec<String>,
767 #[serde(default, alias = "component-refs")]
769 pub component_refs: Vec<String>,
770 #[serde(default, alias = "feature-group-refs")]
772 pub feature_group_refs: Vec<String>,
773 #[serde(default, alias = "feature-refs")]
775 pub feature_refs: Vec<String>,
776 #[serde(default, alias = "merge-refs")]
778 pub merge_refs: Vec<String>,
779 #[serde(default, alias = "enable-elevated-update-task")]
781 pub enable_elevated_update_task: bool,
782 #[serde(alias = "banner-path")]
787 pub banner_path: Option<PathBuf>,
788 #[serde(alias = "dialog-image-path")]
793 pub dialog_image_path: Option<PathBuf>,
794 #[serde(default, alias = "fips-compliant")]
797 pub fips_compliant: bool,
798}
799
800#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Default)]
804#[cfg_attr(feature = "schema", derive(JsonSchema))]
805#[serde(rename_all = "camelCase", deny_unknown_fields)]
806pub enum NsisCompression {
807 Zlib,
809 Bzip2,
811 #[default]
813 Lzma,
814 None,
816}
817
818#[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
820#[serde(rename_all = "camelCase", deny_unknown_fields)]
821#[cfg_attr(feature = "schema", derive(JsonSchema))]
822pub enum NSISInstallerMode {
823 #[default]
829 CurrentUser,
830 PerMachine,
835 Both,
841}
842
843#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
845#[cfg_attr(feature = "schema", derive(JsonSchema))]
846#[serde(rename_all = "camelCase", deny_unknown_fields)]
847pub struct NsisConfig {
848 pub template: Option<PathBuf>,
850 #[serde(alias = "header-image")]
854 pub header_image: Option<PathBuf>,
855 #[serde(alias = "sidebar-image")]
859 pub sidebar_image: Option<PathBuf>,
860 #[serde(alias = "install-icon")]
862 pub installer_icon: Option<PathBuf>,
863 #[serde(default, alias = "install-mode")]
865 pub install_mode: NSISInstallerMode,
866 pub languages: Option<Vec<String>>,
872 pub custom_language_files: Option<HashMap<String, PathBuf>>,
879 #[serde(default, alias = "display-language-selector")]
882 pub display_language_selector: bool,
883 #[serde(default)]
887 pub compression: NsisCompression,
888 #[serde(alias = "start-menu-folder")]
897 pub start_menu_folder: Option<String>,
898 #[serde(alias = "installer-hooks")]
928 pub installer_hooks: Option<PathBuf>,
929 #[serde(alias = "minimum-webview2-version")]
933 pub minimum_webview2_version: Option<String>,
934}
935
936#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
941#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
942#[cfg_attr(feature = "schema", derive(JsonSchema))]
943pub enum WebviewInstallMode {
944 Skip,
946 DownloadBootstrapper {
950 #[serde(default = "default_true")]
952 silent: bool,
953 },
954 EmbedBootstrapper {
958 #[serde(default = "default_true")]
960 silent: bool,
961 },
962 OfflineInstaller {
966 #[serde(default = "default_true")]
968 silent: bool,
969 },
970 FixedRuntime {
973 path: PathBuf,
978 },
979}
980
981impl Default for WebviewInstallMode {
982 fn default() -> Self {
983 Self::DownloadBootstrapper { silent: true }
984 }
985}
986
987#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
989#[cfg_attr(feature = "schema", derive(JsonSchema))]
990#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
991pub enum CustomSignCommandConfig {
992 Command(String),
1001 CommandWithOptions {
1006 cmd: String,
1008 args: Vec<String>,
1012 },
1013}
1014
1015#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1019#[cfg_attr(feature = "schema", derive(JsonSchema))]
1020#[serde(rename_all = "camelCase", deny_unknown_fields)]
1021pub struct WindowsConfig {
1022 #[serde(alias = "digest-algorithm")]
1025 pub digest_algorithm: Option<String>,
1026 #[serde(alias = "certificate-thumbprint")]
1028 pub certificate_thumbprint: Option<String>,
1029 #[serde(alias = "timestamp-url")]
1031 pub timestamp_url: Option<String>,
1032 #[serde(default)]
1035 pub tsp: bool,
1036 #[serde(default, alias = "webview-install-mode")]
1038 pub webview_install_mode: WebviewInstallMode,
1039 #[serde(default = "default_true", alias = "allow-downgrades")]
1045 pub allow_downgrades: bool,
1046 pub wix: Option<WixConfig>,
1048 pub nsis: Option<NsisConfig>,
1050 #[serde(alias = "sign-command")]
1058 pub sign_command: Option<CustomSignCommandConfig>,
1059}
1060
1061impl Default for WindowsConfig {
1062 fn default() -> Self {
1063 Self {
1064 digest_algorithm: None,
1065 certificate_thumbprint: None,
1066 timestamp_url: None,
1067 tsp: false,
1068 webview_install_mode: Default::default(),
1069 allow_downgrades: true,
1070 wix: None,
1071 nsis: None,
1072 sign_command: None,
1073 }
1074 }
1075}
1076
1077#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1079#[cfg_attr(feature = "schema", derive(JsonSchema))]
1080pub enum BundleTypeRole {
1081 #[default]
1083 Editor,
1084 Viewer,
1086 Shell,
1088 QLGenerator,
1090 None,
1092}
1093
1094impl Display for BundleTypeRole {
1095 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1096 match self {
1097 Self::Editor => write!(f, "Editor"),
1098 Self::Viewer => write!(f, "Viewer"),
1099 Self::Shell => write!(f, "Shell"),
1100 Self::QLGenerator => write!(f, "QLGenerator"),
1101 Self::None => write!(f, "None"),
1102 }
1103 }
1104}
1105
1106#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1110#[cfg_attr(feature = "schema", derive(JsonSchema))]
1111pub enum HandlerRank {
1112 #[default]
1114 Default,
1115 Owner,
1117 Alternate,
1119 None,
1121}
1122
1123impl Display for HandlerRank {
1124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1125 match self {
1126 Self::Default => write!(f, "Default"),
1127 Self::Owner => write!(f, "Owner"),
1128 Self::Alternate => write!(f, "Alternate"),
1129 Self::None => write!(f, "None"),
1130 }
1131 }
1132}
1133
1134#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
1138#[cfg_attr(feature = "schema", derive(JsonSchema))]
1139pub struct AssociationExt(pub String);
1140
1141impl fmt::Display for AssociationExt {
1142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1143 write!(f, "{}", self.0)
1144 }
1145}
1146
1147impl<'d> serde::Deserialize<'d> for AssociationExt {
1148 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
1149 let ext = String::deserialize(deserializer)?;
1150 if let Some(ext) = ext.strip_prefix('.') {
1151 Ok(AssociationExt(ext.into()))
1152 } else {
1153 Ok(AssociationExt(ext))
1154 }
1155 }
1156}
1157
1158#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1160#[cfg_attr(feature = "schema", derive(JsonSchema))]
1161#[serde(rename_all = "camelCase", deny_unknown_fields)]
1162pub struct FileAssociation {
1163 pub ext: Vec<AssociationExt>,
1165 #[serde(alias = "content-types")]
1170 pub content_types: Option<Vec<String>>,
1171 pub name: Option<String>,
1173 pub description: Option<String>,
1175 #[serde(default)]
1177 pub role: BundleTypeRole,
1178 #[serde(alias = "mime-type")]
1180 pub mime_type: Option<String>,
1181 #[serde(default)]
1183 pub rank: HandlerRank,
1184 pub exported_type: Option<ExportedFileAssociation>,
1188}
1189
1190#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1192#[cfg_attr(feature = "schema", derive(JsonSchema))]
1193#[serde(rename_all = "camelCase", deny_unknown_fields)]
1194pub struct ExportedFileAssociation {
1195 pub identifier: String,
1197 #[serde(alias = "conforms-to")]
1201 pub conforms_to: Option<Vec<String>>,
1202}
1203
1204#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1206#[cfg_attr(feature = "schema", derive(JsonSchema))]
1207#[serde(rename_all = "camelCase", deny_unknown_fields)]
1208pub struct DeepLinkProtocol {
1209 #[serde(default)]
1211 pub schemes: Vec<String>,
1212 #[serde(default)]
1220 pub domains: Vec<String>,
1221 pub name: Option<String>,
1223 #[serde(default)]
1225 pub role: BundleTypeRole,
1226}
1227
1228#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1231#[cfg_attr(feature = "schema", derive(JsonSchema))]
1232#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1233pub enum BundleResources {
1234 List(Vec<String>),
1236 Map(HashMap<String, String>),
1238}
1239
1240impl BundleResources {
1241 pub fn push(&mut self, path: impl Into<String>) {
1243 match self {
1244 Self::List(l) => l.push(path.into()),
1245 Self::Map(l) => {
1246 let path = path.into();
1247 l.insert(path.clone(), path);
1248 }
1249 }
1250 }
1251}
1252
1253#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1255#[cfg_attr(feature = "schema", derive(JsonSchema))]
1256#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
1257pub enum Updater {
1258 String(V1Compatible),
1260 Bool(bool),
1263}
1264
1265impl Default for Updater {
1266 fn default() -> Self {
1267 Self::Bool(false)
1268 }
1269}
1270
1271#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1273#[cfg_attr(feature = "schema", derive(JsonSchema))]
1274#[serde(rename_all = "camelCase", deny_unknown_fields)]
1275pub enum V1Compatible {
1276 V1Compatible,
1278}
1279
1280#[skip_serializing_none]
1284#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
1285#[cfg_attr(feature = "schema", derive(JsonSchema))]
1286#[serde(rename_all = "camelCase", deny_unknown_fields)]
1287pub struct BundleConfig {
1288 #[serde(default)]
1290 pub active: bool,
1291 #[serde(default)]
1293 pub targets: BundleTarget,
1294 #[serde(default)]
1295 pub create_updater_artifacts: Updater,
1297 pub publisher: Option<String>,
1302 pub homepage: Option<String>,
1307 #[serde(default)]
1309 pub icon: Vec<String>,
1310 pub resources: Option<BundleResources>,
1355 pub copyright: Option<String>,
1357 pub license: Option<String>,
1360 #[serde(alias = "license-file")]
1362 pub license_file: Option<PathBuf>,
1363 pub category: Option<String>,
1368 pub file_associations: Option<Vec<FileAssociation>>,
1370 #[serde(alias = "short-description")]
1372 pub short_description: Option<String>,
1373 #[serde(alias = "long-description")]
1375 pub long_description: Option<String>,
1376 #[serde(default, alias = "use-local-tools-dir")]
1384 pub use_local_tools_dir: bool,
1385 #[serde(alias = "external-bin")]
1397 pub external_bin: Option<Vec<String>>,
1398 #[serde(default)]
1400 pub windows: WindowsConfig,
1401 #[serde(default)]
1403 pub linux: LinuxConfig,
1404 #[serde(rename = "macOS", alias = "macos", default)]
1406 pub macos: MacConfig,
1407 #[serde(rename = "iOS", alias = "ios", default)]
1409 pub ios: IosConfig,
1410 #[serde(default)]
1412 pub android: AndroidConfig,
1413}
1414
1415#[derive(Debug, PartialEq, Eq, Serialize, Default, Clone, Copy)]
1417#[serde(rename_all = "camelCase", deny_unknown_fields)]
1418pub struct Color(pub u8, pub u8, pub u8, pub u8);
1419
1420impl From<Color> for (u8, u8, u8, u8) {
1421 fn from(value: Color) -> Self {
1422 (value.0, value.1, value.2, value.3)
1423 }
1424}
1425
1426impl From<Color> for (u8, u8, u8) {
1427 fn from(value: Color) -> Self {
1428 (value.0, value.1, value.2)
1429 }
1430}
1431
1432impl From<(u8, u8, u8, u8)> for Color {
1433 fn from(value: (u8, u8, u8, u8)) -> Self {
1434 Color(value.0, value.1, value.2, value.3)
1435 }
1436}
1437
1438impl From<(u8, u8, u8)> for Color {
1439 fn from(value: (u8, u8, u8)) -> Self {
1440 Color(value.0, value.1, value.2, 255)
1441 }
1442}
1443
1444impl From<Color> for [u8; 4] {
1445 fn from(value: Color) -> Self {
1446 [value.0, value.1, value.2, value.3]
1447 }
1448}
1449
1450impl From<Color> for [u8; 3] {
1451 fn from(value: Color) -> Self {
1452 [value.0, value.1, value.2]
1453 }
1454}
1455
1456impl From<[u8; 4]> for Color {
1457 fn from(value: [u8; 4]) -> Self {
1458 Color(value[0], value[1], value[2], value[3])
1459 }
1460}
1461
1462impl From<[u8; 3]> for Color {
1463 fn from(value: [u8; 3]) -> Self {
1464 Color(value[0], value[1], value[2], 255)
1465 }
1466}
1467
1468impl FromStr for Color {
1469 type Err = String;
1470 fn from_str(mut color: &str) -> Result<Self, Self::Err> {
1471 color = color.trim().strip_prefix('#').unwrap_or(color);
1472 let color = match color.len() {
1473 3 => color.chars()
1475 .flat_map(|c| std::iter::repeat(c).take(2))
1476 .chain(std::iter::repeat('f').take(2))
1477 .collect(),
1478 6 => format!("{color}FF"),
1479 8 => color.to_string(),
1480 _ => return Err("Invalid hex color length, must be either 3, 6 or 8, for example: #fff, #ffffff, or #ffffffff".into()),
1481 };
1482
1483 let r = u8::from_str_radix(&color[0..2], 16).map_err(|e| e.to_string())?;
1484 let g = u8::from_str_radix(&color[2..4], 16).map_err(|e| e.to_string())?;
1485 let b = u8::from_str_radix(&color[4..6], 16).map_err(|e| e.to_string())?;
1486 let a = u8::from_str_radix(&color[6..8], 16).map_err(|e| e.to_string())?;
1487
1488 Ok(Color(r, g, b, a))
1489 }
1490}
1491
1492fn default_alpha() -> u8 {
1493 255
1494}
1495
1496#[derive(Deserialize)]
1497#[cfg_attr(feature = "schema", derive(JsonSchema))]
1498#[serde(untagged)]
1499enum InnerColor {
1500 String(String),
1502 Rgb((u8, u8, u8)),
1504 Rgba((u8, u8, u8, u8)),
1506 RgbaObject {
1508 red: u8,
1509 green: u8,
1510 blue: u8,
1511 #[serde(default = "default_alpha")]
1512 alpha: u8,
1513 },
1514}
1515
1516impl<'de> Deserialize<'de> for Color {
1517 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1518 where
1519 D: Deserializer<'de>,
1520 {
1521 let color = InnerColor::deserialize(deserializer)?;
1522 let color = match color {
1523 InnerColor::String(string) => string.parse().map_err(serde::de::Error::custom)?,
1524 InnerColor::Rgb(rgb) => Color(rgb.0, rgb.1, rgb.2, 255),
1525 InnerColor::Rgba(rgb) => rgb.into(),
1526 InnerColor::RgbaObject {
1527 red,
1528 green,
1529 blue,
1530 alpha,
1531 } => Color(red, green, blue, alpha),
1532 };
1533
1534 Ok(color)
1535 }
1536}
1537
1538#[cfg(feature = "schema")]
1539impl schemars::JsonSchema for Color {
1540 fn schema_name() -> String {
1541 "Color".to_string()
1542 }
1543
1544 fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1545 let mut schema = schemars::schema_for!(InnerColor).schema;
1546 schema.metadata = None; let any_of = schema.subschemas().any_of.as_mut().unwrap();
1550 let schemars::schema::Schema::Object(str_schema) = any_of.first_mut().unwrap() else {
1551 unreachable!()
1552 };
1553 str_schema.string().pattern = Some("^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$".into());
1554
1555 schema.into()
1556 }
1557}
1558
1559#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1561#[cfg_attr(feature = "schema", derive(JsonSchema))]
1562#[serde(rename_all = "camelCase", deny_unknown_fields)]
1563pub enum BackgroundThrottlingPolicy {
1564 Disabled,
1566 Suspend,
1568 Throttle,
1570}
1571
1572#[skip_serializing_none]
1574#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1575#[cfg_attr(feature = "schema", derive(JsonSchema))]
1576#[serde(rename_all = "camelCase", deny_unknown_fields)]
1577pub struct WindowEffectsConfig {
1578 pub effects: Vec<WindowEffect>,
1581 pub state: Option<WindowEffectState>,
1583 pub radius: Option<f64>,
1585 pub color: Option<Color>,
1588}
1589
1590#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
1593#[cfg_attr(feature = "schema", derive(JsonSchema))]
1594#[serde(rename_all = "camelCase", deny_unknown_fields)]
1595pub struct PreventOverflowMargin {
1596 pub width: u32,
1598 pub height: u32,
1600}
1601
1602#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1604#[cfg_attr(feature = "schema", derive(JsonSchema))]
1605#[serde(untagged)]
1606pub enum PreventOverflowConfig {
1607 Enable(bool),
1609 Margin(PreventOverflowMargin),
1612}
1613
1614#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
1620#[cfg_attr(feature = "schema", derive(JsonSchema))]
1621#[serde(rename_all = "camelCase", deny_unknown_fields)]
1622#[non_exhaustive]
1623pub enum ScrollBarStyle {
1624 #[default]
1625 Default,
1627
1628 FluentOverlay,
1633}
1634
1635#[skip_serializing_none]
1639#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
1640#[cfg_attr(feature = "schema", derive(JsonSchema))]
1641#[serde(rename_all = "camelCase", deny_unknown_fields)]
1642pub struct WindowConfig {
1643 #[serde(default = "default_window_label")]
1645 pub label: String,
1646 #[serde(default = "default_true")]
1661 pub create: bool,
1662 #[serde(default)]
1664 pub url: WebviewUrl,
1665 #[serde(alias = "user-agent")]
1667 pub user_agent: Option<String>,
1668 #[serde(default = "default_true", alias = "drag-drop-enabled")]
1672 pub drag_drop_enabled: bool,
1673 #[serde(default)]
1675 pub center: bool,
1676 pub x: Option<f64>,
1678 pub y: Option<f64>,
1680 #[serde(default = "default_width")]
1682 pub width: f64,
1683 #[serde(default = "default_height")]
1685 pub height: f64,
1686 #[serde(alias = "min-width")]
1688 pub min_width: Option<f64>,
1689 #[serde(alias = "min-height")]
1691 pub min_height: Option<f64>,
1692 #[serde(alias = "max-width")]
1694 pub max_width: Option<f64>,
1695 #[serde(alias = "max-height")]
1697 pub max_height: Option<f64>,
1698 #[serde(alias = "prevent-overflow")]
1704 pub prevent_overflow: Option<PreventOverflowConfig>,
1705 #[serde(default = "default_true")]
1707 pub resizable: bool,
1708 #[serde(default = "default_true")]
1716 pub maximizable: bool,
1717 #[serde(default = "default_true")]
1723 pub minimizable: bool,
1724 #[serde(default = "default_true")]
1732 pub closable: bool,
1733 #[serde(default = "default_title")]
1735 pub title: String,
1736 #[serde(default)]
1738 pub fullscreen: bool,
1739 #[serde(default = "default_true")]
1741 pub focus: bool,
1742 #[serde(default = "default_true")]
1744 pub focusable: bool,
1745 #[serde(default)]
1750 pub transparent: bool,
1751 #[serde(default)]
1753 pub maximized: bool,
1754 #[serde(default = "default_true")]
1756 pub visible: bool,
1757 #[serde(default = "default_true")]
1759 pub decorations: bool,
1760 #[serde(default, alias = "always-on-bottom")]
1762 pub always_on_bottom: bool,
1763 #[serde(default, alias = "always-on-top")]
1765 pub always_on_top: bool,
1766 #[serde(default, alias = "visible-on-all-workspaces")]
1772 pub visible_on_all_workspaces: bool,
1773 #[serde(default, alias = "content-protected")]
1775 pub content_protected: bool,
1776 #[serde(default, alias = "skip-taskbar")]
1778 pub skip_taskbar: bool,
1779 pub window_classname: Option<String>,
1781 pub theme: Option<crate::Theme>,
1783 #[serde(default, alias = "title-bar-style")]
1785 pub title_bar_style: TitleBarStyle,
1786 #[serde(default, alias = "traffic-light-position")]
1790 pub traffic_light_position: Option<LogicalPosition>,
1791 #[serde(default, alias = "hidden-title")]
1793 pub hidden_title: bool,
1794 #[serde(default, alias = "accept-first-mouse")]
1796 pub accept_first_mouse: bool,
1797 #[serde(default, alias = "tabbing-identifier")]
1804 pub tabbing_identifier: Option<String>,
1805 #[serde(default, alias = "additional-browser-args")]
1808 pub additional_browser_args: Option<String>,
1809 #[serde(default = "default_true")]
1819 pub shadow: bool,
1820 #[serde(default, alias = "window-effects")]
1829 pub window_effects: Option<WindowEffectsConfig>,
1830 #[serde(default)]
1836 pub incognito: bool,
1837 pub parent: Option<String>,
1849 #[serde(alias = "proxy-url")]
1857 pub proxy_url: Option<Url>,
1858 #[serde(default, alias = "zoom-hotkeys-enabled")]
1868 pub zoom_hotkeys_enabled: bool,
1869 #[serde(default, alias = "browser-extensions-enabled")]
1876 pub browser_extensions_enabled: bool,
1877
1878 #[serde(default, alias = "use-https-scheme")]
1888 pub use_https_scheme: bool,
1889 pub devtools: Option<bool>,
1899
1900 #[serde(alias = "background-color")]
1908 pub background_color: Option<Color>,
1909
1910 #[serde(default, alias = "background-throttling")]
1925 pub background_throttling: Option<BackgroundThrottlingPolicy>,
1926 #[serde(default, alias = "javascript-disabled")]
1928 pub javascript_disabled: bool,
1929 #[serde(default = "default_true", alias = "allow-link-preview")]
1932 pub allow_link_preview: bool,
1933 #[serde(
1938 default,
1939 alias = "disable-input-accessory-view",
1940 alias = "disable_input_accessory_view"
1941 )]
1942 pub disable_input_accessory_view: bool,
1943 #[serde(default, alias = "data-directory")]
1954 pub data_directory: Option<PathBuf>,
1955 #[serde(default, alias = "data-store-identifier")]
1967 pub data_store_identifier: Option<[u8; 16]>,
1968
1969 #[serde(default, alias = "scroll-bar-style")]
1982 pub scroll_bar_style: ScrollBarStyle,
1983}
1984
1985impl Default for WindowConfig {
1986 fn default() -> Self {
1987 Self {
1988 label: default_window_label(),
1989 url: WebviewUrl::default(),
1990 create: true,
1991 user_agent: None,
1992 drag_drop_enabled: true,
1993 center: false,
1994 x: None,
1995 y: None,
1996 width: default_width(),
1997 height: default_height(),
1998 min_width: None,
1999 min_height: None,
2000 max_width: None,
2001 max_height: None,
2002 prevent_overflow: None,
2003 resizable: true,
2004 maximizable: true,
2005 minimizable: true,
2006 closable: true,
2007 title: default_title(),
2008 fullscreen: false,
2009 focus: true,
2010 focusable: true,
2011 transparent: false,
2012 maximized: false,
2013 visible: true,
2014 decorations: true,
2015 always_on_bottom: false,
2016 always_on_top: false,
2017 visible_on_all_workspaces: false,
2018 content_protected: false,
2019 skip_taskbar: false,
2020 window_classname: None,
2021 theme: None,
2022 title_bar_style: Default::default(),
2023 traffic_light_position: None,
2024 hidden_title: false,
2025 accept_first_mouse: false,
2026 tabbing_identifier: None,
2027 additional_browser_args: None,
2028 shadow: true,
2029 window_effects: None,
2030 incognito: false,
2031 parent: None,
2032 proxy_url: None,
2033 zoom_hotkeys_enabled: false,
2034 browser_extensions_enabled: false,
2035 use_https_scheme: false,
2036 devtools: None,
2037 background_color: None,
2038 background_throttling: None,
2039 javascript_disabled: false,
2040 allow_link_preview: true,
2041 disable_input_accessory_view: false,
2042 data_directory: None,
2043 data_store_identifier: None,
2044 scroll_bar_style: ScrollBarStyle::Default,
2045 }
2046 }
2047}
2048
2049fn default_window_label() -> String {
2050 "main".to_string()
2051}
2052
2053fn default_width() -> f64 {
2054 800f64
2055}
2056
2057fn default_height() -> f64 {
2058 600f64
2059}
2060
2061fn default_title() -> String {
2062 "Tauri App".to_string()
2063}
2064
2065#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2068#[cfg_attr(feature = "schema", derive(JsonSchema))]
2069#[serde(rename_all = "camelCase", untagged)]
2070pub enum CspDirectiveSources {
2071 Inline(String),
2073 List(Vec<String>),
2075}
2076
2077impl Default for CspDirectiveSources {
2078 fn default() -> Self {
2079 Self::List(Vec::new())
2080 }
2081}
2082
2083impl From<CspDirectiveSources> for Vec<String> {
2084 fn from(sources: CspDirectiveSources) -> Self {
2085 match sources {
2086 CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
2087 CspDirectiveSources::List(l) => l,
2088 }
2089 }
2090}
2091
2092impl CspDirectiveSources {
2093 pub fn contains(&self, source: &str) -> bool {
2095 match self {
2096 Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
2097 Self::List(l) => l.contains(&source.into()),
2098 }
2099 }
2100
2101 pub fn push<S: AsRef<str>>(&mut self, source: S) {
2103 match self {
2104 Self::Inline(s) => {
2105 s.push(' ');
2106 s.push_str(source.as_ref());
2107 }
2108 Self::List(l) => {
2109 l.push(source.as_ref().to_string());
2110 }
2111 }
2112 }
2113
2114 pub fn extend(&mut self, sources: Vec<String>) {
2116 for s in sources {
2117 self.push(s);
2118 }
2119 }
2120}
2121
2122#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2125#[cfg_attr(feature = "schema", derive(JsonSchema))]
2126#[serde(rename_all = "camelCase", untagged)]
2127pub enum Csp {
2128 Policy(String),
2130 DirectiveMap(HashMap<String, CspDirectiveSources>),
2132}
2133
2134impl From<HashMap<String, CspDirectiveSources>> for Csp {
2135 fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
2136 Self::DirectiveMap(map)
2137 }
2138}
2139
2140impl From<Csp> for HashMap<String, CspDirectiveSources> {
2141 fn from(csp: Csp) -> Self {
2142 match csp {
2143 Csp::Policy(policy) => {
2144 let mut map = HashMap::new();
2145 for directive in policy.split(';') {
2146 let mut tokens = directive.trim().split(' ');
2147 if let Some(directive) = tokens.next() {
2148 let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
2149 map.insert(directive.to_string(), CspDirectiveSources::List(sources));
2150 }
2151 }
2152 map
2153 }
2154 Csp::DirectiveMap(m) => m,
2155 }
2156 }
2157}
2158
2159impl Display for Csp {
2160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2161 match self {
2162 Self::Policy(s) => write!(f, "{s}"),
2163 Self::DirectiveMap(m) => {
2164 let len = m.len();
2165 let mut i = 0;
2166 for (directive, sources) in m {
2167 let sources: Vec<String> = sources.clone().into();
2168 write!(f, "{} {}", directive, sources.join(" "))?;
2169 i += 1;
2170 if i != len {
2171 write!(f, "; ")?;
2172 }
2173 }
2174 Ok(())
2175 }
2176 }
2177 }
2178}
2179
2180#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2182#[serde(untagged)]
2183#[cfg_attr(feature = "schema", derive(JsonSchema))]
2184pub enum DisabledCspModificationKind {
2185 Flag(bool),
2188 List(Vec<String>),
2190}
2191
2192impl DisabledCspModificationKind {
2193 pub fn can_modify(&self, directive: &str) -> bool {
2195 match self {
2196 Self::Flag(f) => !f,
2197 Self::List(l) => !l.contains(&directive.into()),
2198 }
2199 }
2200}
2201
2202impl Default for DisabledCspModificationKind {
2203 fn default() -> Self {
2204 Self::Flag(false)
2205 }
2206}
2207
2208#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2217#[serde(untagged)]
2218#[cfg_attr(feature = "schema", derive(JsonSchema))]
2219pub enum FsScope {
2220 AllowedPaths(Vec<PathBuf>),
2222 #[serde(rename_all = "camelCase")]
2224 Scope {
2225 #[serde(default)]
2227 allow: Vec<PathBuf>,
2228 #[serde(default)]
2231 deny: Vec<PathBuf>,
2232 #[serde(alias = "require-literal-leading-dot")]
2241 require_literal_leading_dot: Option<bool>,
2242 },
2243}
2244
2245impl Default for FsScope {
2246 fn default() -> Self {
2247 Self::AllowedPaths(Vec::new())
2248 }
2249}
2250
2251impl FsScope {
2252 pub fn allowed_paths(&self) -> &Vec<PathBuf> {
2254 match self {
2255 Self::AllowedPaths(p) => p,
2256 Self::Scope { allow, .. } => allow,
2257 }
2258 }
2259
2260 pub fn forbidden_paths(&self) -> Option<&Vec<PathBuf>> {
2262 match self {
2263 Self::AllowedPaths(_) => None,
2264 Self::Scope { deny, .. } => Some(deny),
2265 }
2266 }
2267}
2268
2269#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2273#[cfg_attr(feature = "schema", derive(JsonSchema))]
2274#[serde(rename_all = "camelCase", deny_unknown_fields)]
2275pub struct AssetProtocolConfig {
2276 #[serde(default)]
2278 pub scope: FsScope,
2279 #[serde(default)]
2281 pub enable: bool,
2282}
2283
2284#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2288#[cfg_attr(feature = "schema", derive(JsonSchema))]
2289#[serde(rename_all = "camelCase", untagged)]
2290pub enum HeaderSource {
2291 Inline(String),
2293 List(Vec<String>),
2295 Map(HashMap<String, String>),
2297}
2298
2299impl Display for HeaderSource {
2300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2301 match self {
2302 Self::Inline(s) => write!(f, "{s}"),
2303 Self::List(l) => write!(f, "{}", l.join(", ")),
2304 Self::Map(m) => {
2305 let len = m.len();
2306 let mut i = 0;
2307 for (key, value) in m {
2308 write!(f, "{key} {value}")?;
2309 i += 1;
2310 if i != len {
2311 write!(f, "; ")?;
2312 }
2313 }
2314 Ok(())
2315 }
2316 }
2317 }
2318}
2319
2320pub trait HeaderAddition {
2324 fn add_configured_headers(self, headers: Option<&HeaderConfig>) -> http::response::Builder;
2326}
2327
2328impl HeaderAddition for Builder {
2329 fn add_configured_headers(mut self, headers: Option<&HeaderConfig>) -> http::response::Builder {
2333 if let Some(headers) = headers {
2334 if let Some(value) = &headers.access_control_allow_credentials {
2336 self = self.header("Access-Control-Allow-Credentials", value.to_string());
2337 };
2338
2339 if let Some(value) = &headers.access_control_allow_headers {
2341 self = self.header("Access-Control-Allow-Headers", value.to_string());
2342 };
2343
2344 if let Some(value) = &headers.access_control_allow_methods {
2346 self = self.header("Access-Control-Allow-Methods", value.to_string());
2347 };
2348
2349 if let Some(value) = &headers.access_control_expose_headers {
2351 self = self.header("Access-Control-Expose-Headers", value.to_string());
2352 };
2353
2354 if let Some(value) = &headers.access_control_max_age {
2356 self = self.header("Access-Control-Max-Age", value.to_string());
2357 };
2358
2359 if let Some(value) = &headers.cross_origin_embedder_policy {
2361 self = self.header("Cross-Origin-Embedder-Policy", value.to_string());
2362 };
2363
2364 if let Some(value) = &headers.cross_origin_opener_policy {
2366 self = self.header("Cross-Origin-Opener-Policy", value.to_string());
2367 };
2368
2369 if let Some(value) = &headers.cross_origin_resource_policy {
2371 self = self.header("Cross-Origin-Resource-Policy", value.to_string());
2372 };
2373
2374 if let Some(value) = &headers.permissions_policy {
2376 self = self.header("Permission-Policy", value.to_string());
2377 };
2378
2379 if let Some(value) = &headers.service_worker_allowed {
2380 self = self.header("Service-Worker-Allowed", value.to_string());
2381 }
2382
2383 if let Some(value) = &headers.timing_allow_origin {
2385 self = self.header("Timing-Allow-Origin", value.to_string());
2386 };
2387
2388 if let Some(value) = &headers.x_content_type_options {
2390 self = self.header("X-Content-Type-Options", value.to_string());
2391 };
2392
2393 if let Some(value) = &headers.tauri_custom_header {
2395 self = self.header("Tauri-Custom-Header", value.to_string());
2397 };
2398 }
2399 self
2400 }
2401}
2402
2403#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2455#[cfg_attr(feature = "schema", derive(JsonSchema))]
2456#[serde(deny_unknown_fields)]
2457pub struct HeaderConfig {
2458 #[serde(rename = "Access-Control-Allow-Credentials")]
2463 pub access_control_allow_credentials: Option<HeaderSource>,
2464 #[serde(rename = "Access-Control-Allow-Headers")]
2472 pub access_control_allow_headers: Option<HeaderSource>,
2473 #[serde(rename = "Access-Control-Allow-Methods")]
2478 pub access_control_allow_methods: Option<HeaderSource>,
2479 #[serde(rename = "Access-Control-Expose-Headers")]
2485 pub access_control_expose_headers: Option<HeaderSource>,
2486 #[serde(rename = "Access-Control-Max-Age")]
2493 pub access_control_max_age: Option<HeaderSource>,
2494 #[serde(rename = "Cross-Origin-Embedder-Policy")]
2499 pub cross_origin_embedder_policy: Option<HeaderSource>,
2500 #[serde(rename = "Cross-Origin-Opener-Policy")]
2507 pub cross_origin_opener_policy: Option<HeaderSource>,
2508 #[serde(rename = "Cross-Origin-Resource-Policy")]
2513 pub cross_origin_resource_policy: Option<HeaderSource>,
2514 #[serde(rename = "Permissions-Policy")]
2519 pub permissions_policy: Option<HeaderSource>,
2520 #[serde(rename = "Service-Worker-Allowed")]
2530 pub service_worker_allowed: Option<HeaderSource>,
2531 #[serde(rename = "Timing-Allow-Origin")]
2537 pub timing_allow_origin: Option<HeaderSource>,
2538 #[serde(rename = "X-Content-Type-Options")]
2545 pub x_content_type_options: Option<HeaderSource>,
2546 #[serde(rename = "Tauri-Custom-Header")]
2551 pub tauri_custom_header: Option<HeaderSource>,
2552}
2553
2554impl HeaderConfig {
2555 pub fn new() -> Self {
2557 HeaderConfig {
2558 access_control_allow_credentials: None,
2559 access_control_allow_methods: None,
2560 access_control_allow_headers: None,
2561 access_control_expose_headers: None,
2562 access_control_max_age: None,
2563 cross_origin_embedder_policy: None,
2564 cross_origin_opener_policy: None,
2565 cross_origin_resource_policy: None,
2566 permissions_policy: None,
2567 service_worker_allowed: None,
2568 timing_allow_origin: None,
2569 x_content_type_options: None,
2570 tauri_custom_header: None,
2571 }
2572 }
2573}
2574
2575#[skip_serializing_none]
2579#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2580#[cfg_attr(feature = "schema", derive(JsonSchema))]
2581#[serde(rename_all = "camelCase", deny_unknown_fields)]
2582pub struct SecurityConfig {
2583 pub csp: Option<Csp>,
2589 #[serde(alias = "dev-csp")]
2594 pub dev_csp: Option<Csp>,
2595 #[serde(default, alias = "freeze-prototype")]
2597 pub freeze_prototype: bool,
2598 #[serde(default, alias = "dangerous-disable-asset-csp-modification")]
2611 pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
2612 #[serde(default, alias = "asset-protocol")]
2614 pub asset_protocol: AssetProtocolConfig,
2615 #[serde(default)]
2617 pub pattern: PatternKind,
2618 #[serde(default)]
2641 pub capabilities: Vec<CapabilityEntry>,
2642 #[serde(default)]
2645 pub headers: Option<HeaderConfig>,
2646}
2647
2648#[derive(Debug, Clone, PartialEq, Serialize)]
2650#[cfg_attr(feature = "schema", derive(JsonSchema))]
2651#[serde(untagged)]
2652pub enum CapabilityEntry {
2653 Inlined(Capability),
2655 Reference(String),
2657}
2658
2659impl<'de> Deserialize<'de> for CapabilityEntry {
2660 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2661 where
2662 D: Deserializer<'de>,
2663 {
2664 UntaggedEnumVisitor::new()
2665 .string(|string| Ok(Self::Reference(string.to_owned())))
2666 .map(|map| map.deserialize::<Capability>().map(Self::Inlined))
2667 .deserialize(deserializer)
2668 }
2669}
2670
2671#[skip_serializing_none]
2673#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
2674#[serde(rename_all = "lowercase", tag = "use", content = "options")]
2675#[cfg_attr(feature = "schema", derive(JsonSchema))]
2676pub enum PatternKind {
2677 #[default]
2679 Brownfield,
2680 Isolation {
2682 dir: PathBuf,
2684 },
2685}
2686
2687#[skip_serializing_none]
2691#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
2692#[cfg_attr(feature = "schema", derive(JsonSchema))]
2693#[serde(rename_all = "camelCase", deny_unknown_fields)]
2694pub struct AppConfig {
2695 #[serde(default)]
2750 pub windows: Vec<WindowConfig>,
2751 #[serde(default)]
2753 pub security: SecurityConfig,
2754 #[serde(alias = "tray-icon")]
2756 pub tray_icon: Option<TrayIconConfig>,
2757 #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
2759 pub macos_private_api: bool,
2760 #[serde(default, alias = "with-global-tauri")]
2762 pub with_global_tauri: bool,
2763 #[serde(rename = "enableGTKAppId", alias = "enable-gtk-app-id", default)]
2765 pub enable_gtk_app_id: bool,
2766}
2767
2768impl AppConfig {
2769 pub fn all_features() -> Vec<&'static str> {
2771 vec![
2772 "tray-icon",
2773 "macos-private-api",
2774 "protocol-asset",
2775 "isolation",
2776 ]
2777 }
2778
2779 pub fn features(&self) -> Vec<&str> {
2781 let mut features = Vec::new();
2782 if self.tray_icon.is_some() {
2783 features.push("tray-icon");
2784 }
2785 if self.macos_private_api {
2786 features.push("macos-private-api");
2787 }
2788 if self.security.asset_protocol.enable {
2789 features.push("protocol-asset");
2790 }
2791
2792 if let PatternKind::Isolation { .. } = self.security.pattern {
2793 features.push("isolation");
2794 }
2795
2796 features.sort_unstable();
2797 features
2798 }
2799}
2800
2801#[skip_serializing_none]
2805#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
2806#[cfg_attr(feature = "schema", derive(JsonSchema))]
2807#[serde(rename_all = "camelCase", deny_unknown_fields)]
2808pub struct TrayIconConfig {
2809 pub id: Option<String>,
2811 #[serde(alias = "icon-path")]
2817 pub icon_path: PathBuf,
2818 #[serde(default, alias = "icon-as-template")]
2820 pub icon_as_template: bool,
2821 #[serde(default = "default_true", alias = "menu-on-left-click")]
2827 #[deprecated(since = "2.2.0", note = "Use `show_menu_on_left_click` instead.")]
2828 pub menu_on_left_click: bool,
2829 #[serde(default = "default_true", alias = "show-menu-on-left-click")]
2835 pub show_menu_on_left_click: bool,
2836 pub title: Option<String>,
2838 pub tooltip: Option<String>,
2840}
2841
2842#[skip_serializing_none]
2844#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2845#[cfg_attr(feature = "schema", derive(JsonSchema))]
2846#[serde(rename_all = "camelCase", deny_unknown_fields)]
2847pub struct IosConfig {
2848 pub template: Option<PathBuf>,
2852 pub frameworks: Option<Vec<String>>,
2856 #[serde(alias = "development-team")]
2859 pub development_team: Option<String>,
2860 #[serde(alias = "bundle-version")]
2864 pub bundle_version: Option<String>,
2865 #[serde(
2869 alias = "minimum-system-version",
2870 default = "ios_minimum_system_version"
2871 )]
2872 pub minimum_system_version: String,
2873 #[serde(alias = "info-plist")]
2877 pub info_plist: Option<PathBuf>,
2878}
2879
2880impl Default for IosConfig {
2881 fn default() -> Self {
2882 Self {
2883 template: None,
2884 frameworks: None,
2885 development_team: None,
2886 bundle_version: None,
2887 minimum_system_version: ios_minimum_system_version(),
2888 info_plist: None,
2889 }
2890 }
2891}
2892
2893#[skip_serializing_none]
2895#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2896#[cfg_attr(feature = "schema", derive(JsonSchema))]
2897#[serde(rename_all = "camelCase", deny_unknown_fields)]
2898pub struct AndroidConfig {
2899 #[serde(alias = "min-sdk-version", default = "default_min_sdk_version")]
2902 pub min_sdk_version: u32,
2903
2904 #[serde(alias = "version-code")]
2910 #[cfg_attr(feature = "schema", validate(range(min = 1, max = 2_100_000_000)))]
2911 pub version_code: Option<u32>,
2912
2913 #[serde(alias = "auto-increment-version-code", default)]
2921 pub auto_increment_version_code: bool,
2922}
2923
2924impl Default for AndroidConfig {
2925 fn default() -> Self {
2926 Self {
2927 min_sdk_version: default_min_sdk_version(),
2928 version_code: None,
2929 auto_increment_version_code: false,
2930 }
2931 }
2932}
2933
2934fn default_min_sdk_version() -> u32 {
2935 24
2936}
2937
2938#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2940#[cfg_attr(feature = "schema", derive(JsonSchema))]
2941#[serde(untagged, deny_unknown_fields)]
2942#[non_exhaustive]
2943pub enum FrontendDist {
2944 Url(Url),
2946 Directory(PathBuf),
2948 Files(Vec<PathBuf>),
2950}
2951
2952impl std::fmt::Display for FrontendDist {
2953 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2954 match self {
2955 Self::Url(url) => write!(f, "{url}"),
2956 Self::Directory(p) => write!(f, "{}", p.display()),
2957 Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
2958 }
2959 }
2960}
2961
2962#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2964#[cfg_attr(feature = "schema", derive(JsonSchema))]
2965#[serde(rename_all = "camelCase", untagged)]
2966pub enum BeforeDevCommand {
2967 Script(String),
2969 ScriptWithOptions {
2971 script: String,
2973 cwd: Option<String>,
2975 #[serde(default)]
2977 wait: bool,
2978 },
2979}
2980
2981#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
2983#[cfg_attr(feature = "schema", derive(JsonSchema))]
2984#[serde(rename_all = "camelCase", untagged)]
2985pub enum HookCommand {
2986 Script(String),
2988 ScriptWithOptions {
2990 script: String,
2992 cwd: Option<String>,
2994 },
2995}
2996
2997#[skip_serializing_none]
2999#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
3000#[cfg_attr(feature = "schema", derive(JsonSchema))]
3001#[serde(untagged)]
3002pub enum RunnerConfig {
3003 String(String),
3005 Object {
3007 cmd: String,
3009 cwd: Option<String>,
3011 args: Option<Vec<String>>,
3013 },
3014}
3015
3016impl Default for RunnerConfig {
3017 fn default() -> Self {
3018 RunnerConfig::String("cargo".to_string())
3019 }
3020}
3021
3022impl RunnerConfig {
3023 pub fn cmd(&self) -> &str {
3025 match self {
3026 RunnerConfig::String(cmd) => cmd,
3027 RunnerConfig::Object { cmd, .. } => cmd,
3028 }
3029 }
3030
3031 pub fn cwd(&self) -> Option<&str> {
3033 match self {
3034 RunnerConfig::String(_) => None,
3035 RunnerConfig::Object { cwd, .. } => cwd.as_deref(),
3036 }
3037 }
3038
3039 pub fn args(&self) -> Option<&[String]> {
3041 match self {
3042 RunnerConfig::String(_) => None,
3043 RunnerConfig::Object { args, .. } => args.as_deref(),
3044 }
3045 }
3046}
3047
3048impl std::str::FromStr for RunnerConfig {
3049 type Err = std::convert::Infallible;
3050
3051 fn from_str(s: &str) -> Result<Self, Self::Err> {
3052 Ok(RunnerConfig::String(s.to_string()))
3053 }
3054}
3055
3056impl From<&str> for RunnerConfig {
3057 fn from(s: &str) -> Self {
3058 RunnerConfig::String(s.to_string())
3059 }
3060}
3061
3062impl From<String> for RunnerConfig {
3063 fn from(s: String) -> Self {
3064 RunnerConfig::String(s)
3065 }
3066}
3067
3068#[skip_serializing_none]
3072#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
3073#[cfg_attr(feature = "schema", derive(JsonSchema))]
3074#[serde(rename_all = "camelCase", deny_unknown_fields)]
3075pub struct BuildConfig {
3076 pub runner: Option<RunnerConfig>,
3078 #[serde(alias = "dev-url")]
3086 pub dev_url: Option<Url>,
3087 #[serde(alias = "frontend-dist")]
3101 pub frontend_dist: Option<FrontendDist>,
3102 #[serde(alias = "before-dev-command")]
3106 pub before_dev_command: Option<BeforeDevCommand>,
3107 #[serde(alias = "before-build-command")]
3111 pub before_build_command: Option<HookCommand>,
3112 #[serde(alias = "before-bundle-command")]
3116 pub before_bundle_command: Option<HookCommand>,
3117 pub features: Option<Vec<String>>,
3119 #[serde(alias = "remove-unused-commands", default)]
3127 pub remove_unused_commands: bool,
3128 #[serde(alias = "additional-watch-directories", default)]
3130 pub additional_watch_folders: Vec<PathBuf>,
3131}
3132
3133#[derive(Debug, PartialEq, Eq)]
3134struct PackageVersion(String);
3135
3136impl<'d> serde::Deserialize<'d> for PackageVersion {
3137 fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
3138 struct PackageVersionVisitor;
3139
3140 impl Visitor<'_> for PackageVersionVisitor {
3141 type Value = PackageVersion;
3142
3143 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
3144 write!(
3145 formatter,
3146 "a semver string or a path to a package.json file"
3147 )
3148 }
3149
3150 fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
3151 let path = PathBuf::from(value);
3152 if path.exists() {
3153 let json_str = read_to_string(&path)
3154 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
3155 let package_json: serde_json::Value = serde_json::from_str(&json_str)
3156 .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
3157 if let Some(obj) = package_json.as_object() {
3158 let version = obj
3159 .get("version")
3160 .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
3161 .as_str()
3162 .ok_or_else(|| {
3163 DeError::custom(format!("`{} > version` must be a string", path.display()))
3164 })?;
3165 Ok(PackageVersion(
3166 Version::from_str(version)
3167 .map_err(|_| {
3168 DeError::custom("`tauri.conf.json > version` must be a semver string")
3169 })?
3170 .to_string(),
3171 ))
3172 } else {
3173 Err(DeError::custom(
3174 "`tauri.conf.json > version` value is not a path to a JSON object",
3175 ))
3176 }
3177 } else {
3178 Ok(PackageVersion(
3179 Version::from_str(value)
3180 .map_err(|_| DeError::custom("`tauri.conf.json > version` must be a semver string"))?
3181 .to_string(),
3182 ))
3183 }
3184 }
3185 }
3186
3187 deserializer.deserialize_string(PackageVersionVisitor {})
3188 }
3189}
3190
3191fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
3192where
3193 D: Deserializer<'de>,
3194{
3195 Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
3196}
3197
3198#[skip_serializing_none]
3264#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
3265#[cfg_attr(feature = "schema", derive(JsonSchema))]
3266#[serde(rename_all = "camelCase", deny_unknown_fields)]
3267pub struct Config {
3268 #[serde(rename = "$schema")]
3270 pub schema: Option<String>,
3271 #[serde(alias = "product-name")]
3273 #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
3274 pub product_name: Option<String>,
3275 #[serde(alias = "main-binary-name")]
3289 pub main_binary_name: Option<String>,
3290 #[serde(deserialize_with = "version_deserializer", default)]
3306 pub version: Option<String>,
3307 pub identifier: String,
3313 #[serde(default)]
3315 pub app: AppConfig,
3316 #[serde(default)]
3318 pub build: BuildConfig,
3319 #[serde(default)]
3321 pub bundle: BundleConfig,
3322 #[serde(default)]
3324 pub plugins: PluginConfig,
3325}
3326
3327#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
3331#[cfg_attr(feature = "schema", derive(JsonSchema))]
3332pub struct PluginConfig(pub HashMap<String, JsonValue>);
3333
3334#[cfg(feature = "build")]
3340mod build {
3341 use super::*;
3342 use crate::{literal_struct, tokens::*};
3343 use proc_macro2::TokenStream;
3344 use quote::{quote, ToTokens, TokenStreamExt};
3345 use std::convert::identity;
3346
3347 impl ToTokens for WebviewUrl {
3348 fn to_tokens(&self, tokens: &mut TokenStream) {
3349 let prefix = quote! { ::tauri::utils::config::WebviewUrl };
3350
3351 tokens.append_all(match self {
3352 Self::App(path) => {
3353 let path = path_buf_lit(path);
3354 quote! { #prefix::App(#path) }
3355 }
3356 Self::External(url) => {
3357 let url = url_lit(url);
3358 quote! { #prefix::External(#url) }
3359 }
3360 Self::CustomProtocol(url) => {
3361 let url = url_lit(url);
3362 quote! { #prefix::CustomProtocol(#url) }
3363 }
3364 })
3365 }
3366 }
3367
3368 impl ToTokens for BackgroundThrottlingPolicy {
3369 fn to_tokens(&self, tokens: &mut TokenStream) {
3370 let prefix = quote! { ::tauri::utils::config::BackgroundThrottlingPolicy };
3371 tokens.append_all(match self {
3372 Self::Disabled => quote! { #prefix::Disabled },
3373 Self::Throttle => quote! { #prefix::Throttle },
3374 Self::Suspend => quote! { #prefix::Suspend },
3375 })
3376 }
3377 }
3378
3379 impl ToTokens for crate::Theme {
3380 fn to_tokens(&self, tokens: &mut TokenStream) {
3381 let prefix = quote! { ::tauri::utils::Theme };
3382
3383 tokens.append_all(match self {
3384 Self::Light => quote! { #prefix::Light },
3385 Self::Dark => quote! { #prefix::Dark },
3386 })
3387 }
3388 }
3389
3390 impl ToTokens for Color {
3391 fn to_tokens(&self, tokens: &mut TokenStream) {
3392 let Color(r, g, b, a) = self;
3393 tokens.append_all(quote! {::tauri::utils::config::Color(#r,#g,#b,#a)});
3394 }
3395 }
3396 impl ToTokens for WindowEffectsConfig {
3397 fn to_tokens(&self, tokens: &mut TokenStream) {
3398 let effects = vec_lit(self.effects.clone(), |d| d);
3399 let state = opt_lit(self.state.as_ref());
3400 let radius = opt_lit(self.radius.as_ref());
3401 let color = opt_lit(self.color.as_ref());
3402
3403 literal_struct!(
3404 tokens,
3405 ::tauri::utils::config::WindowEffectsConfig,
3406 effects,
3407 state,
3408 radius,
3409 color
3410 )
3411 }
3412 }
3413
3414 impl ToTokens for crate::TitleBarStyle {
3415 fn to_tokens(&self, tokens: &mut TokenStream) {
3416 let prefix = quote! { ::tauri::utils::TitleBarStyle };
3417
3418 tokens.append_all(match self {
3419 Self::Visible => quote! { #prefix::Visible },
3420 Self::Transparent => quote! { #prefix::Transparent },
3421 Self::Overlay => quote! { #prefix::Overlay },
3422 })
3423 }
3424 }
3425
3426 impl ToTokens for LogicalPosition {
3427 fn to_tokens(&self, tokens: &mut TokenStream) {
3428 let LogicalPosition { x, y } = self;
3429 literal_struct!(tokens, ::tauri::utils::config::LogicalPosition, x, y)
3430 }
3431 }
3432
3433 impl ToTokens for crate::WindowEffect {
3434 fn to_tokens(&self, tokens: &mut TokenStream) {
3435 let prefix = quote! { ::tauri::utils::WindowEffect };
3436
3437 #[allow(deprecated)]
3438 tokens.append_all(match self {
3439 WindowEffect::AppearanceBased => quote! { #prefix::AppearanceBased},
3440 WindowEffect::Light => quote! { #prefix::Light},
3441 WindowEffect::Dark => quote! { #prefix::Dark},
3442 WindowEffect::MediumLight => quote! { #prefix::MediumLight},
3443 WindowEffect::UltraDark => quote! { #prefix::UltraDark},
3444 WindowEffect::Titlebar => quote! { #prefix::Titlebar},
3445 WindowEffect::Selection => quote! { #prefix::Selection},
3446 WindowEffect::Menu => quote! { #prefix::Menu},
3447 WindowEffect::Popover => quote! { #prefix::Popover},
3448 WindowEffect::Sidebar => quote! { #prefix::Sidebar},
3449 WindowEffect::HeaderView => quote! { #prefix::HeaderView},
3450 WindowEffect::Sheet => quote! { #prefix::Sheet},
3451 WindowEffect::WindowBackground => quote! { #prefix::WindowBackground},
3452 WindowEffect::HudWindow => quote! { #prefix::HudWindow},
3453 WindowEffect::FullScreenUI => quote! { #prefix::FullScreenUI},
3454 WindowEffect::Tooltip => quote! { #prefix::Tooltip},
3455 WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
3456 WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
3457 WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
3458 WindowEffect::Mica => quote! { #prefix::Mica},
3459 WindowEffect::MicaDark => quote! { #prefix::MicaDark},
3460 WindowEffect::MicaLight => quote! { #prefix::MicaLight},
3461 WindowEffect::Blur => quote! { #prefix::Blur},
3462 WindowEffect::Acrylic => quote! { #prefix::Acrylic},
3463 WindowEffect::Tabbed => quote! { #prefix::Tabbed },
3464 WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
3465 WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
3466 })
3467 }
3468 }
3469
3470 impl ToTokens for crate::WindowEffectState {
3471 fn to_tokens(&self, tokens: &mut TokenStream) {
3472 let prefix = quote! { ::tauri::utils::WindowEffectState };
3473
3474 #[allow(deprecated)]
3475 tokens.append_all(match self {
3476 WindowEffectState::Active => quote! { #prefix::Active},
3477 WindowEffectState::FollowsWindowActiveState => quote! { #prefix::FollowsWindowActiveState},
3478 WindowEffectState::Inactive => quote! { #prefix::Inactive},
3479 })
3480 }
3481 }
3482
3483 impl ToTokens for PreventOverflowMargin {
3484 fn to_tokens(&self, tokens: &mut TokenStream) {
3485 let width = self.width;
3486 let height = self.height;
3487
3488 literal_struct!(
3489 tokens,
3490 ::tauri::utils::config::PreventOverflowMargin,
3491 width,
3492 height
3493 )
3494 }
3495 }
3496
3497 impl ToTokens for PreventOverflowConfig {
3498 fn to_tokens(&self, tokens: &mut TokenStream) {
3499 let prefix = quote! { ::tauri::utils::config::PreventOverflowConfig };
3500
3501 #[allow(deprecated)]
3502 tokens.append_all(match self {
3503 Self::Enable(enable) => quote! { #prefix::Enable(#enable) },
3504 Self::Margin(margin) => quote! { #prefix::Margin(#margin) },
3505 })
3506 }
3507 }
3508
3509 impl ToTokens for ScrollBarStyle {
3510 fn to_tokens(&self, tokens: &mut TokenStream) {
3511 let prefix = quote! { ::tauri::utils::config::ScrollBarStyle };
3512
3513 tokens.append_all(match self {
3514 Self::Default => quote! { #prefix::Default },
3515 Self::FluentOverlay => quote! { #prefix::FluentOverlay },
3516 })
3517 }
3518 }
3519
3520 impl ToTokens for WindowConfig {
3521 fn to_tokens(&self, tokens: &mut TokenStream) {
3522 let label = str_lit(&self.label);
3523 let create = &self.create;
3524 let url = &self.url;
3525 let user_agent = opt_str_lit(self.user_agent.as_ref());
3526 let drag_drop_enabled = self.drag_drop_enabled;
3527 let center = self.center;
3528 let x = opt_lit(self.x.as_ref());
3529 let y = opt_lit(self.y.as_ref());
3530 let width = self.width;
3531 let height = self.height;
3532 let min_width = opt_lit(self.min_width.as_ref());
3533 let min_height = opt_lit(self.min_height.as_ref());
3534 let max_width = opt_lit(self.max_width.as_ref());
3535 let max_height = opt_lit(self.max_height.as_ref());
3536 let prevent_overflow = opt_lit(self.prevent_overflow.as_ref());
3537 let resizable = self.resizable;
3538 let maximizable = self.maximizable;
3539 let minimizable = self.minimizable;
3540 let closable = self.closable;
3541 let title = str_lit(&self.title);
3542 let proxy_url = opt_lit(self.proxy_url.as_ref().map(url_lit).as_ref());
3543 let fullscreen = self.fullscreen;
3544 let focus = self.focus;
3545 let focusable = self.focusable;
3546 let transparent = self.transparent;
3547 let maximized = self.maximized;
3548 let visible = self.visible;
3549 let decorations = self.decorations;
3550 let always_on_bottom = self.always_on_bottom;
3551 let always_on_top = self.always_on_top;
3552 let visible_on_all_workspaces = self.visible_on_all_workspaces;
3553 let content_protected = self.content_protected;
3554 let skip_taskbar = self.skip_taskbar;
3555 let window_classname = opt_str_lit(self.window_classname.as_ref());
3556 let theme = opt_lit(self.theme.as_ref());
3557 let title_bar_style = &self.title_bar_style;
3558 let traffic_light_position = opt_lit(self.traffic_light_position.as_ref());
3559 let hidden_title = self.hidden_title;
3560 let accept_first_mouse = self.accept_first_mouse;
3561 let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
3562 let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
3563 let shadow = self.shadow;
3564 let window_effects = opt_lit(self.window_effects.as_ref());
3565 let incognito = self.incognito;
3566 let parent = opt_str_lit(self.parent.as_ref());
3567 let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled;
3568 let browser_extensions_enabled = self.browser_extensions_enabled;
3569 let use_https_scheme = self.use_https_scheme;
3570 let devtools = opt_lit(self.devtools.as_ref());
3571 let background_color = opt_lit(self.background_color.as_ref());
3572 let background_throttling = opt_lit(self.background_throttling.as_ref());
3573 let javascript_disabled = self.javascript_disabled;
3574 let allow_link_preview = self.allow_link_preview;
3575 let disable_input_accessory_view = self.disable_input_accessory_view;
3576 let data_directory = opt_lit(self.data_directory.as_ref().map(path_buf_lit).as_ref());
3577 let data_store_identifier = opt_vec_lit(self.data_store_identifier, identity);
3578 let scroll_bar_style = &self.scroll_bar_style;
3579
3580 literal_struct!(
3581 tokens,
3582 ::tauri::utils::config::WindowConfig,
3583 label,
3584 url,
3585 create,
3586 user_agent,
3587 drag_drop_enabled,
3588 center,
3589 x,
3590 y,
3591 width,
3592 height,
3593 min_width,
3594 min_height,
3595 max_width,
3596 max_height,
3597 prevent_overflow,
3598 resizable,
3599 maximizable,
3600 minimizable,
3601 closable,
3602 title,
3603 proxy_url,
3604 fullscreen,
3605 focus,
3606 focusable,
3607 transparent,
3608 maximized,
3609 visible,
3610 decorations,
3611 always_on_bottom,
3612 always_on_top,
3613 visible_on_all_workspaces,
3614 content_protected,
3615 skip_taskbar,
3616 window_classname,
3617 theme,
3618 title_bar_style,
3619 traffic_light_position,
3620 hidden_title,
3621 accept_first_mouse,
3622 tabbing_identifier,
3623 additional_browser_args,
3624 shadow,
3625 window_effects,
3626 incognito,
3627 parent,
3628 zoom_hotkeys_enabled,
3629 browser_extensions_enabled,
3630 use_https_scheme,
3631 devtools,
3632 background_color,
3633 background_throttling,
3634 javascript_disabled,
3635 allow_link_preview,
3636 disable_input_accessory_view,
3637 data_directory,
3638 data_store_identifier,
3639 scroll_bar_style
3640 );
3641 }
3642 }
3643
3644 impl ToTokens for PatternKind {
3645 fn to_tokens(&self, tokens: &mut TokenStream) {
3646 let prefix = quote! { ::tauri::utils::config::PatternKind };
3647
3648 tokens.append_all(match self {
3649 Self::Brownfield => quote! { #prefix::Brownfield },
3650 #[cfg(not(feature = "isolation"))]
3651 Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
3652 #[cfg(feature = "isolation")]
3653 Self::Isolation { dir } => {
3654 let dir = path_buf_lit(dir);
3655 quote! { #prefix::Isolation { dir: #dir } }
3656 }
3657 })
3658 }
3659 }
3660
3661 impl ToTokens for WebviewInstallMode {
3662 fn to_tokens(&self, tokens: &mut TokenStream) {
3663 let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
3664
3665 tokens.append_all(match self {
3666 Self::Skip => quote! { #prefix::Skip },
3667 Self::DownloadBootstrapper { silent } => {
3668 quote! { #prefix::DownloadBootstrapper { silent: #silent } }
3669 }
3670 Self::EmbedBootstrapper { silent } => {
3671 quote! { #prefix::EmbedBootstrapper { silent: #silent } }
3672 }
3673 Self::OfflineInstaller { silent } => {
3674 quote! { #prefix::OfflineInstaller { silent: #silent } }
3675 }
3676 Self::FixedRuntime { path } => {
3677 let path = path_buf_lit(path);
3678 quote! { #prefix::FixedRuntime { path: #path } }
3679 }
3680 })
3681 }
3682 }
3683
3684 impl ToTokens for WindowsConfig {
3685 fn to_tokens(&self, tokens: &mut TokenStream) {
3686 let webview_install_mode = &self.webview_install_mode;
3687 tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
3688 webview_install_mode: #webview_install_mode,
3689 ..Default::default()
3690 }})
3691 }
3692 }
3693
3694 impl ToTokens for BundleConfig {
3695 fn to_tokens(&self, tokens: &mut TokenStream) {
3696 let publisher = quote!(None);
3697 let homepage = quote!(None);
3698 let icon = vec_lit(&self.icon, str_lit);
3699 let active = self.active;
3700 let targets = quote!(Default::default());
3701 let create_updater_artifacts = quote!(Default::default());
3702 let resources = quote!(None);
3703 let copyright = quote!(None);
3704 let category = quote!(None);
3705 let file_associations = quote!(None);
3706 let short_description = quote!(None);
3707 let long_description = quote!(None);
3708 let use_local_tools_dir = self.use_local_tools_dir;
3709 let external_bin = opt_vec_lit(self.external_bin.as_ref(), str_lit);
3710 let windows = &self.windows;
3711 let license = opt_str_lit(self.license.as_ref());
3712 let license_file = opt_lit(self.license_file.as_ref().map(path_buf_lit).as_ref());
3713 let linux = quote!(Default::default());
3714 let macos = quote!(Default::default());
3715 let ios = quote!(Default::default());
3716 let android = quote!(Default::default());
3717
3718 literal_struct!(
3719 tokens,
3720 ::tauri::utils::config::BundleConfig,
3721 active,
3722 publisher,
3723 homepage,
3724 icon,
3725 targets,
3726 create_updater_artifacts,
3727 resources,
3728 copyright,
3729 category,
3730 license,
3731 license_file,
3732 file_associations,
3733 short_description,
3734 long_description,
3735 use_local_tools_dir,
3736 external_bin,
3737 windows,
3738 linux,
3739 macos,
3740 ios,
3741 android
3742 );
3743 }
3744 }
3745
3746 impl ToTokens for FrontendDist {
3747 fn to_tokens(&self, tokens: &mut TokenStream) {
3748 let prefix = quote! { ::tauri::utils::config::FrontendDist };
3749
3750 tokens.append_all(match self {
3751 Self::Url(url) => {
3752 let url = url_lit(url);
3753 quote! { #prefix::Url(#url) }
3754 }
3755 Self::Directory(path) => {
3756 let path = path_buf_lit(path);
3757 quote! { #prefix::Directory(#path) }
3758 }
3759 Self::Files(files) => {
3760 let files = vec_lit(files, path_buf_lit);
3761 quote! { #prefix::Files(#files) }
3762 }
3763 })
3764 }
3765 }
3766
3767 impl ToTokens for RunnerConfig {
3768 fn to_tokens(&self, tokens: &mut TokenStream) {
3769 let prefix = quote! { ::tauri::utils::config::RunnerConfig };
3770
3771 tokens.append_all(match self {
3772 Self::String(cmd) => {
3773 let cmd = cmd.as_str();
3774 quote!(#prefix::String(#cmd.into()))
3775 }
3776 Self::Object { cmd, cwd, args } => {
3777 let cmd = cmd.as_str();
3778 let cwd = opt_str_lit(cwd.as_ref());
3779 let args = opt_lit(args.as_ref().map(|v| vec_lit(v, str_lit)).as_ref());
3780 quote!(#prefix::Object {
3781 cmd: #cmd.into(),
3782 cwd: #cwd,
3783 args: #args,
3784 })
3785 }
3786 })
3787 }
3788 }
3789
3790 impl ToTokens for BuildConfig {
3791 fn to_tokens(&self, tokens: &mut TokenStream) {
3792 let dev_url = opt_lit(self.dev_url.as_ref().map(url_lit).as_ref());
3793 let frontend_dist = opt_lit(self.frontend_dist.as_ref());
3794 let runner = opt_lit(self.runner.as_ref());
3795 let before_dev_command = quote!(None);
3796 let before_build_command = quote!(None);
3797 let before_bundle_command = quote!(None);
3798 let features = quote!(None);
3799 let remove_unused_commands = quote!(false);
3800 let additional_watch_folders = quote!(Vec::new());
3801
3802 literal_struct!(
3803 tokens,
3804 ::tauri::utils::config::BuildConfig,
3805 runner,
3806 dev_url,
3807 frontend_dist,
3808 before_dev_command,
3809 before_build_command,
3810 before_bundle_command,
3811 features,
3812 remove_unused_commands,
3813 additional_watch_folders
3814 );
3815 }
3816 }
3817
3818 impl ToTokens for CspDirectiveSources {
3819 fn to_tokens(&self, tokens: &mut TokenStream) {
3820 let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
3821
3822 tokens.append_all(match self {
3823 Self::Inline(sources) => {
3824 let sources = sources.as_str();
3825 quote!(#prefix::Inline(#sources.into()))
3826 }
3827 Self::List(list) => {
3828 let list = vec_lit(list, str_lit);
3829 quote!(#prefix::List(#list))
3830 }
3831 })
3832 }
3833 }
3834
3835 impl ToTokens for Csp {
3836 fn to_tokens(&self, tokens: &mut TokenStream) {
3837 let prefix = quote! { ::tauri::utils::config::Csp };
3838
3839 tokens.append_all(match self {
3840 Self::Policy(policy) => {
3841 let policy = policy.as_str();
3842 quote!(#prefix::Policy(#policy.into()))
3843 }
3844 Self::DirectiveMap(list) => {
3845 let map = map_lit(
3846 quote! { ::std::collections::HashMap },
3847 list,
3848 str_lit,
3849 identity,
3850 );
3851 quote!(#prefix::DirectiveMap(#map))
3852 }
3853 })
3854 }
3855 }
3856
3857 impl ToTokens for DisabledCspModificationKind {
3858 fn to_tokens(&self, tokens: &mut TokenStream) {
3859 let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
3860
3861 tokens.append_all(match self {
3862 Self::Flag(flag) => {
3863 quote! { #prefix::Flag(#flag) }
3864 }
3865 Self::List(directives) => {
3866 let directives = vec_lit(directives, str_lit);
3867 quote! { #prefix::List(#directives) }
3868 }
3869 });
3870 }
3871 }
3872
3873 impl ToTokens for CapabilityEntry {
3874 fn to_tokens(&self, tokens: &mut TokenStream) {
3875 let prefix = quote! { ::tauri::utils::config::CapabilityEntry };
3876
3877 tokens.append_all(match self {
3878 Self::Inlined(capability) => {
3879 quote! { #prefix::Inlined(#capability) }
3880 }
3881 Self::Reference(id) => {
3882 let id = str_lit(id);
3883 quote! { #prefix::Reference(#id) }
3884 }
3885 });
3886 }
3887 }
3888
3889 impl ToTokens for HeaderSource {
3890 fn to_tokens(&self, tokens: &mut TokenStream) {
3891 let prefix = quote! { ::tauri::utils::config::HeaderSource };
3892
3893 tokens.append_all(match self {
3894 Self::Inline(s) => {
3895 let line = s.as_str();
3896 quote!(#prefix::Inline(#line.into()))
3897 }
3898 Self::List(l) => {
3899 let list = vec_lit(l, str_lit);
3900 quote!(#prefix::List(#list))
3901 }
3902 Self::Map(m) => {
3903 let map = map_lit(quote! { ::std::collections::HashMap }, m, str_lit, str_lit);
3904 quote!(#prefix::Map(#map))
3905 }
3906 })
3907 }
3908 }
3909
3910 impl ToTokens for HeaderConfig {
3911 fn to_tokens(&self, tokens: &mut TokenStream) {
3912 let access_control_allow_credentials =
3913 opt_lit(self.access_control_allow_credentials.as_ref());
3914 let access_control_allow_headers = opt_lit(self.access_control_allow_headers.as_ref());
3915 let access_control_allow_methods = opt_lit(self.access_control_allow_methods.as_ref());
3916 let access_control_expose_headers = opt_lit(self.access_control_expose_headers.as_ref());
3917 let access_control_max_age = opt_lit(self.access_control_max_age.as_ref());
3918 let cross_origin_embedder_policy = opt_lit(self.cross_origin_embedder_policy.as_ref());
3919 let cross_origin_opener_policy = opt_lit(self.cross_origin_opener_policy.as_ref());
3920 let cross_origin_resource_policy = opt_lit(self.cross_origin_resource_policy.as_ref());
3921 let permissions_policy = opt_lit(self.permissions_policy.as_ref());
3922 let service_worker_allowed = opt_lit(self.service_worker_allowed.as_ref());
3923 let timing_allow_origin = opt_lit(self.timing_allow_origin.as_ref());
3924 let x_content_type_options = opt_lit(self.x_content_type_options.as_ref());
3925 let tauri_custom_header = opt_lit(self.tauri_custom_header.as_ref());
3926
3927 literal_struct!(
3928 tokens,
3929 ::tauri::utils::config::HeaderConfig,
3930 access_control_allow_credentials,
3931 access_control_allow_headers,
3932 access_control_allow_methods,
3933 access_control_expose_headers,
3934 access_control_max_age,
3935 cross_origin_embedder_policy,
3936 cross_origin_opener_policy,
3937 cross_origin_resource_policy,
3938 permissions_policy,
3939 service_worker_allowed,
3940 timing_allow_origin,
3941 x_content_type_options,
3942 tauri_custom_header
3943 );
3944 }
3945 }
3946
3947 impl ToTokens for SecurityConfig {
3948 fn to_tokens(&self, tokens: &mut TokenStream) {
3949 let csp = opt_lit(self.csp.as_ref());
3950 let dev_csp = opt_lit(self.dev_csp.as_ref());
3951 let freeze_prototype = self.freeze_prototype;
3952 let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
3953 let asset_protocol = &self.asset_protocol;
3954 let pattern = &self.pattern;
3955 let capabilities = vec_lit(&self.capabilities, identity);
3956 let headers = opt_lit(self.headers.as_ref());
3957
3958 literal_struct!(
3959 tokens,
3960 ::tauri::utils::config::SecurityConfig,
3961 csp,
3962 dev_csp,
3963 freeze_prototype,
3964 dangerous_disable_asset_csp_modification,
3965 asset_protocol,
3966 pattern,
3967 capabilities,
3968 headers
3969 );
3970 }
3971 }
3972
3973 impl ToTokens for TrayIconConfig {
3974 fn to_tokens(&self, tokens: &mut TokenStream) {
3975 tokens.append_all(quote!(#[allow(deprecated)]));
3977
3978 let id = opt_str_lit(self.id.as_ref());
3979 let icon_as_template = self.icon_as_template;
3980 #[allow(deprecated)]
3981 let menu_on_left_click = self.menu_on_left_click;
3982 let show_menu_on_left_click = self.show_menu_on_left_click;
3983 let icon_path = path_buf_lit(&self.icon_path);
3984 let title = opt_str_lit(self.title.as_ref());
3985 let tooltip = opt_str_lit(self.tooltip.as_ref());
3986 literal_struct!(
3987 tokens,
3988 ::tauri::utils::config::TrayIconConfig,
3989 id,
3990 icon_path,
3991 icon_as_template,
3992 menu_on_left_click,
3993 show_menu_on_left_click,
3994 title,
3995 tooltip
3996 );
3997 }
3998 }
3999
4000 impl ToTokens for FsScope {
4001 fn to_tokens(&self, tokens: &mut TokenStream) {
4002 let prefix = quote! { ::tauri::utils::config::FsScope };
4003
4004 tokens.append_all(match self {
4005 Self::AllowedPaths(allow) => {
4006 let allowed_paths = vec_lit(allow, path_buf_lit);
4007 quote! { #prefix::AllowedPaths(#allowed_paths) }
4008 }
4009 Self::Scope { allow, deny , require_literal_leading_dot} => {
4010 let allow = vec_lit(allow, path_buf_lit);
4011 let deny = vec_lit(deny, path_buf_lit);
4012 let require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref());
4013 quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
4014 }
4015 });
4016 }
4017 }
4018
4019 impl ToTokens for AssetProtocolConfig {
4020 fn to_tokens(&self, tokens: &mut TokenStream) {
4021 let scope = &self.scope;
4022 tokens.append_all(quote! { ::tauri::utils::config::AssetProtocolConfig { scope: #scope, ..Default::default() } })
4023 }
4024 }
4025
4026 impl ToTokens for AppConfig {
4027 fn to_tokens(&self, tokens: &mut TokenStream) {
4028 let windows = vec_lit(&self.windows, identity);
4029 let security = &self.security;
4030 let tray_icon = opt_lit(self.tray_icon.as_ref());
4031 let macos_private_api = self.macos_private_api;
4032 let with_global_tauri = self.with_global_tauri;
4033 let enable_gtk_app_id = self.enable_gtk_app_id;
4034
4035 literal_struct!(
4036 tokens,
4037 ::tauri::utils::config::AppConfig,
4038 windows,
4039 security,
4040 tray_icon,
4041 macos_private_api,
4042 with_global_tauri,
4043 enable_gtk_app_id
4044 );
4045 }
4046 }
4047
4048 impl ToTokens for PluginConfig {
4049 fn to_tokens(&self, tokens: &mut TokenStream) {
4050 let config = map_lit(
4051 quote! { ::std::collections::HashMap },
4052 &self.0,
4053 str_lit,
4054 json_value_lit,
4055 );
4056 tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
4057 }
4058 }
4059
4060 impl ToTokens for Config {
4061 fn to_tokens(&self, tokens: &mut TokenStream) {
4062 let schema = quote!(None);
4063 let product_name = opt_str_lit(self.product_name.as_ref());
4064 let main_binary_name = opt_str_lit(self.main_binary_name.as_ref());
4065 let version = opt_str_lit(self.version.as_ref());
4066 let identifier = str_lit(&self.identifier);
4067 let app = &self.app;
4068 let build = &self.build;
4069 let bundle = &self.bundle;
4070 let plugins = &self.plugins;
4071
4072 literal_struct!(
4073 tokens,
4074 ::tauri::utils::config::Config,
4075 schema,
4076 product_name,
4077 main_binary_name,
4078 version,
4079 identifier,
4080 app,
4081 build,
4082 bundle,
4083 plugins
4084 );
4085 }
4086 }
4087}
4088
4089#[cfg(test)]
4090mod test {
4091 use super::*;
4092
4093 #[test]
4096 fn test_defaults() {
4098 let a_config = AppConfig::default();
4100 let b_config = BuildConfig::default();
4102 let d_windows: Vec<WindowConfig> = vec![];
4104 let d_bundle = BundleConfig::default();
4106
4107 let app = AppConfig {
4109 windows: vec![],
4110 security: SecurityConfig {
4111 csp: None,
4112 dev_csp: None,
4113 freeze_prototype: false,
4114 dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
4115 asset_protocol: AssetProtocolConfig::default(),
4116 pattern: Default::default(),
4117 capabilities: Vec::new(),
4118 headers: None,
4119 },
4120 tray_icon: None,
4121 macos_private_api: false,
4122 with_global_tauri: false,
4123 enable_gtk_app_id: false,
4124 };
4125
4126 let build = BuildConfig {
4128 runner: None,
4129 dev_url: None,
4130 frontend_dist: None,
4131 before_dev_command: None,
4132 before_build_command: None,
4133 before_bundle_command: None,
4134 features: None,
4135 remove_unused_commands: false,
4136 additional_watch_folders: Vec::new(),
4137 };
4138
4139 let bundle = BundleConfig {
4141 active: false,
4142 targets: Default::default(),
4143 create_updater_artifacts: Default::default(),
4144 publisher: None,
4145 homepage: None,
4146 icon: Vec::new(),
4147 resources: None,
4148 copyright: None,
4149 category: None,
4150 file_associations: None,
4151 short_description: None,
4152 long_description: None,
4153 use_local_tools_dir: false,
4154 license: None,
4155 license_file: None,
4156 linux: Default::default(),
4157 macos: Default::default(),
4158 external_bin: None,
4159 windows: Default::default(),
4160 ios: Default::default(),
4161 android: Default::default(),
4162 };
4163
4164 assert_eq!(a_config, app);
4166 assert_eq!(b_config, build);
4167 assert_eq!(d_bundle, bundle);
4168 assert_eq!(d_windows, app.windows);
4169 }
4170
4171 #[test]
4172 fn parse_hex_color() {
4173 use super::Color;
4174
4175 assert_eq!(Color(255, 255, 255, 255), "fff".parse().unwrap());
4176 assert_eq!(Color(255, 255, 255, 255), "#fff".parse().unwrap());
4177 assert_eq!(Color(0, 0, 0, 255), "#000000".parse().unwrap());
4178 assert_eq!(Color(0, 0, 0, 255), "#000000ff".parse().unwrap());
4179 assert_eq!(Color(0, 255, 0, 255), "#00ff00ff".parse().unwrap());
4180 }
4181
4182 #[test]
4183 fn test_runner_config_string_format() {
4184 use super::RunnerConfig;
4185
4186 let json = r#""cargo""#;
4188 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4189
4190 assert_eq!(runner.cmd(), "cargo");
4191 assert_eq!(runner.cwd(), None);
4192 assert_eq!(runner.args(), None);
4193
4194 let serialized = serde_json::to_string(&runner).unwrap();
4196 assert_eq!(serialized, r#""cargo""#);
4197 }
4198
4199 #[test]
4200 fn test_runner_config_object_format_full() {
4201 use super::RunnerConfig;
4202
4203 let json = r#"{"cmd": "my_runner", "cwd": "/tmp/build", "args": ["--quiet", "--verbose"]}"#;
4205 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4206
4207 assert_eq!(runner.cmd(), "my_runner");
4208 assert_eq!(runner.cwd(), Some("/tmp/build"));
4209 assert_eq!(
4210 runner.args(),
4211 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4212 );
4213
4214 let serialized = serde_json::to_string(&runner).unwrap();
4216 let deserialized: RunnerConfig = serde_json::from_str(&serialized).unwrap();
4217 assert_eq!(runner, deserialized);
4218 }
4219
4220 #[test]
4221 fn test_runner_config_object_format_minimal() {
4222 use super::RunnerConfig;
4223
4224 let json = r#"{"cmd": "cross"}"#;
4226 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4227
4228 assert_eq!(runner.cmd(), "cross");
4229 assert_eq!(runner.cwd(), None);
4230 assert_eq!(runner.args(), None);
4231 }
4232
4233 #[test]
4234 fn test_runner_config_default() {
4235 use super::RunnerConfig;
4236
4237 let default_runner = RunnerConfig::default();
4238 assert_eq!(default_runner.cmd(), "cargo");
4239 assert_eq!(default_runner.cwd(), None);
4240 assert_eq!(default_runner.args(), None);
4241 }
4242
4243 #[test]
4244 fn test_runner_config_from_str() {
4245 use super::RunnerConfig;
4246
4247 let runner: RunnerConfig = "my_runner".into();
4249 assert_eq!(runner.cmd(), "my_runner");
4250 assert_eq!(runner.cwd(), None);
4251 assert_eq!(runner.args(), None);
4252 }
4253
4254 #[test]
4255 fn test_runner_config_from_string() {
4256 use super::RunnerConfig;
4257
4258 let runner: RunnerConfig = "another_runner".to_string().into();
4260 assert_eq!(runner.cmd(), "another_runner");
4261 assert_eq!(runner.cwd(), None);
4262 assert_eq!(runner.args(), None);
4263 }
4264
4265 #[test]
4266 fn test_runner_config_from_str_parse() {
4267 use super::RunnerConfig;
4268 use std::str::FromStr;
4269
4270 let runner = RunnerConfig::from_str("parsed_runner").unwrap();
4272 assert_eq!(runner.cmd(), "parsed_runner");
4273 assert_eq!(runner.cwd(), None);
4274 assert_eq!(runner.args(), None);
4275 }
4276
4277 #[test]
4278 fn test_runner_config_in_build_config() {
4279 use super::BuildConfig;
4280
4281 let json = r#"{"runner": "cargo"}"#;
4283 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4284
4285 let runner = build_config.runner.unwrap();
4286 assert_eq!(runner.cmd(), "cargo");
4287 assert_eq!(runner.cwd(), None);
4288 assert_eq!(runner.args(), None);
4289 }
4290
4291 #[test]
4292 fn test_runner_config_in_build_config_object() {
4293 use super::BuildConfig;
4294
4295 let json = r#"{"runner": {"cmd": "cross", "cwd": "/workspace", "args": ["--target", "x86_64-unknown-linux-gnu"]}}"#;
4297 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4298
4299 let runner = build_config.runner.unwrap();
4300 assert_eq!(runner.cmd(), "cross");
4301 assert_eq!(runner.cwd(), Some("/workspace"));
4302 assert_eq!(
4303 runner.args(),
4304 Some(
4305 &[
4306 "--target".to_string(),
4307 "x86_64-unknown-linux-gnu".to_string()
4308 ][..]
4309 )
4310 );
4311 }
4312
4313 #[test]
4314 fn test_runner_config_in_full_config() {
4315 use super::Config;
4316
4317 let json = r#"{
4319 "productName": "Test App",
4320 "version": "1.0.0",
4321 "identifier": "com.test.app",
4322 "build": {
4323 "runner": {
4324 "cmd": "my_custom_cargo",
4325 "cwd": "/tmp/build",
4326 "args": ["--quiet", "--verbose"]
4327 }
4328 }
4329 }"#;
4330
4331 let config: Config = serde_json::from_str(json).unwrap();
4332 let runner = config.build.runner.unwrap();
4333
4334 assert_eq!(runner.cmd(), "my_custom_cargo");
4335 assert_eq!(runner.cwd(), Some("/tmp/build"));
4336 assert_eq!(
4337 runner.args(),
4338 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4339 );
4340 }
4341
4342 #[test]
4343 fn test_runner_config_equality() {
4344 use super::RunnerConfig;
4345
4346 let runner1 = RunnerConfig::String("cargo".to_string());
4347 let runner2 = RunnerConfig::String("cargo".to_string());
4348 let runner3 = RunnerConfig::String("cross".to_string());
4349
4350 assert_eq!(runner1, runner2);
4351 assert_ne!(runner1, runner3);
4352
4353 let runner4 = RunnerConfig::Object {
4354 cmd: "cargo".to_string(),
4355 cwd: Some("/tmp".to_string()),
4356 args: Some(vec!["--quiet".to_string()]),
4357 };
4358 let runner5 = RunnerConfig::Object {
4359 cmd: "cargo".to_string(),
4360 cwd: Some("/tmp".to_string()),
4361 args: Some(vec!["--quiet".to_string()]),
4362 };
4363
4364 assert_eq!(runner4, runner5);
4365 assert_ne!(runner1, runner4);
4366 }
4367
4368 #[test]
4369 fn test_runner_config_untagged_serialization() {
4370 use super::RunnerConfig;
4371
4372 let string_runner = RunnerConfig::String("cargo".to_string());
4374 let string_json = serde_json::to_string(&string_runner).unwrap();
4375 assert_eq!(string_json, r#""cargo""#);
4376
4377 let object_runner = RunnerConfig::Object {
4379 cmd: "cross".to_string(),
4380 cwd: None,
4381 args: None,
4382 };
4383 let object_json = serde_json::to_string(&object_runner).unwrap();
4384 assert!(object_json.contains("\"cmd\":\"cross\""));
4385 assert!(object_json.contains("\"cwd\":null") || !object_json.contains("cwd"));
4387 assert!(object_json.contains("\"args\":null") || !object_json.contains("args"));
4388 }
4389
4390 #[test]
4391 fn window_config_default_same_as_deserialize() {
4392 let config_from_deserialization: WindowConfig = serde_json::from_str("{}").unwrap();
4393 let config_from_default: WindowConfig = WindowConfig::default();
4394
4395 assert_eq!(config_from_deserialization, config_from_default);
4396 }
4397}