1use std::{collections::HashMap, path::PathBuf};
13
14use serde::Deserialize;
15use serde_json::Value as JsonValue;
16use url::Url;
17
18#[derive(PartialEq, Debug, Clone, Deserialize)]
20#[serde(untagged)]
21#[non_exhaustive]
22pub enum WindowUrl {
23 External(Url),
25 App(PathBuf),
27}
28
29impl Default for WindowUrl {
30 fn default() -> Self {
31 Self::App("index.html".into())
32 }
33}
34
35#[derive(PartialEq, Deserialize, Debug, Clone)]
37#[serde(rename_all = "camelCase")]
38pub struct WindowConfig {
39 #[serde(default = "default_window_label")]
40 pub label: String,
42 #[serde(default)]
44 pub url: WindowUrl,
45 #[serde(default = "default_file_drop_enabled")]
49 pub file_drop_enabled: bool,
50 #[serde(default)]
52 pub center: bool,
53 pub x: Option<f64>,
55 pub y: Option<f64>,
57 #[serde(default = "default_width")]
59 pub width: f64,
60 #[serde(default = "default_height")]
62 pub height: f64,
63 pub min_width: Option<f64>,
65 pub min_height: Option<f64>,
67 pub max_width: Option<f64>,
69 pub max_height: Option<f64>,
71 #[serde(default = "default_resizable")]
73 pub resizable: bool,
74 #[serde(default = "default_title")]
76 pub title: String,
77 #[serde(default)]
79 pub fullscreen: bool,
80 #[serde(default)]
82 pub focus: bool,
83 #[serde(default)]
85 pub transparent: bool,
86 #[serde(default)]
88 pub maximized: bool,
89 #[serde(default = "default_visible")]
91 pub visible: bool,
92 #[serde(default = "default_decorations")]
94 pub decorations: bool,
95 #[serde(default)]
97 pub always_on_top: bool,
98 #[serde(default)]
100 pub skip_taskbar: bool,
101}
102
103fn default_window_label() -> String {
104 "main".to_string()
105}
106
107fn default_width() -> f64 {
108 800f64
109}
110
111fn default_height() -> f64 {
112 600f64
113}
114
115fn default_resizable() -> bool {
116 true
117}
118
119fn default_visible() -> bool {
120 true
121}
122
123fn default_decorations() -> bool {
124 true
125}
126
127fn default_title() -> String {
128 "Tauri App".to_string()
129}
130
131fn default_file_drop_enabled() -> bool {
132 true
133}
134
135impl Default for WindowConfig {
136 fn default() -> Self {
137 Self {
138 label: default_window_label(),
139 url: WindowUrl::default(),
140 file_drop_enabled: default_file_drop_enabled(),
141 center: false,
142 x: None,
143 y: None,
144 width: default_width(),
145 height: default_height(),
146 min_width: None,
147 min_height: None,
148 max_width: None,
149 max_height: None,
150 resizable: default_resizable(),
151 title: default_title(),
152 fullscreen: false,
153 focus: false,
154 transparent: false,
155 maximized: false,
156 visible: default_visible(),
157 decorations: default_decorations(),
158 always_on_top: false,
159 skip_taskbar: false,
160 }
161 }
162}
163
164#[derive(PartialEq, Deserialize, Debug, Clone)]
166#[serde(rename_all = "camelCase")]
167pub struct UpdaterConfig {
168 #[serde(default)]
170 pub active: bool,
171 #[serde(default = "default_updater_dialog")]
173 pub dialog: bool,
174 #[serde(default)]
176 pub endpoints: Option<Vec<String>>,
177 #[serde(default)]
179 pub pubkey: Option<String>,
180}
181
182fn default_updater_dialog() -> bool {
183 true
184}
185
186impl Default for UpdaterConfig {
187 fn default() -> Self {
188 Self {
189 active: false,
190 dialog: true,
191 endpoints: None,
192 pubkey: None,
193 }
194 }
195}
196
197#[derive(PartialEq, Deserialize, Debug, Clone, Default)]
199#[serde(rename_all = "camelCase")]
200pub struct SecurityConfig {
201 pub csp: Option<String>,
203}
204
205#[derive(PartialEq, Deserialize, Debug, Clone, Default)]
207#[serde(rename_all = "camelCase")]
208pub struct SystemTrayConfig {
209 pub icon_path: PathBuf,
212 #[serde(default)]
214 pub icon_as_template: bool,
215}
216
217#[derive(PartialEq, Deserialize, Debug, Default, Clone)]
219#[serde(rename_all = "camelCase")]
220pub struct CliArg {
221 pub short: Option<char>,
225 pub name: String,
227 pub description: Option<String>,
230 pub long_description: Option<String>,
233 pub takes_value: Option<bool>,
240 pub multiple: Option<bool>,
247 pub multiple_occurrences: Option<bool>,
249 pub number_of_values: Option<u64>,
251 pub possible_values: Option<Vec<String>>,
254 pub min_values: Option<u64>,
258 pub max_values: Option<u64>,
262 pub required: Option<bool>,
267 pub required_unless_present: Option<String>,
270 pub required_unless_present_all: Option<Vec<String>>,
273 pub required_unless_present_any: Option<Vec<String>>,
276 pub conflicts_with: Option<String>,
279 pub conflicts_with_all: Option<Vec<String>>,
281 pub requires: Option<String>,
284 pub requires_all: Option<Vec<String>>,
287 pub requires_if: Option<Vec<String>>,
290 pub required_if_eq: Option<Vec<String>>,
293 pub require_equals: Option<bool>,
296 pub index: Option<u64>,
302}
303
304#[derive(PartialEq, Deserialize, Debug, Clone)]
306#[serde(rename_all = "camelCase")]
307#[allow(missing_docs)] pub struct CliConfig {
309 pub description: Option<String>,
310 pub long_description: Option<String>,
311 pub before_help: Option<String>,
312 pub after_help: Option<String>,
313 pub args: Option<Vec<CliArg>>,
314 pub subcommands: Option<HashMap<String, CliConfig>>,
315}
316
317impl CliConfig {
318 pub fn args(&self) -> Option<&Vec<CliArg>> {
320 self.args.as_ref()
321 }
322
323 pub fn subcommands(&self) -> Option<&HashMap<String, CliConfig>> {
325 self.subcommands.as_ref()
326 }
327
328 pub fn description(&self) -> Option<&String> {
330 self.description.as_ref()
331 }
332
333 pub fn long_description(&self) -> Option<&String> {
335 self.description.as_ref()
336 }
337
338 pub fn before_help(&self) -> Option<&String> {
342 self.before_help.as_ref()
343 }
344
345 pub fn after_help(&self) -> Option<&String> {
349 self.after_help.as_ref()
350 }
351}
352
353#[derive(PartialEq, Deserialize, Debug)]
355#[serde(rename_all = "camelCase")]
356pub struct BundleConfig {
357 pub identifier: String,
359 #[serde(default)]
361 pub icon: Vec<String>,
362}
363
364impl Default for BundleConfig {
365 fn default() -> Self {
366 Self {
367 identifier: String::from(""),
368 icon: Vec::default(),
369 }
370 }
371}
372
373fn default_window_config() -> Vec<WindowConfig> {
374 vec![Default::default()]
375}
376
377#[derive(PartialEq, Deserialize, Debug)]
379#[serde(rename_all = "camelCase")]
380pub struct TauriConfig {
381 #[serde(default = "default_window_config")]
383 pub windows: Vec<WindowConfig>,
384 #[serde(default)]
386 pub cli: Option<CliConfig>,
387 #[serde(default)]
389 pub bundle: BundleConfig,
390 #[serde(default)]
392 pub updater: UpdaterConfig,
393 #[serde(default)]
395 pub security: SecurityConfig,
396 #[serde(default)]
398 pub system_tray: Option<SystemTrayConfig>,
399}
400
401impl Default for TauriConfig {
402 fn default() -> Self {
403 Self {
404 windows: default_window_config(),
405 cli: None,
406 bundle: BundleConfig::default(),
407 updater: UpdaterConfig::default(),
408 security: SecurityConfig::default(),
409 system_tray: None,
410 }
411 }
412}
413
414#[derive(PartialEq, Debug, Clone, Deserialize)]
416#[serde(untagged)]
417#[non_exhaustive]
418pub enum AppUrl {
419 Url(WindowUrl),
421 Files(Vec<PathBuf>),
423}
424
425#[derive(PartialEq, Deserialize, Debug)]
427#[serde(rename_all = "camelCase")]
428pub struct BuildConfig {
429 #[serde(default = "default_dev_path")]
431 pub dev_path: AppUrl,
432 #[serde(default = "default_dist_path")]
434 pub dist_dir: AppUrl,
435 #[serde(default)]
437 pub with_global_tauri: bool,
438}
439
440fn default_dev_path() -> AppUrl {
441 AppUrl::Url(WindowUrl::External(
442 Url::parse("http://localhost:8080").unwrap(),
443 ))
444}
445
446fn default_dist_path() -> AppUrl {
447 AppUrl::Url(WindowUrl::App("../dist".into()))
448}
449
450impl Default for BuildConfig {
451 fn default() -> Self {
452 Self {
453 dev_path: default_dev_path(),
454 dist_dir: default_dist_path(),
455 with_global_tauri: false,
456 }
457 }
458}
459
460#[derive(Debug, Default, PartialEq, Deserialize)]
462#[serde(rename_all = "camelCase")]
463pub struct PackageConfig {
464 pub product_name: Option<String>,
466 pub version: Option<String>,
468}
469
470#[derive(Debug, Default, PartialEq, Deserialize)]
472#[serde(rename_all = "camelCase")]
473pub struct Config {
474 #[serde(default)]
476 pub package: PackageConfig,
477 #[serde(default)]
479 pub tauri: TauriConfig,
480 #[serde(default)]
482 pub build: BuildConfig,
483 #[serde(default)]
485 pub plugins: PluginConfig,
486}
487
488#[derive(Debug, Clone, Default, PartialEq, Deserialize)]
490pub struct PluginConfig(pub HashMap<String, JsonValue>);
491
492#[cfg(feature = "build")]
498mod build {
499 use std::{convert::identity, path::Path};
500
501 use proc_macro2::TokenStream;
502 use quote::{quote, ToTokens, TokenStreamExt};
503
504 use super::*;
505
506 fn str_lit(s: impl AsRef<str>) -> TokenStream {
511 let s = s.as_ref();
512 quote! { #s.into() }
513 }
514
515 fn opt_lit(item: Option<&impl ToTokens>) -> TokenStream {
517 match item {
518 None => quote! { ::core::option::Option::None },
519 Some(item) => quote! { ::core::option::Option::Some(#item) },
520 }
521 }
522
523 fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
525 opt_lit(item.map(str_lit).as_ref())
526 }
527
528 fn opt_vec_str_lit(item: Option<impl IntoIterator<Item = impl AsRef<str>>>) -> TokenStream {
530 opt_lit(item.map(|list| vec_lit(list, str_lit)).as_ref())
531 }
532
533 fn vec_lit<Raw, Tokens>(
535 list: impl IntoIterator<Item = Raw>,
536 map: impl Fn(Raw) -> Tokens,
537 ) -> TokenStream
538 where
539 Tokens: ToTokens,
540 {
541 let items = list.into_iter().map(map);
542 quote! { vec![#(#items),*] }
543 }
544
545 fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
550 let s = s.as_ref().to_string_lossy().into_owned();
551 quote! { ::std::path::PathBuf::from(#s) }
552 }
553
554 fn map_lit<Map, Key, Value, TokenStreamKey, TokenStreamValue, FuncKey, FuncValue>(
558 map_type: TokenStream,
559 map: Map,
560 map_key: FuncKey,
561 map_value: FuncValue,
562 ) -> TokenStream
563 where
564 <Map as IntoIterator>::IntoIter: ExactSizeIterator,
565 Map: IntoIterator<Item = (Key, Value)>,
566 TokenStreamKey: ToTokens,
567 TokenStreamValue: ToTokens,
568 FuncKey: Fn(Key) -> TokenStreamKey,
569 FuncValue: Fn(Value) -> TokenStreamValue,
570 {
571 let ident = quote::format_ident!("map");
572 let map = map.into_iter();
573
574 if map.len() > 0 {
575 let items = map.map(|(key, value)| {
576 let key = map_key(key);
577 let value = map_value(value);
578 quote! { #ident.insert(#key, #value); }
579 });
580
581 quote! {{
582 let mut #ident = #map_type::new();
583 #(#items)*
584 #ident
585 }}
586 } else {
587 quote! { #map_type::new() }
588 }
589 }
590
591 fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
593 let prefix = quote! { ::serde_json::Value };
595 if num.is_u64() {
596 let num = num.as_u64().unwrap();
598 quote! { #prefix::Number(#num.into()) }
599 } else if num.is_i64() {
600 let num = num.as_i64().unwrap();
602 quote! { #prefix::Number(#num.into()) }
603 } else if num.is_f64() {
604 let num = num.as_f64().unwrap();
606 quote! { #prefix::Number(#num.into()) }
607 } else {
608 quote! { #prefix::Null }
610 }
611 }
612
613 fn json_value_lit(jv: &JsonValue) -> TokenStream {
615 let prefix = quote! { ::serde_json::Value };
616
617 match jv {
618 JsonValue::Null => quote! { #prefix::Null },
619 JsonValue::Bool(bool) => quote! { #prefix::Bool(#bool) },
620 JsonValue::Number(number) => json_value_number_lit(number),
621 JsonValue::String(str) => {
622 let s = str_lit(str);
623 quote! { #prefix::String(#s) }
624 }
625 JsonValue::Array(vec) => {
626 let items = vec.iter().map(json_value_lit);
627 quote! { #prefix::Array(vec![#(#items),*]) }
628 }
629 JsonValue::Object(map) => {
630 let map = map_lit(quote! { ::serde_json::Map }, map, str_lit, json_value_lit);
631 quote! { #prefix::Object(#map) }
632 }
633 }
634 }
635
636 macro_rules! literal_struct {
640 ($tokens:ident, $struct:ident, $($field:ident),+) => {
641 $tokens.append_all(quote! {
642 ::tauri::api::config::$struct {
643 $($field: #$field),+
644 }
645 });
646 };
647 }
648
649 impl ToTokens for WindowUrl {
650 fn to_tokens(&self, tokens: &mut TokenStream) {
651 let prefix = quote! { ::tauri::api::config::WindowUrl };
652
653 tokens.append_all(match self {
654 Self::App(path) => {
655 let path = path_buf_lit(&path);
656 quote! { #prefix::App(#path) }
657 }
658 Self::External(url) => {
659 let url = url.as_str();
660 quote! { #prefix::External(#url.parse().unwrap()) }
661 }
662 })
663 }
664 }
665
666 impl ToTokens for WindowConfig {
667 fn to_tokens(&self, tokens: &mut TokenStream) {
668 let label = str_lit(&self.label);
669 let url = &self.url;
670 let file_drop_enabled = self.file_drop_enabled;
671 let center = self.center;
672 let x = opt_lit(self.x.as_ref());
673 let y = opt_lit(self.y.as_ref());
674 let width = self.width;
675 let height = self.height;
676 let min_width = opt_lit(self.min_width.as_ref());
677 let min_height = opt_lit(self.min_height.as_ref());
678 let max_width = opt_lit(self.max_width.as_ref());
679 let max_height = opt_lit(self.max_height.as_ref());
680 let resizable = self.resizable;
681 let title = str_lit(&self.title);
682 let fullscreen = self.fullscreen;
683 let focus = self.focus;
684 let transparent = self.transparent;
685 let maximized = self.maximized;
686 let visible = self.visible;
687 let decorations = self.decorations;
688 let always_on_top = self.always_on_top;
689 let skip_taskbar = self.skip_taskbar;
690
691 literal_struct!(
692 tokens,
693 WindowConfig,
694 label,
695 url,
696 file_drop_enabled,
697 center,
698 x,
699 y,
700 width,
701 height,
702 min_width,
703 min_height,
704 max_width,
705 max_height,
706 resizable,
707 title,
708 fullscreen,
709 focus,
710 transparent,
711 maximized,
712 visible,
713 decorations,
714 always_on_top,
715 skip_taskbar
716 );
717 }
718 }
719
720 impl ToTokens for CliArg {
721 fn to_tokens(&self, tokens: &mut TokenStream) {
722 let short = opt_lit(self.short.as_ref());
723 let name = str_lit(&self.name);
724 let description = opt_str_lit(self.description.as_ref());
725 let long_description = opt_str_lit(self.long_description.as_ref());
726 let takes_value = opt_lit(self.takes_value.as_ref());
727 let multiple = opt_lit(self.multiple.as_ref());
728 let multiple_occurrences = opt_lit(self.multiple_occurrences.as_ref());
729 let number_of_values = opt_lit(self.number_of_values.as_ref());
730 let possible_values = opt_vec_str_lit(self.possible_values.as_ref());
731 let min_values = opt_lit(self.min_values.as_ref());
732 let max_values = opt_lit(self.max_values.as_ref());
733 let required = opt_lit(self.required.as_ref());
734 let required_unless_present = opt_str_lit(self.required_unless_present.as_ref());
735 let required_unless_present_all = opt_vec_str_lit(self.required_unless_present_all.as_ref());
736 let required_unless_present_any = opt_vec_str_lit(self.required_unless_present_any.as_ref());
737 let conflicts_with = opt_str_lit(self.conflicts_with.as_ref());
738 let conflicts_with_all = opt_vec_str_lit(self.conflicts_with_all.as_ref());
739 let requires = opt_str_lit(self.requires.as_ref());
740 let requires_all = opt_vec_str_lit(self.requires_all.as_ref());
741 let requires_if = opt_vec_str_lit(self.requires_if.as_ref());
742 let required_if_eq = opt_vec_str_lit(self.required_if_eq.as_ref());
743 let require_equals = opt_lit(self.require_equals.as_ref());
744 let index = opt_lit(self.index.as_ref());
745
746 literal_struct!(
747 tokens,
748 CliArg,
749 short,
750 name,
751 description,
752 long_description,
753 takes_value,
754 multiple,
755 multiple_occurrences,
756 number_of_values,
757 possible_values,
758 min_values,
759 max_values,
760 required,
761 required_unless_present,
762 required_unless_present_all,
763 required_unless_present_any,
764 conflicts_with,
765 conflicts_with_all,
766 requires,
767 requires_all,
768 requires_if,
769 required_if_eq,
770 require_equals,
771 index
772 );
773 }
774 }
775
776 impl ToTokens for CliConfig {
777 fn to_tokens(&self, tokens: &mut TokenStream) {
778 let description = opt_str_lit(self.description.as_ref());
779 let long_description = opt_str_lit(self.long_description.as_ref());
780 let before_help = opt_str_lit(self.before_help.as_ref());
781 let after_help = opt_str_lit(self.after_help.as_ref());
782 let args = {
783 let args = self.args.as_ref().map(|args| {
784 let arg = args.iter().map(|a| quote! { #a });
785 quote! { vec![#(#arg),*] }
786 });
787 opt_lit(args.as_ref())
788 };
789 let subcommands = opt_lit(
790 self
791 .subcommands
792 .as_ref()
793 .map(|map| {
794 map_lit(
795 quote! { ::std::collections::HashMap },
796 map,
797 str_lit,
798 identity,
799 )
800 })
801 .as_ref(),
802 );
803
804 literal_struct!(
805 tokens,
806 CliConfig,
807 description,
808 long_description,
809 before_help,
810 after_help,
811 args,
812 subcommands
813 );
814 }
815 }
816
817 impl ToTokens for BundleConfig {
818 fn to_tokens(&self, tokens: &mut TokenStream) {
819 let identifier = str_lit(&self.identifier);
820 let icon = vec_lit(&self.icon, str_lit);
821
822 literal_struct!(tokens, BundleConfig, identifier, icon);
823 }
824 }
825
826 impl ToTokens for AppUrl {
827 fn to_tokens(&self, tokens: &mut TokenStream) {
828 let prefix = quote! { ::tauri::api::config::AppUrl };
829
830 tokens.append_all(match self {
831 Self::Url(url) => {
832 quote! { #prefix::Url(#url) }
833 }
834 Self::Files(files) => {
835 let files = vec_lit(files, path_buf_lit);
836 quote! { #prefix::Files(#files) }
837 }
838 })
839 }
840 }
841
842 impl ToTokens for BuildConfig {
843 fn to_tokens(&self, tokens: &mut TokenStream) {
844 let dev_path = &self.dev_path;
845 let dist_dir = &self.dist_dir;
846 let with_global_tauri = self.with_global_tauri;
847
848 literal_struct!(tokens, BuildConfig, dev_path, dist_dir, with_global_tauri);
849 }
850 }
851
852 impl ToTokens for UpdaterConfig {
853 fn to_tokens(&self, tokens: &mut TokenStream) {
854 let active = self.active;
855 let dialog = self.dialog;
856 let pubkey = opt_str_lit(self.pubkey.as_ref());
857 let endpoints = opt_vec_str_lit(self.endpoints.as_ref());
858
859 literal_struct!(tokens, UpdaterConfig, active, dialog, pubkey, endpoints);
860 }
861 }
862
863 impl ToTokens for SecurityConfig {
864 fn to_tokens(&self, tokens: &mut TokenStream) {
865 let csp = opt_str_lit(self.csp.as_ref());
866
867 literal_struct!(tokens, SecurityConfig, csp);
868 }
869 }
870
871 impl ToTokens for SystemTrayConfig {
872 fn to_tokens(&self, tokens: &mut TokenStream) {
873 let icon_as_template = self.icon_as_template;
874 let icon_path = path_buf_lit(&self.icon_path);
875 literal_struct!(tokens, SystemTrayConfig, icon_path, icon_as_template);
876 }
877 }
878
879 impl ToTokens for TauriConfig {
880 fn to_tokens(&self, tokens: &mut TokenStream) {
881 let windows = vec_lit(&self.windows, identity);
882 let cli = opt_lit(self.cli.as_ref());
883 let bundle = &self.bundle;
884 let updater = &self.updater;
885 let security = &self.security;
886 let system_tray = opt_lit(self.system_tray.as_ref());
887
888 literal_struct!(
889 tokens,
890 TauriConfig,
891 windows,
892 cli,
893 bundle,
894 updater,
895 security,
896 system_tray
897 );
898 }
899 }
900
901 impl ToTokens for PluginConfig {
902 fn to_tokens(&self, tokens: &mut TokenStream) {
903 let config = map_lit(
904 quote! { ::std::collections::HashMap },
905 &self.0,
906 str_lit,
907 json_value_lit,
908 );
909 tokens.append_all(quote! { ::tauri::api::config::PluginConfig(#config) })
910 }
911 }
912
913 impl ToTokens for PackageConfig {
914 fn to_tokens(&self, tokens: &mut TokenStream) {
915 let product_name = opt_str_lit(self.product_name.as_ref());
916 let version = opt_str_lit(self.version.as_ref());
917
918 literal_struct!(tokens, PackageConfig, product_name, version);
919 }
920 }
921
922 impl ToTokens for Config {
923 fn to_tokens(&self, tokens: &mut TokenStream) {
924 let package = &self.package;
925 let tauri = &self.tauri;
926 let build = &self.build;
927 let plugins = &self.plugins;
928
929 literal_struct!(tokens, Config, package, tauri, build, plugins);
930 }
931 }
932}
933
934#[cfg(test)]
935mod test {
936 use super::*;
937
938 #[test]
941 fn test_defaults() {
943 let t_config = TauriConfig::default();
945 let b_config = BuildConfig::default();
947 let d_path = default_dev_path();
949 let d_windows = default_window_config();
951 let d_title = default_title();
953 let d_bundle = BundleConfig::default();
955 let d_updater = UpdaterConfig::default();
957
958 let tauri = TauriConfig {
960 windows: vec![WindowConfig {
961 label: "main".to_string(),
962 url: WindowUrl::default(),
963 file_drop_enabled: true,
964 center: false,
965 x: None,
966 y: None,
967 width: 800f64,
968 height: 600f64,
969 min_width: None,
970 min_height: None,
971 max_width: None,
972 max_height: None,
973 resizable: true,
974 title: String::from("Tauri App"),
975 fullscreen: false,
976 focus: false,
977 transparent: false,
978 maximized: false,
979 visible: true,
980 decorations: true,
981 always_on_top: false,
982 skip_taskbar: false,
983 }],
984 bundle: BundleConfig {
985 identifier: String::from(""),
986 icon: Vec::new(),
987 },
988 cli: None,
989 updater: UpdaterConfig {
990 active: false,
991 dialog: true,
992 pubkey: None,
993 endpoints: None,
994 },
995 security: SecurityConfig { csp: None },
996 system_tray: None,
997 };
998
999 let build = BuildConfig {
1001 dev_path: AppUrl::Url(WindowUrl::External(
1002 Url::parse("http://localhost:8080").unwrap(),
1003 )),
1004 dist_dir: AppUrl::Url(WindowUrl::App("../dist".into())),
1005 with_global_tauri: false,
1006 };
1007
1008 assert_eq!(t_config, tauri);
1010 assert_eq!(b_config, build);
1011 assert_eq!(d_bundle, tauri.bundle);
1012 assert_eq!(d_updater, tauri.updater);
1013 assert_eq!(
1014 d_path,
1015 AppUrl::Url(WindowUrl::External(
1016 Url::parse("http://localhost:8080").unwrap()
1017 ))
1018 );
1019 assert_eq!(d_title, tauri.windows[0].title);
1020 assert_eq!(d_windows, tauri.windows);
1021 }
1022}