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: false,
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(|_| DeError::custom("`package > version` must be a semver string"))?
3168 .to_string(),
3169 ))
3170 } else {
3171 Err(DeError::custom(
3172 "`package > version` value is not a path to a JSON object",
3173 ))
3174 }
3175 } else {
3176 Ok(PackageVersion(
3177 Version::from_str(value)
3178 .map_err(|_| DeError::custom("`package > version` must be a semver string"))?
3179 .to_string(),
3180 ))
3181 }
3182 }
3183 }
3184
3185 deserializer.deserialize_string(PackageVersionVisitor {})
3186 }
3187}
3188
3189fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
3190where
3191 D: Deserializer<'de>,
3192{
3193 Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
3194}
3195
3196#[skip_serializing_none]
3262#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
3263#[cfg_attr(feature = "schema", derive(JsonSchema))]
3264#[serde(rename_all = "camelCase", deny_unknown_fields)]
3265pub struct Config {
3266 #[serde(rename = "$schema")]
3268 pub schema: Option<String>,
3269 #[serde(alias = "product-name")]
3271 #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
3272 pub product_name: Option<String>,
3273 #[serde(alias = "main-binary-name")]
3287 pub main_binary_name: Option<String>,
3288 #[serde(deserialize_with = "version_deserializer", default)]
3304 pub version: Option<String>,
3305 pub identifier: String,
3311 #[serde(default)]
3313 pub app: AppConfig,
3314 #[serde(default)]
3316 pub build: BuildConfig,
3317 #[serde(default)]
3319 pub bundle: BundleConfig,
3320 #[serde(default)]
3322 pub plugins: PluginConfig,
3323}
3324
3325#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
3329#[cfg_attr(feature = "schema", derive(JsonSchema))]
3330pub struct PluginConfig(pub HashMap<String, JsonValue>);
3331
3332#[cfg(feature = "build")]
3338mod build {
3339 use super::*;
3340 use crate::{literal_struct, tokens::*};
3341 use proc_macro2::TokenStream;
3342 use quote::{quote, ToTokens, TokenStreamExt};
3343 use std::convert::identity;
3344
3345 impl ToTokens for WebviewUrl {
3346 fn to_tokens(&self, tokens: &mut TokenStream) {
3347 let prefix = quote! { ::tauri::utils::config::WebviewUrl };
3348
3349 tokens.append_all(match self {
3350 Self::App(path) => {
3351 let path = path_buf_lit(path);
3352 quote! { #prefix::App(#path) }
3353 }
3354 Self::External(url) => {
3355 let url = url_lit(url);
3356 quote! { #prefix::External(#url) }
3357 }
3358 Self::CustomProtocol(url) => {
3359 let url = url_lit(url);
3360 quote! { #prefix::CustomProtocol(#url) }
3361 }
3362 })
3363 }
3364 }
3365
3366 impl ToTokens for BackgroundThrottlingPolicy {
3367 fn to_tokens(&self, tokens: &mut TokenStream) {
3368 let prefix = quote! { ::tauri::utils::config::BackgroundThrottlingPolicy };
3369 tokens.append_all(match self {
3370 Self::Disabled => quote! { #prefix::Disabled },
3371 Self::Throttle => quote! { #prefix::Throttle },
3372 Self::Suspend => quote! { #prefix::Suspend },
3373 })
3374 }
3375 }
3376
3377 impl ToTokens for crate::Theme {
3378 fn to_tokens(&self, tokens: &mut TokenStream) {
3379 let prefix = quote! { ::tauri::utils::Theme };
3380
3381 tokens.append_all(match self {
3382 Self::Light => quote! { #prefix::Light },
3383 Self::Dark => quote! { #prefix::Dark },
3384 })
3385 }
3386 }
3387
3388 impl ToTokens for Color {
3389 fn to_tokens(&self, tokens: &mut TokenStream) {
3390 let Color(r, g, b, a) = self;
3391 tokens.append_all(quote! {::tauri::utils::config::Color(#r,#g,#b,#a)});
3392 }
3393 }
3394 impl ToTokens for WindowEffectsConfig {
3395 fn to_tokens(&self, tokens: &mut TokenStream) {
3396 let effects = vec_lit(self.effects.clone(), |d| d);
3397 let state = opt_lit(self.state.as_ref());
3398 let radius = opt_lit(self.radius.as_ref());
3399 let color = opt_lit(self.color.as_ref());
3400
3401 literal_struct!(
3402 tokens,
3403 ::tauri::utils::config::WindowEffectsConfig,
3404 effects,
3405 state,
3406 radius,
3407 color
3408 )
3409 }
3410 }
3411
3412 impl ToTokens for crate::TitleBarStyle {
3413 fn to_tokens(&self, tokens: &mut TokenStream) {
3414 let prefix = quote! { ::tauri::utils::TitleBarStyle };
3415
3416 tokens.append_all(match self {
3417 Self::Visible => quote! { #prefix::Visible },
3418 Self::Transparent => quote! { #prefix::Transparent },
3419 Self::Overlay => quote! { #prefix::Overlay },
3420 })
3421 }
3422 }
3423
3424 impl ToTokens for LogicalPosition {
3425 fn to_tokens(&self, tokens: &mut TokenStream) {
3426 let LogicalPosition { x, y } = self;
3427 literal_struct!(tokens, ::tauri::utils::config::LogicalPosition, x, y)
3428 }
3429 }
3430
3431 impl ToTokens for crate::WindowEffect {
3432 fn to_tokens(&self, tokens: &mut TokenStream) {
3433 let prefix = quote! { ::tauri::utils::WindowEffect };
3434
3435 #[allow(deprecated)]
3436 tokens.append_all(match self {
3437 WindowEffect::AppearanceBased => quote! { #prefix::AppearanceBased},
3438 WindowEffect::Light => quote! { #prefix::Light},
3439 WindowEffect::Dark => quote! { #prefix::Dark},
3440 WindowEffect::MediumLight => quote! { #prefix::MediumLight},
3441 WindowEffect::UltraDark => quote! { #prefix::UltraDark},
3442 WindowEffect::Titlebar => quote! { #prefix::Titlebar},
3443 WindowEffect::Selection => quote! { #prefix::Selection},
3444 WindowEffect::Menu => quote! { #prefix::Menu},
3445 WindowEffect::Popover => quote! { #prefix::Popover},
3446 WindowEffect::Sidebar => quote! { #prefix::Sidebar},
3447 WindowEffect::HeaderView => quote! { #prefix::HeaderView},
3448 WindowEffect::Sheet => quote! { #prefix::Sheet},
3449 WindowEffect::WindowBackground => quote! { #prefix::WindowBackground},
3450 WindowEffect::HudWindow => quote! { #prefix::HudWindow},
3451 WindowEffect::FullScreenUI => quote! { #prefix::FullScreenUI},
3452 WindowEffect::Tooltip => quote! { #prefix::Tooltip},
3453 WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
3454 WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
3455 WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
3456 WindowEffect::Mica => quote! { #prefix::Mica},
3457 WindowEffect::MicaDark => quote! { #prefix::MicaDark},
3458 WindowEffect::MicaLight => quote! { #prefix::MicaLight},
3459 WindowEffect::Blur => quote! { #prefix::Blur},
3460 WindowEffect::Acrylic => quote! { #prefix::Acrylic},
3461 WindowEffect::Tabbed => quote! { #prefix::Tabbed },
3462 WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
3463 WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
3464 })
3465 }
3466 }
3467
3468 impl ToTokens for crate::WindowEffectState {
3469 fn to_tokens(&self, tokens: &mut TokenStream) {
3470 let prefix = quote! { ::tauri::utils::WindowEffectState };
3471
3472 #[allow(deprecated)]
3473 tokens.append_all(match self {
3474 WindowEffectState::Active => quote! { #prefix::Active},
3475 WindowEffectState::FollowsWindowActiveState => quote! { #prefix::FollowsWindowActiveState},
3476 WindowEffectState::Inactive => quote! { #prefix::Inactive},
3477 })
3478 }
3479 }
3480
3481 impl ToTokens for PreventOverflowMargin {
3482 fn to_tokens(&self, tokens: &mut TokenStream) {
3483 let width = self.width;
3484 let height = self.height;
3485
3486 literal_struct!(
3487 tokens,
3488 ::tauri::utils::config::PreventOverflowMargin,
3489 width,
3490 height
3491 )
3492 }
3493 }
3494
3495 impl ToTokens for PreventOverflowConfig {
3496 fn to_tokens(&self, tokens: &mut TokenStream) {
3497 let prefix = quote! { ::tauri::utils::config::PreventOverflowConfig };
3498
3499 #[allow(deprecated)]
3500 tokens.append_all(match self {
3501 Self::Enable(enable) => quote! { #prefix::Enable(#enable) },
3502 Self::Margin(margin) => quote! { #prefix::Margin(#margin) },
3503 })
3504 }
3505 }
3506
3507 impl ToTokens for ScrollBarStyle {
3508 fn to_tokens(&self, tokens: &mut TokenStream) {
3509 let prefix = quote! { ::tauri::utils::config::ScrollBarStyle };
3510
3511 tokens.append_all(match self {
3512 Self::Default => quote! { #prefix::Default },
3513 Self::FluentOverlay => quote! { #prefix::FluentOverlay },
3514 })
3515 }
3516 }
3517
3518 impl ToTokens for WindowConfig {
3519 fn to_tokens(&self, tokens: &mut TokenStream) {
3520 let label = str_lit(&self.label);
3521 let create = &self.create;
3522 let url = &self.url;
3523 let user_agent = opt_str_lit(self.user_agent.as_ref());
3524 let drag_drop_enabled = self.drag_drop_enabled;
3525 let center = self.center;
3526 let x = opt_lit(self.x.as_ref());
3527 let y = opt_lit(self.y.as_ref());
3528 let width = self.width;
3529 let height = self.height;
3530 let min_width = opt_lit(self.min_width.as_ref());
3531 let min_height = opt_lit(self.min_height.as_ref());
3532 let max_width = opt_lit(self.max_width.as_ref());
3533 let max_height = opt_lit(self.max_height.as_ref());
3534 let prevent_overflow = opt_lit(self.prevent_overflow.as_ref());
3535 let resizable = self.resizable;
3536 let maximizable = self.maximizable;
3537 let minimizable = self.minimizable;
3538 let closable = self.closable;
3539 let title = str_lit(&self.title);
3540 let proxy_url = opt_lit(self.proxy_url.as_ref().map(url_lit).as_ref());
3541 let fullscreen = self.fullscreen;
3542 let focus = self.focus;
3543 let focusable = self.focusable;
3544 let transparent = self.transparent;
3545 let maximized = self.maximized;
3546 let visible = self.visible;
3547 let decorations = self.decorations;
3548 let always_on_bottom = self.always_on_bottom;
3549 let always_on_top = self.always_on_top;
3550 let visible_on_all_workspaces = self.visible_on_all_workspaces;
3551 let content_protected = self.content_protected;
3552 let skip_taskbar = self.skip_taskbar;
3553 let window_classname = opt_str_lit(self.window_classname.as_ref());
3554 let theme = opt_lit(self.theme.as_ref());
3555 let title_bar_style = &self.title_bar_style;
3556 let traffic_light_position = opt_lit(self.traffic_light_position.as_ref());
3557 let hidden_title = self.hidden_title;
3558 let accept_first_mouse = self.accept_first_mouse;
3559 let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
3560 let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
3561 let shadow = self.shadow;
3562 let window_effects = opt_lit(self.window_effects.as_ref());
3563 let incognito = self.incognito;
3564 let parent = opt_str_lit(self.parent.as_ref());
3565 let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled;
3566 let browser_extensions_enabled = self.browser_extensions_enabled;
3567 let use_https_scheme = self.use_https_scheme;
3568 let devtools = opt_lit(self.devtools.as_ref());
3569 let background_color = opt_lit(self.background_color.as_ref());
3570 let background_throttling = opt_lit(self.background_throttling.as_ref());
3571 let javascript_disabled = self.javascript_disabled;
3572 let allow_link_preview = self.allow_link_preview;
3573 let disable_input_accessory_view = self.disable_input_accessory_view;
3574 let data_directory = opt_lit(self.data_directory.as_ref().map(path_buf_lit).as_ref());
3575 let data_store_identifier = opt_vec_lit(self.data_store_identifier, identity);
3576 let scroll_bar_style = &self.scroll_bar_style;
3577
3578 literal_struct!(
3579 tokens,
3580 ::tauri::utils::config::WindowConfig,
3581 label,
3582 url,
3583 create,
3584 user_agent,
3585 drag_drop_enabled,
3586 center,
3587 x,
3588 y,
3589 width,
3590 height,
3591 min_width,
3592 min_height,
3593 max_width,
3594 max_height,
3595 prevent_overflow,
3596 resizable,
3597 maximizable,
3598 minimizable,
3599 closable,
3600 title,
3601 proxy_url,
3602 fullscreen,
3603 focus,
3604 focusable,
3605 transparent,
3606 maximized,
3607 visible,
3608 decorations,
3609 always_on_bottom,
3610 always_on_top,
3611 visible_on_all_workspaces,
3612 content_protected,
3613 skip_taskbar,
3614 window_classname,
3615 theme,
3616 title_bar_style,
3617 traffic_light_position,
3618 hidden_title,
3619 accept_first_mouse,
3620 tabbing_identifier,
3621 additional_browser_args,
3622 shadow,
3623 window_effects,
3624 incognito,
3625 parent,
3626 zoom_hotkeys_enabled,
3627 browser_extensions_enabled,
3628 use_https_scheme,
3629 devtools,
3630 background_color,
3631 background_throttling,
3632 javascript_disabled,
3633 allow_link_preview,
3634 disable_input_accessory_view,
3635 data_directory,
3636 data_store_identifier,
3637 scroll_bar_style
3638 );
3639 }
3640 }
3641
3642 impl ToTokens for PatternKind {
3643 fn to_tokens(&self, tokens: &mut TokenStream) {
3644 let prefix = quote! { ::tauri::utils::config::PatternKind };
3645
3646 tokens.append_all(match self {
3647 Self::Brownfield => quote! { #prefix::Brownfield },
3648 #[cfg(not(feature = "isolation"))]
3649 Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
3650 #[cfg(feature = "isolation")]
3651 Self::Isolation { dir } => {
3652 let dir = path_buf_lit(dir);
3653 quote! { #prefix::Isolation { dir: #dir } }
3654 }
3655 })
3656 }
3657 }
3658
3659 impl ToTokens for WebviewInstallMode {
3660 fn to_tokens(&self, tokens: &mut TokenStream) {
3661 let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
3662
3663 tokens.append_all(match self {
3664 Self::Skip => quote! { #prefix::Skip },
3665 Self::DownloadBootstrapper { silent } => {
3666 quote! { #prefix::DownloadBootstrapper { silent: #silent } }
3667 }
3668 Self::EmbedBootstrapper { silent } => {
3669 quote! { #prefix::EmbedBootstrapper { silent: #silent } }
3670 }
3671 Self::OfflineInstaller { silent } => {
3672 quote! { #prefix::OfflineInstaller { silent: #silent } }
3673 }
3674 Self::FixedRuntime { path } => {
3675 let path = path_buf_lit(path);
3676 quote! { #prefix::FixedRuntime { path: #path } }
3677 }
3678 })
3679 }
3680 }
3681
3682 impl ToTokens for WindowsConfig {
3683 fn to_tokens(&self, tokens: &mut TokenStream) {
3684 let webview_install_mode = &self.webview_install_mode;
3685 tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
3686 webview_install_mode: #webview_install_mode,
3687 ..Default::default()
3688 }})
3689 }
3690 }
3691
3692 impl ToTokens for BundleConfig {
3693 fn to_tokens(&self, tokens: &mut TokenStream) {
3694 let publisher = quote!(None);
3695 let homepage = quote!(None);
3696 let icon = vec_lit(&self.icon, str_lit);
3697 let active = self.active;
3698 let targets = quote!(Default::default());
3699 let create_updater_artifacts = quote!(Default::default());
3700 let resources = quote!(None);
3701 let copyright = quote!(None);
3702 let category = quote!(None);
3703 let file_associations = quote!(None);
3704 let short_description = quote!(None);
3705 let long_description = quote!(None);
3706 let use_local_tools_dir = self.use_local_tools_dir;
3707 let external_bin = opt_vec_lit(self.external_bin.as_ref(), str_lit);
3708 let windows = &self.windows;
3709 let license = opt_str_lit(self.license.as_ref());
3710 let license_file = opt_lit(self.license_file.as_ref().map(path_buf_lit).as_ref());
3711 let linux = quote!(Default::default());
3712 let macos = quote!(Default::default());
3713 let ios = quote!(Default::default());
3714 let android = quote!(Default::default());
3715
3716 literal_struct!(
3717 tokens,
3718 ::tauri::utils::config::BundleConfig,
3719 active,
3720 publisher,
3721 homepage,
3722 icon,
3723 targets,
3724 create_updater_artifacts,
3725 resources,
3726 copyright,
3727 category,
3728 license,
3729 license_file,
3730 file_associations,
3731 short_description,
3732 long_description,
3733 use_local_tools_dir,
3734 external_bin,
3735 windows,
3736 linux,
3737 macos,
3738 ios,
3739 android
3740 );
3741 }
3742 }
3743
3744 impl ToTokens for FrontendDist {
3745 fn to_tokens(&self, tokens: &mut TokenStream) {
3746 let prefix = quote! { ::tauri::utils::config::FrontendDist };
3747
3748 tokens.append_all(match self {
3749 Self::Url(url) => {
3750 let url = url_lit(url);
3751 quote! { #prefix::Url(#url) }
3752 }
3753 Self::Directory(path) => {
3754 let path = path_buf_lit(path);
3755 quote! { #prefix::Directory(#path) }
3756 }
3757 Self::Files(files) => {
3758 let files = vec_lit(files, path_buf_lit);
3759 quote! { #prefix::Files(#files) }
3760 }
3761 })
3762 }
3763 }
3764
3765 impl ToTokens for RunnerConfig {
3766 fn to_tokens(&self, tokens: &mut TokenStream) {
3767 let prefix = quote! { ::tauri::utils::config::RunnerConfig };
3768
3769 tokens.append_all(match self {
3770 Self::String(cmd) => {
3771 let cmd = cmd.as_str();
3772 quote!(#prefix::String(#cmd.into()))
3773 }
3774 Self::Object { cmd, cwd, args } => {
3775 let cmd = cmd.as_str();
3776 let cwd = opt_str_lit(cwd.as_ref());
3777 let args = opt_lit(args.as_ref().map(|v| vec_lit(v, str_lit)).as_ref());
3778 quote!(#prefix::Object {
3779 cmd: #cmd.into(),
3780 cwd: #cwd,
3781 args: #args,
3782 })
3783 }
3784 })
3785 }
3786 }
3787
3788 impl ToTokens for BuildConfig {
3789 fn to_tokens(&self, tokens: &mut TokenStream) {
3790 let dev_url = opt_lit(self.dev_url.as_ref().map(url_lit).as_ref());
3791 let frontend_dist = opt_lit(self.frontend_dist.as_ref());
3792 let runner = opt_lit(self.runner.as_ref());
3793 let before_dev_command = quote!(None);
3794 let before_build_command = quote!(None);
3795 let before_bundle_command = quote!(None);
3796 let features = quote!(None);
3797 let remove_unused_commands = quote!(false);
3798 let additional_watch_folders = quote!(Vec::new());
3799
3800 literal_struct!(
3801 tokens,
3802 ::tauri::utils::config::BuildConfig,
3803 runner,
3804 dev_url,
3805 frontend_dist,
3806 before_dev_command,
3807 before_build_command,
3808 before_bundle_command,
3809 features,
3810 remove_unused_commands,
3811 additional_watch_folders
3812 );
3813 }
3814 }
3815
3816 impl ToTokens for CspDirectiveSources {
3817 fn to_tokens(&self, tokens: &mut TokenStream) {
3818 let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
3819
3820 tokens.append_all(match self {
3821 Self::Inline(sources) => {
3822 let sources = sources.as_str();
3823 quote!(#prefix::Inline(#sources.into()))
3824 }
3825 Self::List(list) => {
3826 let list = vec_lit(list, str_lit);
3827 quote!(#prefix::List(#list))
3828 }
3829 })
3830 }
3831 }
3832
3833 impl ToTokens for Csp {
3834 fn to_tokens(&self, tokens: &mut TokenStream) {
3835 let prefix = quote! { ::tauri::utils::config::Csp };
3836
3837 tokens.append_all(match self {
3838 Self::Policy(policy) => {
3839 let policy = policy.as_str();
3840 quote!(#prefix::Policy(#policy.into()))
3841 }
3842 Self::DirectiveMap(list) => {
3843 let map = map_lit(
3844 quote! { ::std::collections::HashMap },
3845 list,
3846 str_lit,
3847 identity,
3848 );
3849 quote!(#prefix::DirectiveMap(#map))
3850 }
3851 })
3852 }
3853 }
3854
3855 impl ToTokens for DisabledCspModificationKind {
3856 fn to_tokens(&self, tokens: &mut TokenStream) {
3857 let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
3858
3859 tokens.append_all(match self {
3860 Self::Flag(flag) => {
3861 quote! { #prefix::Flag(#flag) }
3862 }
3863 Self::List(directives) => {
3864 let directives = vec_lit(directives, str_lit);
3865 quote! { #prefix::List(#directives) }
3866 }
3867 });
3868 }
3869 }
3870
3871 impl ToTokens for CapabilityEntry {
3872 fn to_tokens(&self, tokens: &mut TokenStream) {
3873 let prefix = quote! { ::tauri::utils::config::CapabilityEntry };
3874
3875 tokens.append_all(match self {
3876 Self::Inlined(capability) => {
3877 quote! { #prefix::Inlined(#capability) }
3878 }
3879 Self::Reference(id) => {
3880 let id = str_lit(id);
3881 quote! { #prefix::Reference(#id) }
3882 }
3883 });
3884 }
3885 }
3886
3887 impl ToTokens for HeaderSource {
3888 fn to_tokens(&self, tokens: &mut TokenStream) {
3889 let prefix = quote! { ::tauri::utils::config::HeaderSource };
3890
3891 tokens.append_all(match self {
3892 Self::Inline(s) => {
3893 let line = s.as_str();
3894 quote!(#prefix::Inline(#line.into()))
3895 }
3896 Self::List(l) => {
3897 let list = vec_lit(l, str_lit);
3898 quote!(#prefix::List(#list))
3899 }
3900 Self::Map(m) => {
3901 let map = map_lit(quote! { ::std::collections::HashMap }, m, str_lit, str_lit);
3902 quote!(#prefix::Map(#map))
3903 }
3904 })
3905 }
3906 }
3907
3908 impl ToTokens for HeaderConfig {
3909 fn to_tokens(&self, tokens: &mut TokenStream) {
3910 let access_control_allow_credentials =
3911 opt_lit(self.access_control_allow_credentials.as_ref());
3912 let access_control_allow_headers = opt_lit(self.access_control_allow_headers.as_ref());
3913 let access_control_allow_methods = opt_lit(self.access_control_allow_methods.as_ref());
3914 let access_control_expose_headers = opt_lit(self.access_control_expose_headers.as_ref());
3915 let access_control_max_age = opt_lit(self.access_control_max_age.as_ref());
3916 let cross_origin_embedder_policy = opt_lit(self.cross_origin_embedder_policy.as_ref());
3917 let cross_origin_opener_policy = opt_lit(self.cross_origin_opener_policy.as_ref());
3918 let cross_origin_resource_policy = opt_lit(self.cross_origin_resource_policy.as_ref());
3919 let permissions_policy = opt_lit(self.permissions_policy.as_ref());
3920 let service_worker_allowed = opt_lit(self.service_worker_allowed.as_ref());
3921 let timing_allow_origin = opt_lit(self.timing_allow_origin.as_ref());
3922 let x_content_type_options = opt_lit(self.x_content_type_options.as_ref());
3923 let tauri_custom_header = opt_lit(self.tauri_custom_header.as_ref());
3924
3925 literal_struct!(
3926 tokens,
3927 ::tauri::utils::config::HeaderConfig,
3928 access_control_allow_credentials,
3929 access_control_allow_headers,
3930 access_control_allow_methods,
3931 access_control_expose_headers,
3932 access_control_max_age,
3933 cross_origin_embedder_policy,
3934 cross_origin_opener_policy,
3935 cross_origin_resource_policy,
3936 permissions_policy,
3937 service_worker_allowed,
3938 timing_allow_origin,
3939 x_content_type_options,
3940 tauri_custom_header
3941 );
3942 }
3943 }
3944
3945 impl ToTokens for SecurityConfig {
3946 fn to_tokens(&self, tokens: &mut TokenStream) {
3947 let csp = opt_lit(self.csp.as_ref());
3948 let dev_csp = opt_lit(self.dev_csp.as_ref());
3949 let freeze_prototype = self.freeze_prototype;
3950 let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
3951 let asset_protocol = &self.asset_protocol;
3952 let pattern = &self.pattern;
3953 let capabilities = vec_lit(&self.capabilities, identity);
3954 let headers = opt_lit(self.headers.as_ref());
3955
3956 literal_struct!(
3957 tokens,
3958 ::tauri::utils::config::SecurityConfig,
3959 csp,
3960 dev_csp,
3961 freeze_prototype,
3962 dangerous_disable_asset_csp_modification,
3963 asset_protocol,
3964 pattern,
3965 capabilities,
3966 headers
3967 );
3968 }
3969 }
3970
3971 impl ToTokens for TrayIconConfig {
3972 fn to_tokens(&self, tokens: &mut TokenStream) {
3973 tokens.append_all(quote!(#[allow(deprecated)]));
3975
3976 let id = opt_str_lit(self.id.as_ref());
3977 let icon_as_template = self.icon_as_template;
3978 #[allow(deprecated)]
3979 let menu_on_left_click = self.menu_on_left_click;
3980 let show_menu_on_left_click = self.show_menu_on_left_click;
3981 let icon_path = path_buf_lit(&self.icon_path);
3982 let title = opt_str_lit(self.title.as_ref());
3983 let tooltip = opt_str_lit(self.tooltip.as_ref());
3984 literal_struct!(
3985 tokens,
3986 ::tauri::utils::config::TrayIconConfig,
3987 id,
3988 icon_path,
3989 icon_as_template,
3990 menu_on_left_click,
3991 show_menu_on_left_click,
3992 title,
3993 tooltip
3994 );
3995 }
3996 }
3997
3998 impl ToTokens for FsScope {
3999 fn to_tokens(&self, tokens: &mut TokenStream) {
4000 let prefix = quote! { ::tauri::utils::config::FsScope };
4001
4002 tokens.append_all(match self {
4003 Self::AllowedPaths(allow) => {
4004 let allowed_paths = vec_lit(allow, path_buf_lit);
4005 quote! { #prefix::AllowedPaths(#allowed_paths) }
4006 }
4007 Self::Scope { allow, deny , require_literal_leading_dot} => {
4008 let allow = vec_lit(allow, path_buf_lit);
4009 let deny = vec_lit(deny, path_buf_lit);
4010 let require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref());
4011 quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
4012 }
4013 });
4014 }
4015 }
4016
4017 impl ToTokens for AssetProtocolConfig {
4018 fn to_tokens(&self, tokens: &mut TokenStream) {
4019 let scope = &self.scope;
4020 tokens.append_all(quote! { ::tauri::utils::config::AssetProtocolConfig { scope: #scope, ..Default::default() } })
4021 }
4022 }
4023
4024 impl ToTokens for AppConfig {
4025 fn to_tokens(&self, tokens: &mut TokenStream) {
4026 let windows = vec_lit(&self.windows, identity);
4027 let security = &self.security;
4028 let tray_icon = opt_lit(self.tray_icon.as_ref());
4029 let macos_private_api = self.macos_private_api;
4030 let with_global_tauri = self.with_global_tauri;
4031 let enable_gtk_app_id = self.enable_gtk_app_id;
4032
4033 literal_struct!(
4034 tokens,
4035 ::tauri::utils::config::AppConfig,
4036 windows,
4037 security,
4038 tray_icon,
4039 macos_private_api,
4040 with_global_tauri,
4041 enable_gtk_app_id
4042 );
4043 }
4044 }
4045
4046 impl ToTokens for PluginConfig {
4047 fn to_tokens(&self, tokens: &mut TokenStream) {
4048 let config = map_lit(
4049 quote! { ::std::collections::HashMap },
4050 &self.0,
4051 str_lit,
4052 json_value_lit,
4053 );
4054 tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
4055 }
4056 }
4057
4058 impl ToTokens for Config {
4059 fn to_tokens(&self, tokens: &mut TokenStream) {
4060 let schema = quote!(None);
4061 let product_name = opt_str_lit(self.product_name.as_ref());
4062 let main_binary_name = opt_str_lit(self.main_binary_name.as_ref());
4063 let version = opt_str_lit(self.version.as_ref());
4064 let identifier = str_lit(&self.identifier);
4065 let app = &self.app;
4066 let build = &self.build;
4067 let bundle = &self.bundle;
4068 let plugins = &self.plugins;
4069
4070 literal_struct!(
4071 tokens,
4072 ::tauri::utils::config::Config,
4073 schema,
4074 product_name,
4075 main_binary_name,
4076 version,
4077 identifier,
4078 app,
4079 build,
4080 bundle,
4081 plugins
4082 );
4083 }
4084 }
4085}
4086
4087#[cfg(test)]
4088mod test {
4089 use super::*;
4090
4091 #[test]
4094 fn test_defaults() {
4096 let a_config = AppConfig::default();
4098 let b_config = BuildConfig::default();
4100 let d_windows: Vec<WindowConfig> = vec![];
4102 let d_bundle = BundleConfig::default();
4104
4105 let app = AppConfig {
4107 windows: vec![],
4108 security: SecurityConfig {
4109 csp: None,
4110 dev_csp: None,
4111 freeze_prototype: false,
4112 dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
4113 asset_protocol: AssetProtocolConfig::default(),
4114 pattern: Default::default(),
4115 capabilities: Vec::new(),
4116 headers: None,
4117 },
4118 tray_icon: None,
4119 macos_private_api: false,
4120 with_global_tauri: false,
4121 enable_gtk_app_id: false,
4122 };
4123
4124 let build = BuildConfig {
4126 runner: None,
4127 dev_url: None,
4128 frontend_dist: None,
4129 before_dev_command: None,
4130 before_build_command: None,
4131 before_bundle_command: None,
4132 features: None,
4133 remove_unused_commands: false,
4134 additional_watch_folders: Vec::new(),
4135 };
4136
4137 let bundle = BundleConfig {
4139 active: false,
4140 targets: Default::default(),
4141 create_updater_artifacts: Default::default(),
4142 publisher: None,
4143 homepage: None,
4144 icon: Vec::new(),
4145 resources: None,
4146 copyright: None,
4147 category: None,
4148 file_associations: None,
4149 short_description: None,
4150 long_description: None,
4151 use_local_tools_dir: false,
4152 license: None,
4153 license_file: None,
4154 linux: Default::default(),
4155 macos: Default::default(),
4156 external_bin: None,
4157 windows: Default::default(),
4158 ios: Default::default(),
4159 android: Default::default(),
4160 };
4161
4162 assert_eq!(a_config, app);
4164 assert_eq!(b_config, build);
4165 assert_eq!(d_bundle, bundle);
4166 assert_eq!(d_windows, app.windows);
4167 }
4168
4169 #[test]
4170 fn parse_hex_color() {
4171 use super::Color;
4172
4173 assert_eq!(Color(255, 255, 255, 255), "fff".parse().unwrap());
4174 assert_eq!(Color(255, 255, 255, 255), "#fff".parse().unwrap());
4175 assert_eq!(Color(0, 0, 0, 255), "#000000".parse().unwrap());
4176 assert_eq!(Color(0, 0, 0, 255), "#000000ff".parse().unwrap());
4177 assert_eq!(Color(0, 255, 0, 255), "#00ff00ff".parse().unwrap());
4178 }
4179
4180 #[test]
4181 fn test_runner_config_string_format() {
4182 use super::RunnerConfig;
4183
4184 let json = r#""cargo""#;
4186 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4187
4188 assert_eq!(runner.cmd(), "cargo");
4189 assert_eq!(runner.cwd(), None);
4190 assert_eq!(runner.args(), None);
4191
4192 let serialized = serde_json::to_string(&runner).unwrap();
4194 assert_eq!(serialized, r#""cargo""#);
4195 }
4196
4197 #[test]
4198 fn test_runner_config_object_format_full() {
4199 use super::RunnerConfig;
4200
4201 let json = r#"{"cmd": "my_runner", "cwd": "/tmp/build", "args": ["--quiet", "--verbose"]}"#;
4203 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4204
4205 assert_eq!(runner.cmd(), "my_runner");
4206 assert_eq!(runner.cwd(), Some("/tmp/build"));
4207 assert_eq!(
4208 runner.args(),
4209 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4210 );
4211
4212 let serialized = serde_json::to_string(&runner).unwrap();
4214 let deserialized: RunnerConfig = serde_json::from_str(&serialized).unwrap();
4215 assert_eq!(runner, deserialized);
4216 }
4217
4218 #[test]
4219 fn test_runner_config_object_format_minimal() {
4220 use super::RunnerConfig;
4221
4222 let json = r#"{"cmd": "cross"}"#;
4224 let runner: RunnerConfig = serde_json::from_str(json).unwrap();
4225
4226 assert_eq!(runner.cmd(), "cross");
4227 assert_eq!(runner.cwd(), None);
4228 assert_eq!(runner.args(), None);
4229 }
4230
4231 #[test]
4232 fn test_runner_config_default() {
4233 use super::RunnerConfig;
4234
4235 let default_runner = RunnerConfig::default();
4236 assert_eq!(default_runner.cmd(), "cargo");
4237 assert_eq!(default_runner.cwd(), None);
4238 assert_eq!(default_runner.args(), None);
4239 }
4240
4241 #[test]
4242 fn test_runner_config_from_str() {
4243 use super::RunnerConfig;
4244
4245 let runner: RunnerConfig = "my_runner".into();
4247 assert_eq!(runner.cmd(), "my_runner");
4248 assert_eq!(runner.cwd(), None);
4249 assert_eq!(runner.args(), None);
4250 }
4251
4252 #[test]
4253 fn test_runner_config_from_string() {
4254 use super::RunnerConfig;
4255
4256 let runner: RunnerConfig = "another_runner".to_string().into();
4258 assert_eq!(runner.cmd(), "another_runner");
4259 assert_eq!(runner.cwd(), None);
4260 assert_eq!(runner.args(), None);
4261 }
4262
4263 #[test]
4264 fn test_runner_config_from_str_parse() {
4265 use super::RunnerConfig;
4266 use std::str::FromStr;
4267
4268 let runner = RunnerConfig::from_str("parsed_runner").unwrap();
4270 assert_eq!(runner.cmd(), "parsed_runner");
4271 assert_eq!(runner.cwd(), None);
4272 assert_eq!(runner.args(), None);
4273 }
4274
4275 #[test]
4276 fn test_runner_config_in_build_config() {
4277 use super::BuildConfig;
4278
4279 let json = r#"{"runner": "cargo"}"#;
4281 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4282
4283 let runner = build_config.runner.unwrap();
4284 assert_eq!(runner.cmd(), "cargo");
4285 assert_eq!(runner.cwd(), None);
4286 assert_eq!(runner.args(), None);
4287 }
4288
4289 #[test]
4290 fn test_runner_config_in_build_config_object() {
4291 use super::BuildConfig;
4292
4293 let json = r#"{"runner": {"cmd": "cross", "cwd": "/workspace", "args": ["--target", "x86_64-unknown-linux-gnu"]}}"#;
4295 let build_config: BuildConfig = serde_json::from_str(json).unwrap();
4296
4297 let runner = build_config.runner.unwrap();
4298 assert_eq!(runner.cmd(), "cross");
4299 assert_eq!(runner.cwd(), Some("/workspace"));
4300 assert_eq!(
4301 runner.args(),
4302 Some(
4303 &[
4304 "--target".to_string(),
4305 "x86_64-unknown-linux-gnu".to_string()
4306 ][..]
4307 )
4308 );
4309 }
4310
4311 #[test]
4312 fn test_runner_config_in_full_config() {
4313 use super::Config;
4314
4315 let json = r#"{
4317 "productName": "Test App",
4318 "version": "1.0.0",
4319 "identifier": "com.test.app",
4320 "build": {
4321 "runner": {
4322 "cmd": "my_custom_cargo",
4323 "cwd": "/tmp/build",
4324 "args": ["--quiet", "--verbose"]
4325 }
4326 }
4327 }"#;
4328
4329 let config: Config = serde_json::from_str(json).unwrap();
4330 let runner = config.build.runner.unwrap();
4331
4332 assert_eq!(runner.cmd(), "my_custom_cargo");
4333 assert_eq!(runner.cwd(), Some("/tmp/build"));
4334 assert_eq!(
4335 runner.args(),
4336 Some(&["--quiet".to_string(), "--verbose".to_string()][..])
4337 );
4338 }
4339
4340 #[test]
4341 fn test_runner_config_equality() {
4342 use super::RunnerConfig;
4343
4344 let runner1 = RunnerConfig::String("cargo".to_string());
4345 let runner2 = RunnerConfig::String("cargo".to_string());
4346 let runner3 = RunnerConfig::String("cross".to_string());
4347
4348 assert_eq!(runner1, runner2);
4349 assert_ne!(runner1, runner3);
4350
4351 let runner4 = RunnerConfig::Object {
4352 cmd: "cargo".to_string(),
4353 cwd: Some("/tmp".to_string()),
4354 args: Some(vec!["--quiet".to_string()]),
4355 };
4356 let runner5 = RunnerConfig::Object {
4357 cmd: "cargo".to_string(),
4358 cwd: Some("/tmp".to_string()),
4359 args: Some(vec!["--quiet".to_string()]),
4360 };
4361
4362 assert_eq!(runner4, runner5);
4363 assert_ne!(runner1, runner4);
4364 }
4365
4366 #[test]
4367 fn test_runner_config_untagged_serialization() {
4368 use super::RunnerConfig;
4369
4370 let string_runner = RunnerConfig::String("cargo".to_string());
4372 let string_json = serde_json::to_string(&string_runner).unwrap();
4373 assert_eq!(string_json, r#""cargo""#);
4374
4375 let object_runner = RunnerConfig::Object {
4377 cmd: "cross".to_string(),
4378 cwd: None,
4379 args: None,
4380 };
4381 let object_json = serde_json::to_string(&object_runner).unwrap();
4382 assert!(object_json.contains("\"cmd\":\"cross\""));
4383 assert!(object_json.contains("\"cwd\":null") || !object_json.contains("cwd"));
4385 assert!(object_json.contains("\"args\":null") || !object_json.contains("args"));
4386 }
4387}