tauri_utils/
config.rs

1// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! The Tauri configuration used at runtime.
6//! It is pulled from a `tauri.conf.json` file and the [`config::Config`] struct is generated at compile time.
7//!
8//! # Stability
9//! This is a core functionality that is not considered part of the stable API.
10//! If you use it, note that it may include breaking changes in the future.
11
12use std::{collections::HashMap, path::PathBuf};
13
14use serde::Deserialize;
15use serde_json::Value as JsonValue;
16use url::Url;
17
18/// The window webview URL options.
19#[derive(PartialEq, Debug, Clone, Deserialize)]
20#[serde(untagged)]
21#[non_exhaustive]
22pub enum WindowUrl {
23  /// An external URL.
24  External(Url),
25  /// An app URL.
26  App(PathBuf),
27}
28
29impl Default for WindowUrl {
30  fn default() -> Self {
31    Self::App("index.html".into())
32  }
33}
34
35/// The window configuration object.
36#[derive(PartialEq, Deserialize, Debug, Clone)]
37#[serde(rename_all = "camelCase")]
38pub struct WindowConfig {
39  #[serde(default = "default_window_label")]
40  /// The window identifier.
41  pub label: String,
42  /// The window webview URL.
43  #[serde(default)]
44  pub url: WindowUrl,
45  /// Whether the file drop is enabled or not on the webview. By default it is enabled.
46  ///
47  /// Disabling it is required to use drag and drop on the frontend on Windows.
48  #[serde(default = "default_file_drop_enabled")]
49  pub file_drop_enabled: bool,
50  /// Center the window.
51  #[serde(default)]
52  pub center: bool,
53  /// The horizontal position of the window's top left corner
54  pub x: Option<f64>,
55  /// The vertical position of the window's top left corner
56  pub y: Option<f64>,
57  /// The window width.
58  #[serde(default = "default_width")]
59  pub width: f64,
60  /// The window height.
61  #[serde(default = "default_height")]
62  pub height: f64,
63  /// The min window width.
64  pub min_width: Option<f64>,
65  /// The min window height.
66  pub min_height: Option<f64>,
67  /// The max window width.
68  pub max_width: Option<f64>,
69  /// The max window height.
70  pub max_height: Option<f64>,
71  /// Whether the window is resizable or not.
72  #[serde(default = "default_resizable")]
73  pub resizable: bool,
74  /// The window title.
75  #[serde(default = "default_title")]
76  pub title: String,
77  /// Whether the window starts as fullscreen or not.
78  #[serde(default)]
79  pub fullscreen: bool,
80  /// Whether the window will be initially hidden or focused.
81  #[serde(default)]
82  pub focus: bool,
83  /// Whether the window is transparent or not.
84  #[serde(default)]
85  pub transparent: bool,
86  /// Whether the window is maximized or not.
87  #[serde(default)]
88  pub maximized: bool,
89  /// Whether the window is visible or not.
90  #[serde(default = "default_visible")]
91  pub visible: bool,
92  /// Whether the window should have borders and bars.
93  #[serde(default = "default_decorations")]
94  pub decorations: bool,
95  /// Whether the window should always be on top of other windows.
96  #[serde(default)]
97  pub always_on_top: bool,
98  /// Whether or not the window icon should be added to the taskbar.
99  #[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/// The Updater configuration object.
165#[derive(PartialEq, Deserialize, Debug, Clone)]
166#[serde(rename_all = "camelCase")]
167pub struct UpdaterConfig {
168  /// Whether the updater is active or not.
169  #[serde(default)]
170  pub active: bool,
171  /// Display built-in dialog or use event system if disabled.
172  #[serde(default = "default_updater_dialog")]
173  pub dialog: bool,
174  /// The updater endpoints.
175  #[serde(default)]
176  pub endpoints: Option<Vec<String>>,
177  /// Optional pubkey.
178  #[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/// Security configuration.
198#[derive(PartialEq, Deserialize, Debug, Clone, Default)]
199#[serde(rename_all = "camelCase")]
200pub struct SecurityConfig {
201  /// Content security policy to inject to HTML files with the custom protocol.
202  pub csp: Option<String>,
203}
204
205/// Configuration for application system tray icon.
206#[derive(PartialEq, Deserialize, Debug, Clone, Default)]
207#[serde(rename_all = "camelCase")]
208pub struct SystemTrayConfig {
209  /// Path to the icon to use on the system tray.
210  /// Automatically set to be an `.png` on macOS and Linux, and `.ico` on Windows.
211  pub icon_path: PathBuf,
212  /// A Boolean value that determines whether the image represents a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc) image on macOS.
213  #[serde(default)]
214  pub icon_as_template: bool,
215}
216
217/// A CLI argument definition
218#[derive(PartialEq, Deserialize, Debug, Default, Clone)]
219#[serde(rename_all = "camelCase")]
220pub struct CliArg {
221  /// The short version of the argument, without the preceding -.
222  ///
223  /// NOTE: Any leading - characters will be stripped, and only the first non - character will be used as the short version.
224  pub short: Option<char>,
225  /// The unique argument name
226  pub name: String,
227  /// The argument description which will be shown on the help information.
228  /// Typically, this is a short (one line) description of the arg.
229  pub description: Option<String>,
230  /// The argument long description which will be shown on the help information.
231  /// Typically this a more detailed (multi-line) message that describes the argument.
232  pub long_description: Option<String>,
233  /// Specifies that the argument takes a value at run time.
234  ///
235  /// NOTE: values for arguments may be specified in any of the following methods
236  /// - Using a space such as -o value or --option value
237  /// - Using an equals and no space such as -o=value or --option=value
238  /// - Use a short and no space such as -ovalue
239  pub takes_value: Option<bool>,
240  /// Specifies that the argument may appear more than once.
241  ///
242  /// - For flags, this results in the number of occurrences of the flag being recorded.
243  /// For example -ddd or -d -d -d would count as three occurrences.
244  /// - For options there is a distinct difference in multiple occurrences vs multiple values.
245  /// For example, --opt val1 val2 is one occurrence, but two values. Whereas --opt val1 --opt val2 is two occurrences.
246  pub multiple: Option<bool>,
247  ///
248  pub multiple_occurrences: Option<bool>,
249  ///
250  pub number_of_values: Option<u64>,
251  /// Specifies a list of possible values for this argument.
252  /// At runtime, the CLI verifies that only one of the specified values was used, or fails with an error message.
253  pub possible_values: Option<Vec<String>>,
254  /// Specifies the minimum number of values for this argument.
255  /// For example, if you had a -f <file> argument where you wanted at least 2 'files',
256  /// you would set `minValues: 2`, and this argument would be satisfied if the user provided, 2 or more values.
257  pub min_values: Option<u64>,
258  /// Specifies the maximum number of values are for this argument.
259  /// For example, if you had a -f <file> argument where you wanted up to 3 'files',
260  /// you would set .max_values(3), and this argument would be satisfied if the user provided, 1, 2, or 3 values.
261  pub max_values: Option<u64>,
262  /// Sets whether or not the argument is required by default.
263  ///
264  /// - Required by default means it is required, when no other conflicting rules have been evaluated
265  /// - Conflicting rules take precedence over being required.
266  pub required: Option<bool>,
267  /// Sets an arg that override this arg's required setting
268  /// i.e. this arg will be required unless this other argument is present.
269  pub required_unless_present: Option<String>,
270  /// Sets args that override this arg's required setting
271  /// i.e. this arg will be required unless all these other arguments are present.
272  pub required_unless_present_all: Option<Vec<String>>,
273  /// Sets args that override this arg's required setting
274  /// i.e. this arg will be required unless at least one of these other arguments are present.
275  pub required_unless_present_any: Option<Vec<String>>,
276  /// Sets a conflicting argument by name
277  /// i.e. when using this argument, the following argument can't be present and vice versa.
278  pub conflicts_with: Option<String>,
279  /// The same as conflictsWith but allows specifying multiple two-way conflicts per argument.
280  pub conflicts_with_all: Option<Vec<String>>,
281  /// Tets an argument by name that is required when this one is present
282  /// i.e. when using this argument, the following argument must be present.
283  pub requires: Option<String>,
284  /// Sts multiple arguments by names that are required when this one is present
285  /// i.e. when using this argument, the following arguments must be present.
286  pub requires_all: Option<Vec<String>>,
287  /// Allows a conditional requirement with the signature [arg, value]
288  /// the requirement will only become valid if `arg`'s value equals `${value}`.
289  pub requires_if: Option<Vec<String>>,
290  /// Allows specifying that an argument is required conditionally with the signature [arg, value]
291  /// the requirement will only become valid if the `arg`'s value equals `${value}`.
292  pub required_if_eq: Option<Vec<String>>,
293  /// Requires that options use the --option=val syntax
294  /// i.e. an equals between the option and associated value.
295  pub require_equals: Option<bool>,
296  /// The positional argument index, starting at 1.
297  ///
298  /// The index refers to position according to other positional argument.
299  /// It does not define position in the argument list as a whole. When utilized with multiple=true,
300  /// only the last positional argument may be defined as multiple (i.e. the one with the highest index).
301  pub index: Option<u64>,
302}
303
304/// The CLI root command definition.
305#[derive(PartialEq, Deserialize, Debug, Clone)]
306#[serde(rename_all = "camelCase")]
307#[allow(missing_docs)] // TODO
308pub 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  /// List of args for the command
319  pub fn args(&self) -> Option<&Vec<CliArg>> {
320    self.args.as_ref()
321  }
322
323  /// List of subcommands of this command
324  pub fn subcommands(&self) -> Option<&HashMap<String, CliConfig>> {
325    self.subcommands.as_ref()
326  }
327
328  /// Command description which will be shown on the help information.
329  pub fn description(&self) -> Option<&String> {
330    self.description.as_ref()
331  }
332
333  /// Command long description which will be shown on the help information.
334  pub fn long_description(&self) -> Option<&String> {
335    self.description.as_ref()
336  }
337
338  /// Adds additional help information to be displayed in addition to auto-generated help.
339  /// This information is displayed before the auto-generated help information.
340  /// This is often used for header information.
341  pub fn before_help(&self) -> Option<&String> {
342    self.before_help.as_ref()
343  }
344
345  /// Adds additional help information to be displayed in addition to auto-generated help.
346  /// This information is displayed after the auto-generated help information.
347  /// This is often used to describe how to use the arguments, or caveats to be noted.
348  pub fn after_help(&self) -> Option<&String> {
349    self.after_help.as_ref()
350  }
351}
352
353/// The bundler configuration object.
354#[derive(PartialEq, Deserialize, Debug)]
355#[serde(rename_all = "camelCase")]
356pub struct BundleConfig {
357  /// The bundle identifier.
358  pub identifier: String,
359  /// The bundle icons.
360  #[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/// The Tauri configuration object.
378#[derive(PartialEq, Deserialize, Debug)]
379#[serde(rename_all = "camelCase")]
380pub struct TauriConfig {
381  /// The window configuration.
382  #[serde(default = "default_window_config")]
383  pub windows: Vec<WindowConfig>,
384  /// The CLI configuration.
385  #[serde(default)]
386  pub cli: Option<CliConfig>,
387  /// The bundler configuration.
388  #[serde(default)]
389  pub bundle: BundleConfig,
390  /// The updater configuration.
391  #[serde(default)]
392  pub updater: UpdaterConfig,
393  /// The security configuration.
394  #[serde(default)]
395  pub security: SecurityConfig,
396  /// System tray configuration.
397  #[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/// The `dev_path` and `dist_dir` options.
415#[derive(PartialEq, Debug, Clone, Deserialize)]
416#[serde(untagged)]
417#[non_exhaustive]
418pub enum AppUrl {
419  /// A url or file path.
420  Url(WindowUrl),
421  /// An array of files.
422  Files(Vec<PathBuf>),
423}
424
425/// The Build configuration object.
426#[derive(PartialEq, Deserialize, Debug)]
427#[serde(rename_all = "camelCase")]
428pub struct BuildConfig {
429  /// the devPath config.
430  #[serde(default = "default_dev_path")]
431  pub dev_path: AppUrl,
432  /// the dist config.
433  #[serde(default = "default_dist_path")]
434  pub dist_dir: AppUrl,
435  /// Whether we should inject the Tauri API on `window.__TAURI__` or not.
436  #[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/// The package configuration.
461#[derive(Debug, Default, PartialEq, Deserialize)]
462#[serde(rename_all = "camelCase")]
463pub struct PackageConfig {
464  /// App name.
465  pub product_name: Option<String>,
466  /// App version.
467  pub version: Option<String>,
468}
469
470/// The tauri.conf.json mapper.
471#[derive(Debug, Default, PartialEq, Deserialize)]
472#[serde(rename_all = "camelCase")]
473pub struct Config {
474  /// Package settings.
475  #[serde(default)]
476  pub package: PackageConfig,
477  /// The Tauri configuration.
478  #[serde(default)]
479  pub tauri: TauriConfig,
480  /// The build configuration.
481  #[serde(default)]
482  pub build: BuildConfig,
483  /// The plugins config.
484  #[serde(default)]
485  pub plugins: PluginConfig,
486}
487
488/// The plugin configs holds a HashMap mapping a plugin name to its configuration object.
489#[derive(Debug, Clone, Default, PartialEq, Deserialize)]
490pub struct PluginConfig(pub HashMap<String, JsonValue>);
491
492/// Implement `ToTokens` for all config structs, allowing a literal `Config` to be built.
493///
494/// This allows for a build script to output the values in a `Config` to a `TokenStream`, which can
495/// then be consumed by another crate. Useful for passing a config to both the build script and the
496/// application using tauri while only parsing it once (in the build script).
497#[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  /// Create a `String` constructor `TokenStream`.
507  ///
508  /// e.g. `"Hello World" -> String::from("Hello World").
509  /// This takes a `&String` to reduce casting all the `&String` -> `&str` manually.
510  fn str_lit(s: impl AsRef<str>) -> TokenStream {
511    let s = s.as_ref();
512    quote! { #s.into() }
513  }
514
515  /// Create an `Option` constructor `TokenStream`.
516  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  /// Helper function to combine an `opt_lit` with `str_lit`.
524  fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
525    opt_lit(item.map(str_lit).as_ref())
526  }
527
528  /// Helper function to combine an `opt_lit` with a list of `str_lit`
529  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  /// Create a `Vec` constructor, mapping items with a function that spits out `TokenStream`s.
534  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  /// Create a `PathBuf` constructor `TokenStream`.
546  ///
547  /// e.g. `"Hello World" -> String::from("Hello World").
548  /// This takes a `&String` to reduce casting all the `&String` -> `&str` manually.
549  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  /// Create a map constructor, mapping keys and values with other `TokenStream`s.
555  ///
556  /// This function is pretty generic because the types of keys AND values get transformed.
557  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  /// Create a `serde_json::Value` variant `TokenStream` for a number
592  fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
593    // See https://docs.rs/serde_json/1/serde_json/struct.Number.html for guarantees
594    let prefix = quote! { ::serde_json::Value };
595    if num.is_u64() {
596      // guaranteed u64
597      let num = num.as_u64().unwrap();
598      quote! { #prefix::Number(#num.into()) }
599    } else if num.is_i64() {
600      // guaranteed i64
601      let num = num.as_i64().unwrap();
602      quote! { #prefix::Number(#num.into()) }
603    } else if num.is_f64() {
604      // guaranteed f64
605      let num = num.as_f64().unwrap();
606      quote! { #prefix::Number(#num.into()) }
607    } else {
608      // invalid number
609      quote! { #prefix::Null }
610    }
611  }
612
613  /// Create a `serde_json::Value` constructor `TokenStream`
614  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  /// Write a `TokenStream` of the `$struct`'s fields to the `$tokens`.
637  ///
638  /// All fields must represent a binding of the same name that implements `ToTokens`.
639  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  // TODO: create a test that compares a config to a json config
939
940  #[test]
941  // test all of the default functions
942  fn test_defaults() {
943    // get default tauri config
944    let t_config = TauriConfig::default();
945    // get default build config
946    let b_config = BuildConfig::default();
947    // get default dev path
948    let d_path = default_dev_path();
949    // get default window
950    let d_windows = default_window_config();
951    // get default title
952    let d_title = default_title();
953    // get default bundle
954    let d_bundle = BundleConfig::default();
955    // get default updater
956    let d_updater = UpdaterConfig::default();
957
958    // create a tauri config.
959    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    // create a build config
1000    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    // test the configs
1009    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}