#[cfg(target_os = "linux")]
use heck::ToKebabCase;
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use semver::Version;
use serde::{
de::{Deserializer, Error as DeError, Visitor},
Deserialize, Serialize, Serializer,
};
use serde_json::Value as JsonValue;
use serde_with::skip_serializing_none;
use url::Url;
use std::{
collections::HashMap,
fmt::{self, Display},
fs::read_to_string,
path::PathBuf,
str::FromStr,
};
pub mod parse;
use crate::TitleBarStyle;
pub use self::parse::parse;
#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
#[non_exhaustive]
pub enum WindowUrl {
External(Url),
App(PathBuf),
}
impl fmt::Display for WindowUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::External(url) => write!(f, "{url}"),
Self::App(path) => write!(f, "{}", path.display()),
}
}
}
impl Default for WindowUrl {
fn default() -> Self {
Self::App("index.html".into())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))]
pub enum BundleType {
Deb,
AppImage,
Msi,
Nsis,
App,
Dmg,
Updater,
}
impl Display for BundleType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Deb => "deb",
Self::AppImage => "appimage",
Self::Msi => "msi",
Self::Nsis => "nsis",
Self::App => "app",
Self::Dmg => "dmg",
Self::Updater => "updater",
}
)
}
}
impl Serialize for BundleType {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
impl<'de> Deserialize<'de> for BundleType {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
"deb" => Ok(Self::Deb),
"appimage" => Ok(Self::AppImage),
"msi" => Ok(Self::Msi),
"nsis" => Ok(Self::Nsis),
"app" => Ok(Self::App),
"dmg" => Ok(Self::Dmg),
"updater" => Ok(Self::Updater),
_ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BundleTarget {
All,
List(Vec<BundleType>),
One(BundleType),
}
#[cfg(feature = "schema")]
impl schemars::JsonSchema for BundleTarget {
fn schema_name() -> std::string::String {
"BundleTarget".to_owned()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
let any_of = vec![
schemars::schema::SchemaObject {
enum_values: Some(vec!["all".into()]),
metadata: Some(Box::new(schemars::schema::Metadata {
description: Some("Bundle all targets.".to_owned()),
..Default::default()
})),
..Default::default()
}
.into(),
schemars::_private::apply_metadata(
gen.subschema_for::<Vec<BundleType>>(),
schemars::schema::Metadata {
description: Some("A list of bundle targets.".to_owned()),
..Default::default()
},
),
schemars::_private::apply_metadata(
gen.subschema_for::<BundleType>(),
schemars::schema::Metadata {
description: Some("A single bundle target.".to_owned()),
..Default::default()
},
),
];
schemars::schema::SchemaObject {
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
any_of: Some(any_of),
..Default::default()
})),
metadata: Some(Box::new(schemars::schema::Metadata {
description: Some("Targets to bundle. Each value is case insensitive.".to_owned()),
..Default::default()
})),
..Default::default()
}
.into()
}
}
impl Default for BundleTarget {
fn default() -> Self {
Self::All
}
}
impl Serialize for BundleTarget {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::All => serializer.serialize_str("all"),
Self::List(l) => l.serialize(serializer),
Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
}
}
}
impl<'de> Deserialize<'de> for BundleTarget {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize, Serialize)]
#[serde(untagged)]
pub enum BundleTargetInner {
List(Vec<BundleType>),
One(BundleType),
All(String),
}
match BundleTargetInner::deserialize(deserializer)? {
BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {t}"))),
BundleTargetInner::List(l) => Ok(Self::List(l)),
BundleTargetInner::One(t) => Ok(Self::One(t)),
}
}
}
impl BundleTarget {
#[allow(dead_code)]
pub fn to_vec(&self) -> Vec<BundleType> {
match self {
Self::All => vec![],
Self::List(list) => list.clone(),
Self::One(i) => vec![i.clone()],
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AppImageConfig {
#[serde(default, alias = "bundle-media-framework")]
pub bundle_media_framework: bool,
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DebConfig {
pub depends: Option<Vec<String>>,
#[serde(default)]
pub files: HashMap<PathBuf, PathBuf>,
}
fn de_minimum_system_version<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let version = Option::<String>::deserialize(deserializer)?;
match version {
Some(v) if v.is_empty() => Ok(minimum_system_version()),
e => Ok(e),
}
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct MacConfig {
pub frameworks: Option<Vec<String>>,
#[serde(
deserialize_with = "de_minimum_system_version",
default = "minimum_system_version",
alias = "minimum-system-version"
)]
pub minimum_system_version: Option<String>,
#[serde(alias = "exception-domain")]
pub exception_domain: Option<String>,
pub license: Option<String>,
#[serde(alias = "signing-identity")]
pub signing_identity: Option<String>,
#[serde(alias = "provider-short-name")]
pub provider_short_name: Option<String>,
pub entitlements: Option<String>,
}
impl Default for MacConfig {
fn default() -> Self {
Self {
frameworks: None,
minimum_system_version: minimum_system_version(),
exception_domain: None,
license: None,
signing_identity: None,
provider_short_name: None,
entitlements: None,
}
}
}
fn minimum_system_version() -> Option<String> {
Some("10.13".into())
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WixLanguageConfig {
#[serde(alias = "locale-path")]
pub locale_path: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum WixLanguage {
One(String),
List(Vec<String>),
Localized(HashMap<String, WixLanguageConfig>),
}
impl Default for WixLanguage {
fn default() -> Self {
Self::One("en-US".into())
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WixConfig {
#[serde(default)]
pub language: WixLanguage,
pub template: Option<PathBuf>,
#[serde(default, alias = "fragment-paths")]
pub fragment_paths: Vec<PathBuf>,
#[serde(default, alias = "component-group-refs")]
pub component_group_refs: Vec<String>,
#[serde(default, alias = "component-refs")]
pub component_refs: Vec<String>,
#[serde(default, alias = "feature-group-refs")]
pub feature_group_refs: Vec<String>,
#[serde(default, alias = "feature-refs")]
pub feature_refs: Vec<String>,
#[serde(default, alias = "merge-refs")]
pub merge_refs: Vec<String>,
#[serde(default, alias = "skip-webview-install")]
pub skip_webview_install: bool,
pub license: Option<PathBuf>,
#[serde(default, alias = "enable-elevated-update-task")]
pub enable_elevated_update_task: bool,
#[serde(alias = "banner-path")]
pub banner_path: Option<PathBuf>,
#[serde(alias = "dialog-image-path")]
pub dialog_image_path: Option<PathBuf>,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct NsisConfig {
pub license: Option<PathBuf>,
#[serde(alias = "header-image")]
pub header_image: Option<PathBuf>,
#[serde(alias = "sidebar-image")]
pub sidebar_image: Option<PathBuf>,
#[serde(alias = "install-icon")]
pub installer_icon: Option<PathBuf>,
#[serde(default, alias = "install-mode")]
pub install_mode: NSISInstallerMode,
pub languages: Option<Vec<String>>,
#[serde(default, alias = "display-language-selector")]
pub display_language_selector: bool,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum NSISInstallerMode {
CurrentUser,
PerMachine,
Both,
}
impl Default for NSISInstallerMode {
fn default() -> Self {
Self::CurrentUser
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum WebviewInstallMode {
Skip,
DownloadBootstrapper {
#[serde(default = "default_true")]
silent: bool,
},
EmbedBootstrapper {
#[serde(default = "default_true")]
silent: bool,
},
OfflineInstaller {
#[serde(default = "default_true")]
silent: bool,
},
FixedRuntime {
path: PathBuf,
},
}
impl Default for WebviewInstallMode {
fn default() -> Self {
Self::DownloadBootstrapper {
silent: default_true(),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowsConfig {
#[serde(alias = "digest-algorithm")]
pub digest_algorithm: Option<String>,
#[serde(alias = "certificate-thumbprint")]
pub certificate_thumbprint: Option<String>,
#[serde(alias = "timestamp-url")]
pub timestamp_url: Option<String>,
#[serde(default)]
pub tsp: bool,
#[serde(default, alias = "webview-install-mode")]
pub webview_install_mode: WebviewInstallMode,
#[serde(alias = "webview-fixed-runtime-path")]
pub webview_fixed_runtime_path: Option<PathBuf>,
#[serde(default = "default_true", alias = "allow-downgrades")]
pub allow_downgrades: bool,
pub wix: Option<WixConfig>,
pub nsis: Option<NsisConfig>,
}
impl Default for WindowsConfig {
fn default() -> Self {
Self {
digest_algorithm: None,
certificate_thumbprint: None,
timestamp_url: None,
tsp: false,
webview_install_mode: Default::default(),
webview_fixed_runtime_path: None,
allow_downgrades: default_true(),
wix: None,
nsis: None,
}
}
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BundleConfig {
#[serde(default)]
pub active: bool,
#[serde(default)]
pub targets: BundleTarget,
pub identifier: String,
pub publisher: Option<String>,
#[serde(default)]
pub icon: Vec<String>,
pub resources: Option<Vec<String>>,
pub copyright: Option<String>,
pub category: Option<String>,
#[serde(alias = "short-description")]
pub short_description: Option<String>,
#[serde(alias = "long-description")]
pub long_description: Option<String>,
#[serde(default)]
pub appimage: AppImageConfig,
#[serde(default)]
pub deb: DebConfig,
#[serde(rename = "macOS", default)]
pub macos: MacConfig,
#[serde(alias = "external-bin")]
pub external_bin: Option<Vec<String>>,
#[serde(default)]
pub windows: WindowsConfig,
#[serde(rename = "iOS", default)]
pub ios: IosConfig,
#[serde(default)]
pub android: AndroidConfig,
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CliArg {
pub short: Option<char>,
pub name: String,
pub description: Option<String>,
#[serde(alias = "long-description")]
pub long_description: Option<String>,
#[serde(default, alias = "takes-value")]
pub takes_value: bool,
#[serde(default)]
pub multiple: bool,
#[serde(default, alias = "multiple-occurrences")]
pub multiple_occurrences: bool,
#[serde(alias = "number-of-values")]
pub number_of_values: Option<usize>,
#[serde(alias = "possible-values")]
pub possible_values: Option<Vec<String>>,
#[serde(alias = "min-values")]
pub min_values: Option<usize>,
#[serde(alias = "max-values")]
pub max_values: Option<usize>,
#[serde(default)]
pub required: bool,
#[serde(alias = "required-unless-present")]
pub required_unless_present: Option<String>,
#[serde(alias = "required-unless-present-all")]
pub required_unless_present_all: Option<Vec<String>>,
#[serde(alias = "required-unless-present-any")]
pub required_unless_present_any: Option<Vec<String>>,
#[serde(alias = "conflicts-with")]
pub conflicts_with: Option<String>,
#[serde(alias = "conflicts-with-all")]
pub conflicts_with_all: Option<Vec<String>>,
pub requires: Option<String>,
#[serde(alias = "requires-all")]
pub requires_all: Option<Vec<String>>,
#[serde(alias = "requires-if")]
pub requires_if: Option<Vec<String>>,
#[serde(alias = "requires-if-eq")]
pub required_if_eq: Option<Vec<String>>,
#[serde(alias = "requires-equals")]
pub require_equals: Option<bool>,
#[cfg_attr(feature = "schema", validate(range(min = 1)))]
pub index: Option<usize>,
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CliConfig {
pub description: Option<String>,
#[serde(alias = "long-description")]
pub long_description: Option<String>,
#[serde(alias = "before-help")]
pub before_help: Option<String>,
#[serde(alias = "after-help")]
pub after_help: Option<String>,
pub args: Option<Vec<CliArg>>,
pub subcommands: Option<HashMap<String, CliConfig>>,
}
impl CliConfig {
pub fn args(&self) -> Option<&Vec<CliArg>> {
self.args.as_ref()
}
pub fn subcommands(&self) -> Option<&HashMap<String, CliConfig>> {
self.subcommands.as_ref()
}
pub fn description(&self) -> Option<&String> {
self.description.as_ref()
}
pub fn long_description(&self) -> Option<&String> {
self.description.as_ref()
}
pub fn before_help(&self) -> Option<&String> {
self.before_help.as_ref()
}
pub fn after_help(&self) -> Option<&String> {
self.after_help.as_ref()
}
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowConfig {
#[serde(default = "default_window_label")]
pub label: String,
#[serde(default)]
pub url: WindowUrl,
#[serde(alias = "user-agent")]
pub user_agent: Option<String>,
#[serde(default = "default_true", alias = "file-drop-enabled")]
pub file_drop_enabled: bool,
#[serde(default)]
pub center: bool,
pub x: Option<f64>,
pub y: Option<f64>,
#[serde(default = "default_width")]
pub width: f64,
#[serde(default = "default_height")]
pub height: f64,
#[serde(alias = "min-width")]
pub min_width: Option<f64>,
#[serde(alias = "min-height")]
pub min_height: Option<f64>,
#[serde(alias = "max-width")]
pub max_width: Option<f64>,
#[serde(alias = "max-height")]
pub max_height: Option<f64>,
#[serde(default = "default_true")]
pub resizable: bool,
#[serde(default = "default_title")]
pub title: String,
#[serde(default)]
pub fullscreen: bool,
#[serde(default = "default_true")]
pub focus: bool,
#[serde(default)]
pub transparent: bool,
#[serde(default)]
pub maximized: bool,
#[serde(default = "default_true")]
pub visible: bool,
#[serde(default = "default_true")]
pub decorations: bool,
#[serde(default, alias = "always-on-top")]
pub always_on_top: bool,
#[serde(default, alias = "content-protected")]
pub content_protected: bool,
#[serde(default, alias = "skip-taskbar")]
pub skip_taskbar: bool,
pub theme: Option<crate::Theme>,
#[serde(default, alias = "title-bar-style")]
pub title_bar_style: TitleBarStyle,
#[serde(default, alias = "hidden-title")]
pub hidden_title: bool,
#[serde(default, alias = "accept-first-mouse")]
pub accept_first_mouse: bool,
#[serde(default, alias = "tabbing-identifier")]
pub tabbing_identifier: Option<String>,
#[serde(default, alias = "additional-browser-args")]
pub additional_browser_args: Option<String>,
#[serde(default)]
pub shadow: bool,
}
impl Default for WindowConfig {
fn default() -> Self {
Self {
label: default_window_label(),
url: WindowUrl::default(),
user_agent: None,
file_drop_enabled: default_true(),
center: false,
x: None,
y: None,
width: default_width(),
height: default_height(),
min_width: None,
min_height: None,
max_width: None,
max_height: None,
resizable: default_true(),
title: default_title(),
fullscreen: false,
focus: false,
transparent: false,
maximized: false,
visible: default_true(),
decorations: default_true(),
always_on_top: false,
content_protected: false,
skip_taskbar: false,
theme: None,
title_bar_style: Default::default(),
hidden_title: false,
accept_first_mouse: false,
tabbing_identifier: None,
additional_browser_args: None,
shadow: false,
}
}
}
fn default_window_label() -> String {
"main".to_string()
}
fn default_width() -> f64 {
800f64
}
fn default_height() -> f64 {
600f64
}
fn default_true() -> bool {
true
}
fn default_title() -> String {
"Tauri App".to_string()
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum CspDirectiveSources {
Inline(String),
List(Vec<String>),
}
impl Default for CspDirectiveSources {
fn default() -> Self {
Self::List(Vec::new())
}
}
impl From<CspDirectiveSources> for Vec<String> {
fn from(sources: CspDirectiveSources) -> Self {
match sources {
CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(),
CspDirectiveSources::List(l) => l,
}
}
}
impl CspDirectiveSources {
pub fn contains(&self, source: &str) -> bool {
match self {
Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")),
Self::List(l) => l.contains(&source.into()),
}
}
pub fn push<S: AsRef<str>>(&mut self, source: S) {
match self {
Self::Inline(s) => {
s.push(' ');
s.push_str(source.as_ref());
}
Self::List(l) => {
l.push(source.as_ref().to_string());
}
}
}
pub fn extend(&mut self, sources: Vec<String>) {
for s in sources {
self.push(s);
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum Csp {
Policy(String),
DirectiveMap(HashMap<String, CspDirectiveSources>),
}
impl From<HashMap<String, CspDirectiveSources>> for Csp {
fn from(map: HashMap<String, CspDirectiveSources>) -> Self {
Self::DirectiveMap(map)
}
}
impl From<Csp> for HashMap<String, CspDirectiveSources> {
fn from(csp: Csp) -> Self {
match csp {
Csp::Policy(policy) => {
let mut map = HashMap::new();
for directive in policy.split(';') {
let mut tokens = directive.trim().split(' ');
if let Some(directive) = tokens.next() {
let sources = tokens.map(|s| s.to_string()).collect::<Vec<String>>();
map.insert(directive.to_string(), CspDirectiveSources::List(sources));
}
}
map
}
Csp::DirectiveMap(m) => m,
}
}
}
impl Display for Csp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Policy(s) => write!(f, "{s}"),
Self::DirectiveMap(m) => {
let len = m.len();
let mut i = 0;
for (directive, sources) in m {
let sources: Vec<String> = sources.clone().into();
write!(f, "{} {}", directive, sources.join(" "))?;
i += 1;
if i != len {
write!(f, "; ")?;
}
}
Ok(())
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum DisabledCspModificationKind {
Flag(bool),
List(Vec<String>),
}
impl DisabledCspModificationKind {
pub fn can_modify(&self, directive: &str) -> bool {
match self {
Self::Flag(f) => !f,
Self::List(l) => !l.contains(&directive.into()),
}
}
}
impl Default for DisabledCspModificationKind {
fn default() -> Self {
Self::Flag(false)
}
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SecurityConfig {
pub csp: Option<Csp>,
#[serde(alias = "dev-csp")]
pub dev_csp: Option<Csp>,
#[serde(default, alias = "freeze-prototype")]
pub freeze_prototype: bool,
#[serde(default, alias = "dangerous-disable-asset-csp-modification")]
pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
}
pub trait Allowlist {
fn all_features() -> Vec<&'static str>;
fn to_features(&self) -> Vec<&'static str>;
}
macro_rules! check_feature {
($self:ident, $features:ident, $flag:ident, $feature_name: expr) => {
if $self.$flag {
$features.push($feature_name)
}
};
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum FsAllowlistScope {
AllowedPaths(Vec<PathBuf>),
Scope {
#[serde(default)]
allow: Vec<PathBuf>,
#[serde(default)]
deny: Vec<PathBuf>,
},
}
impl Default for FsAllowlistScope {
fn default() -> Self {
Self::AllowedPaths(Vec::new())
}
}
impl FsAllowlistScope {
pub fn allowed_paths(&self) -> &Vec<PathBuf> {
match self {
Self::AllowedPaths(p) => p,
Self::Scope { allow, .. } => allow,
}
}
pub fn forbidden_paths(&self) -> Option<&Vec<PathBuf>> {
match self {
Self::AllowedPaths(_) => None,
Self::Scope { deny, .. } => Some(deny),
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct FsAllowlistConfig {
#[serde(default)]
pub scope: FsAllowlistScope,
#[serde(default)]
pub all: bool,
#[serde(default, alias = "read-file")]
pub read_file: bool,
#[serde(default, alias = "write-file")]
pub write_file: bool,
#[serde(default, alias = "read-dir")]
pub read_dir: bool,
#[serde(default, alias = "copy-file")]
pub copy_file: bool,
#[serde(default, alias = "create-dir")]
pub create_dir: bool,
#[serde(default, alias = "remove-dir")]
pub remove_dir: bool,
#[serde(default, alias = "remove-file")]
pub remove_file: bool,
#[serde(default, alias = "rename-file")]
pub rename_file: bool,
#[serde(default)]
pub exists: bool,
}
impl Allowlist for FsAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
scope: Default::default(),
all: false,
read_file: true,
write_file: true,
read_dir: true,
copy_file: true,
create_dir: true,
remove_dir: true,
remove_file: true,
rename_file: true,
exists: true,
};
let mut features = allowlist.to_features();
features.push("fs-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["fs-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, read_file, "fs-read-file");
check_feature!(self, features, write_file, "fs-write-file");
check_feature!(self, features, read_dir, "fs-read-dir");
check_feature!(self, features, copy_file, "fs-copy-file");
check_feature!(self, features, create_dir, "fs-create-dir");
check_feature!(self, features, remove_dir, "fs-remove-dir");
check_feature!(self, features, remove_file, "fs-remove-file");
check_feature!(self, features, rename_file, "fs-rename-file");
check_feature!(self, features, exists, "fs-exists");
features
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowAllowlistConfig {
#[serde(default)]
pub all: bool,
#[serde(default)]
pub create: bool,
#[serde(default)]
pub center: bool,
#[serde(default, alias = "request-user-attention")]
pub request_user_attention: bool,
#[serde(default, alias = "set-resizable")]
pub set_resizable: bool,
#[serde(default, alias = "set-title")]
pub set_title: bool,
#[serde(default)]
pub maximize: bool,
#[serde(default)]
pub unmaximize: bool,
#[serde(default)]
pub minimize: bool,
#[serde(default)]
pub unminimize: bool,
#[serde(default)]
pub show: bool,
#[serde(default)]
pub hide: bool,
#[serde(default)]
pub close: bool,
#[serde(default, alias = "set-decorations")]
pub set_decorations: bool,
#[serde(default, alias = "set-shadow")]
pub set_shadow: bool,
#[serde(default, alias = "set-always-on-top")]
pub set_always_on_top: bool,
#[serde(default, alias = "set-content-protected")]
pub set_content_protected: bool,
#[serde(default, alias = "set-size")]
pub set_size: bool,
#[serde(default, alias = "set-min-size")]
pub set_min_size: bool,
#[serde(default, alias = "set-max-size")]
pub set_max_size: bool,
#[serde(default, alias = "set-position")]
pub set_position: bool,
#[serde(default, alias = "set-fullscreen")]
pub set_fullscreen: bool,
#[serde(default, alias = "set-focus")]
pub set_focus: bool,
#[serde(default, alias = "set-icon")]
pub set_icon: bool,
#[serde(default, alias = "set-skip-taskbar")]
pub set_skip_taskbar: bool,
#[serde(default, alias = "set-cursor-grab")]
pub set_cursor_grab: bool,
#[serde(default, alias = "set-cursor-visible")]
pub set_cursor_visible: bool,
#[serde(default, alias = "set-cursor-icon")]
pub set_cursor_icon: bool,
#[serde(default, alias = "set-cursor-position")]
pub set_cursor_position: bool,
#[serde(default, alias = "set-ignore-cursor-events")]
pub set_ignore_cursor_events: bool,
#[serde(default, alias = "start-dragging")]
pub start_dragging: bool,
#[serde(default)]
pub print: bool,
}
impl Allowlist for WindowAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
create: true,
center: true,
request_user_attention: true,
set_resizable: true,
set_title: true,
maximize: true,
unmaximize: true,
minimize: true,
unminimize: true,
show: true,
hide: true,
close: true,
set_decorations: true,
set_always_on_top: true,
set_content_protected: false,
set_size: true,
set_min_size: true,
set_max_size: true,
set_position: true,
set_fullscreen: true,
set_focus: true,
set_icon: true,
set_skip_taskbar: true,
set_cursor_grab: true,
set_cursor_visible: true,
set_cursor_icon: true,
set_cursor_position: true,
set_ignore_cursor_events: true,
set_shadow: true,
start_dragging: true,
print: true,
};
let mut features = allowlist.to_features();
features.push("window-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["window-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, create, "window-create");
check_feature!(self, features, center, "window-center");
check_feature!(
self,
features,
request_user_attention,
"window-request-user-attention"
);
check_feature!(self, features, set_resizable, "window-set-resizable");
check_feature!(self, features, set_title, "window-set-title");
check_feature!(self, features, maximize, "window-maximize");
check_feature!(self, features, unmaximize, "window-unmaximize");
check_feature!(self, features, minimize, "window-minimize");
check_feature!(self, features, unminimize, "window-unminimize");
check_feature!(self, features, show, "window-show");
check_feature!(self, features, hide, "window-hide");
check_feature!(self, features, close, "window-close");
check_feature!(self, features, set_decorations, "window-set-decorations");
check_feature!(self, features, set_shadow, "window-set-shadow");
check_feature!(
self,
features,
set_always_on_top,
"window-set-always-on-top"
);
check_feature!(
self,
features,
set_content_protected,
"window-set-content-protected"
);
check_feature!(self, features, set_size, "window-set-size");
check_feature!(self, features, set_min_size, "window-set-min-size");
check_feature!(self, features, set_max_size, "window-set-max-size");
check_feature!(self, features, set_position, "window-set-position");
check_feature!(self, features, set_fullscreen, "window-set-fullscreen");
check_feature!(self, features, set_focus, "window-set-focus");
check_feature!(self, features, set_icon, "window-set-icon");
check_feature!(self, features, set_skip_taskbar, "window-set-skip-taskbar");
check_feature!(self, features, set_cursor_grab, "window-set-cursor-grab");
check_feature!(
self,
features,
set_cursor_visible,
"window-set-cursor-visible"
);
check_feature!(self, features, set_cursor_icon, "window-set-cursor-icon");
check_feature!(
self,
features,
set_cursor_position,
"window-set-cursor-position"
);
check_feature!(
self,
features,
set_ignore_cursor_events,
"window-set-ignore-cursor-events"
);
check_feature!(self, features, start_dragging, "window-start-dragging");
check_feature!(self, features, print, "window-print");
features
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct ShellAllowedCommand {
pub name: String,
#[serde(rename = "cmd", default)] pub command: PathBuf,
#[serde(default)]
pub args: ShellAllowedArgs,
#[serde(default)]
pub sidecar: bool,
}
impl<'de> Deserialize<'de> for ShellAllowedCommand {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct InnerShellAllowedCommand {
name: String,
#[serde(rename = "cmd")]
command: Option<PathBuf>,
#[serde(default)]
args: ShellAllowedArgs,
#[serde(default)]
sidecar: bool,
}
let config = InnerShellAllowedCommand::deserialize(deserializer)?;
if !config.sidecar && config.command.is_none() {
return Err(DeError::custom(
"The shell scope `command` value is required.",
));
}
Ok(ShellAllowedCommand {
name: config.name,
command: config.command.unwrap_or_default(),
args: config.args,
sidecar: config.sidecar,
})
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellAllowedArgs {
Flag(bool),
List(Vec<ShellAllowedArg>),
}
impl Default for ShellAllowedArgs {
fn default() -> Self {
Self::Flag(false)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellAllowedArg {
Fixed(String),
Var {
validator: String,
},
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct ShellAllowlistScope(pub Vec<ShellAllowedCommand>);
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellAllowlistOpen {
Flag(bool),
Validate(String),
}
impl Default for ShellAllowlistOpen {
fn default() -> Self {
Self::Flag(false)
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ShellAllowlistConfig {
#[serde(default)]
pub scope: ShellAllowlistScope,
#[serde(default)]
pub all: bool,
#[serde(default)]
pub execute: bool,
#[serde(default)]
pub sidecar: bool,
#[serde(default)]
pub open: ShellAllowlistOpen,
}
impl Allowlist for ShellAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
scope: Default::default(),
all: false,
execute: true,
sidecar: true,
open: ShellAllowlistOpen::Flag(true),
};
let mut features = allowlist.to_features();
features.push("shell-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["shell-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, execute, "shell-execute");
check_feature!(self, features, sidecar, "shell-sidecar");
if !matches!(self.open, ShellAllowlistOpen::Flag(false)) {
features.push("shell-open")
}
features
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DialogAllowlistConfig {
#[serde(default)]
pub all: bool,
#[serde(default)]
pub open: bool,
#[serde(default)]
pub save: bool,
#[serde(default)]
pub message: bool,
#[serde(default)]
pub ask: bool,
#[serde(default)]
pub confirm: bool,
}
impl Allowlist for DialogAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
open: true,
save: true,
message: true,
ask: true,
confirm: true,
};
let mut features = allowlist.to_features();
features.push("dialog-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["dialog-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, open, "dialog-open");
check_feature!(self, features, save, "dialog-save");
check_feature!(self, features, message, "dialog-message");
check_feature!(self, features, ask, "dialog-ask");
check_feature!(self, features, confirm, "dialog-confirm");
features
}
}
}
#[allow(rustdoc::bare_urls)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct HttpAllowlistScope(pub Vec<Url>);
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct HttpAllowlistConfig {
#[serde(default)]
pub scope: HttpAllowlistScope,
#[serde(default)]
pub all: bool,
#[serde(default)]
pub request: bool,
}
impl Allowlist for HttpAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
scope: Default::default(),
all: false,
request: true,
};
let mut features = allowlist.to_features();
features.push("http-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["http-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, request, "http-request");
features
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct NotificationAllowlistConfig {
#[serde(default)]
pub all: bool,
}
impl Allowlist for NotificationAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("notification-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["notification-all"]
} else {
vec![]
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GlobalShortcutAllowlistConfig {
#[serde(default)]
pub all: bool,
}
impl Allowlist for GlobalShortcutAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("global-shortcut-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["global-shortcut-all"]
} else {
vec![]
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct OsAllowlistConfig {
#[serde(default)]
pub all: bool,
}
impl Allowlist for OsAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("os-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["os-all"]
} else {
vec![]
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PathAllowlistConfig {
#[serde(default)]
pub all: bool,
}
impl Allowlist for PathAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self { all: false };
let mut features = allowlist.to_features();
features.push("path-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["path-all"]
} else {
vec![]
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProtocolAllowlistConfig {
#[serde(default, alias = "asset-scope")]
pub asset_scope: FsAllowlistScope,
#[serde(default)]
pub all: bool,
#[serde(default)]
pub asset: bool,
}
impl Allowlist for ProtocolAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
asset_scope: Default::default(),
all: false,
asset: true,
};
let mut features = allowlist.to_features();
features.push("protocol-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["protocol-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, asset, "protocol-asset");
features
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProcessAllowlistConfig {
#[serde(default)]
pub all: bool,
#[serde(default)]
pub relaunch: bool,
#[serde(
default,
alias = "relaunchDangerousAllowSymlinkMacOS",
alias = "relaunch-dangerous-allow-symlink-macos"
)]
pub relaunch_dangerous_allow_symlink_macos: bool,
#[serde(default)]
pub exit: bool,
}
impl Allowlist for ProcessAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
relaunch: true,
relaunch_dangerous_allow_symlink_macos: false,
exit: true,
};
let mut features = allowlist.to_features();
features.push("process-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["process-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, relaunch, "process-relaunch");
check_feature!(
self,
features,
relaunch_dangerous_allow_symlink_macos,
"process-relaunch-dangerous-allow-symlink-macos"
);
check_feature!(self, features, exit, "process-exit");
features
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ClipboardAllowlistConfig {
#[serde(default)]
pub all: bool,
#[serde(default, alias = "writeText")]
pub write_text: bool,
#[serde(default, alias = "readText")]
pub read_text: bool,
}
impl Allowlist for ClipboardAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
write_text: true,
read_text: true,
};
let mut features = allowlist.to_features();
features.push("clipboard-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["clipboard-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, write_text, "clipboard-write-text");
check_feature!(self, features, read_text, "clipboard-read-text");
features
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AppAllowlistConfig {
#[serde(default)]
pub all: bool,
#[serde(default)]
pub show: bool,
#[serde(default)]
pub hide: bool,
}
impl Allowlist for AppAllowlistConfig {
fn all_features() -> Vec<&'static str> {
let allowlist = Self {
all: false,
show: true,
hide: true,
};
let mut features = allowlist.to_features();
features.push("app-all");
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["app-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, show, "app-show");
check_feature!(self, features, hide, "app-hide");
features
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AllowlistConfig {
#[serde(default)]
pub all: bool,
#[serde(default)]
pub fs: FsAllowlistConfig,
#[serde(default)]
pub window: WindowAllowlistConfig,
#[serde(default)]
pub shell: ShellAllowlistConfig,
#[serde(default)]
pub dialog: DialogAllowlistConfig,
#[serde(default)]
pub http: HttpAllowlistConfig,
#[serde(default)]
pub notification: NotificationAllowlistConfig,
#[serde(default, alias = "global-shortcut")]
pub global_shortcut: GlobalShortcutAllowlistConfig,
#[serde(default)]
pub os: OsAllowlistConfig,
#[serde(default)]
pub path: PathAllowlistConfig,
#[serde(default)]
pub protocol: ProtocolAllowlistConfig,
#[serde(default)]
pub process: ProcessAllowlistConfig,
#[serde(default)]
pub clipboard: ClipboardAllowlistConfig,
#[serde(default)]
pub app: AppAllowlistConfig,
}
impl Allowlist for AllowlistConfig {
fn all_features() -> Vec<&'static str> {
let mut features = vec!["api-all"];
features.extend(FsAllowlistConfig::all_features());
features.extend(WindowAllowlistConfig::all_features());
features.extend(ShellAllowlistConfig::all_features());
features.extend(DialogAllowlistConfig::all_features());
features.extend(HttpAllowlistConfig::all_features());
features.extend(NotificationAllowlistConfig::all_features());
features.extend(GlobalShortcutAllowlistConfig::all_features());
features.extend(OsAllowlistConfig::all_features());
features.extend(PathAllowlistConfig::all_features());
features.extend(ProtocolAllowlistConfig::all_features());
features.extend(ProcessAllowlistConfig::all_features());
features.extend(ClipboardAllowlistConfig::all_features());
features.extend(AppAllowlistConfig::all_features());
features
}
fn to_features(&self) -> Vec<&'static str> {
if self.all {
vec!["api-all"]
} else {
let mut features = Vec::new();
features.extend(self.fs.to_features());
features.extend(self.window.to_features());
features.extend(self.shell.to_features());
features.extend(self.dialog.to_features());
features.extend(self.http.to_features());
features.extend(self.notification.to_features());
features.extend(self.global_shortcut.to_features());
features.extend(self.os.to_features());
features.extend(self.path.to_features());
features.extend(self.protocol.to_features());
features.extend(self.process.to_features());
features.extend(self.clipboard.to_features());
features.extend(self.app.to_features());
features
}
}
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase", tag = "use", content = "options")]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum PatternKind {
Brownfield,
Isolation {
dir: PathBuf,
},
}
impl Default for PatternKind {
fn default() -> Self {
Self::Brownfield
}
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TauriConfig {
#[serde(default)]
pub pattern: PatternKind,
#[serde(default)]
pub windows: Vec<WindowConfig>,
pub cli: Option<CliConfig>,
#[serde(default)]
pub bundle: BundleConfig,
#[serde(default)]
pub allowlist: AllowlistConfig,
#[serde(default)]
pub security: SecurityConfig,
#[serde(default)]
pub updater: UpdaterConfig,
#[serde(alias = "system-tray")]
pub system_tray: Option<SystemTrayConfig>,
#[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)]
pub macos_private_api: bool,
}
impl TauriConfig {
#[allow(dead_code)]
pub fn all_features() -> Vec<&'static str> {
let mut features = AllowlistConfig::all_features();
features.extend(vec![
"cli",
"updater",
"system-tray",
"macos-private-api",
"isolation",
]);
features
}
#[allow(dead_code)]
pub fn features(&self) -> Vec<&str> {
let mut features = self.allowlist.to_features();
if self.cli.is_some() {
features.push("cli");
}
if self.updater.active {
features.push("updater");
}
if self.system_tray.is_some() {
features.push("system-tray");
}
if self.macos_private_api {
features.push("macos-private-api");
}
if let PatternKind::Isolation { .. } = self.pattern {
features.push("isolation");
}
features.sort_unstable();
features
}
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct UpdaterEndpoint(pub Url);
impl std::fmt::Display for UpdaterEndpoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'de> Deserialize<'de> for UpdaterEndpoint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let url = Url::deserialize(deserializer)?;
#[cfg(all(not(debug_assertions), not(feature = "schema")))]
{
if url.scheme() != "https" {
return Err(serde::de::Error::custom(
"The configured updater endpoint must use the `https` protocol.",
));
}
}
Ok(Self(url))
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(rename_all = "camelCase"))]
pub enum WindowsUpdateInstallMode {
BasicUi,
Quiet,
Passive,
}
impl WindowsUpdateInstallMode {
pub fn msiexec_args(&self) -> &'static [&'static str] {
match self {
Self::BasicUi => &["/qb+"],
Self::Quiet => &["/quiet"],
Self::Passive => &["/passive"],
}
}
}
impl Display for WindowsUpdateInstallMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::BasicUi => "basicUI",
Self::Quiet => "quiet",
Self::Passive => "passive",
}
)
}
}
impl Default for WindowsUpdateInstallMode {
fn default() -> Self {
Self::Passive
}
}
impl Serialize for WindowsUpdateInstallMode {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
impl<'de> Deserialize<'de> for WindowsUpdateInstallMode {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
"basicui" => Ok(Self::BasicUi),
"quiet" => Ok(Self::Quiet),
"passive" => Ok(Self::Passive),
_ => Err(DeError::custom(format!(
"unknown update install mode '{s}'"
))),
}
}
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UpdaterWindowsConfig {
#[serde(default, alias = "installer-args")]
pub installer_args: Vec<String>,
#[serde(default, alias = "install-mode")]
pub install_mode: WindowsUpdateInstallMode,
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UpdaterConfig {
#[serde(default)]
pub active: bool,
#[serde(default = "default_true")]
pub dialog: bool,
#[allow(rustdoc::bare_urls)]
pub endpoints: Option<Vec<UpdaterEndpoint>>,
#[serde(default)] pub pubkey: String,
#[serde(default)]
pub windows: UpdaterWindowsConfig,
}
impl<'de> Deserialize<'de> for UpdaterConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct InnerUpdaterConfig {
#[serde(default)]
active: bool,
#[serde(default = "default_true")]
dialog: bool,
endpoints: Option<Vec<UpdaterEndpoint>>,
pubkey: Option<String>,
#[serde(default)]
windows: UpdaterWindowsConfig,
}
let config = InnerUpdaterConfig::deserialize(deserializer)?;
if config.active && config.pubkey.is_none() {
return Err(DeError::custom(
"The updater `pubkey` configuration is required.",
));
}
Ok(UpdaterConfig {
active: config.active,
dialog: config.dialog,
endpoints: config.endpoints,
pubkey: config.pubkey.unwrap_or_default(),
windows: config.windows,
})
}
}
impl Default for UpdaterConfig {
fn default() -> Self {
Self {
active: false,
dialog: default_true(),
endpoints: None,
pubkey: "".into(),
windows: Default::default(),
}
}
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SystemTrayConfig {
#[serde(alias = "icon-path")]
pub icon_path: PathBuf,
#[serde(default, alias = "icon-as-template")]
pub icon_as_template: bool,
#[serde(default = "default_true", alias = "menu-on-left-click")]
pub menu_on_left_click: bool,
pub title: Option<String>,
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct IosConfig {
#[serde(alias = "development-team")]
pub development_team: Option<String>,
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AndroidConfig {
#[serde(alias = "min-sdk-version", default = "default_min_sdk_version")]
pub min_sdk_version: u32,
}
impl Default for AndroidConfig {
fn default() -> Self {
Self {
min_sdk_version: default_min_sdk_version(),
}
}
}
fn default_min_sdk_version() -> u32 {
24
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum AppUrl {
Url(WindowUrl),
Files(Vec<PathBuf>),
}
impl std::fmt::Display for AppUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Url(url) => write!(f, "{url}"),
Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum BeforeDevCommand {
Script(String),
ScriptWithOptions {
script: String,
cwd: Option<String>,
#[serde(default)]
wait: bool,
},
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
pub enum HookCommand {
Script(String),
ScriptWithOptions {
script: String,
cwd: Option<String>,
},
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BuildConfig {
pub runner: Option<String>,
#[serde(default = "default_dev_path", alias = "dev-path")]
pub dev_path: AppUrl,
#[serde(default = "default_dist_dir", alias = "dist-dir")]
pub dist_dir: AppUrl,
#[serde(alias = "before-dev-command")]
pub before_dev_command: Option<BeforeDevCommand>,
#[serde(alias = "before-build-command")]
pub before_build_command: Option<HookCommand>,
#[serde(alias = "before-bundle-command")]
pub before_bundle_command: Option<HookCommand>,
pub features: Option<Vec<String>>,
#[serde(default, alias = "with-global-tauri")]
pub with_global_tauri: bool,
}
impl Default for BuildConfig {
fn default() -> Self {
Self {
runner: None,
dev_path: default_dev_path(),
dist_dir: default_dist_dir(),
before_dev_command: None,
before_build_command: None,
before_bundle_command: None,
features: None,
with_global_tauri: false,
}
}
}
fn default_dev_path() -> AppUrl {
AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap(),
))
}
fn default_dist_dir() -> AppUrl {
AppUrl::Url(WindowUrl::App("../dist".into()))
}
#[derive(Debug, PartialEq, Eq)]
struct PackageVersion(String);
impl<'d> serde::Deserialize<'d> for PackageVersion {
fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<PackageVersion, D::Error> {
struct PackageVersionVisitor;
impl<'d> Visitor<'d> for PackageVersionVisitor {
type Value = PackageVersion;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"a semver string or a path to a package.json file"
)
}
fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> {
let path = PathBuf::from(value);
if path.exists() {
let json_str = read_to_string(&path)
.map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
let package_json: serde_json::Value = serde_json::from_str(&json_str)
.map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?;
if let Some(obj) = package_json.as_object() {
let version = obj
.get("version")
.ok_or_else(|| DeError::custom("JSON must contain a `version` field"))?
.as_str()
.ok_or_else(|| {
DeError::custom(format!("`{} > version` must be a string", path.display()))
})?;
Ok(PackageVersion(
Version::from_str(version)
.map_err(|_| DeError::custom("`package > version` must be a semver string"))?
.to_string(),
))
} else {
Err(DeError::custom(
"`package > version` value is not a path to a JSON object",
))
}
} else {
Ok(PackageVersion(
Version::from_str(value)
.map_err(|_| DeError::custom("`package > version` must be a semver string"))?
.to_string(),
))
}
}
}
deserializer.deserialize_string(PackageVersionVisitor {})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PackageConfig {
#[serde(alias = "product-name")]
#[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))]
pub product_name: Option<String>,
#[serde(deserialize_with = "version_deserializer", default)]
pub version: Option<String>,
}
fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0))
}
impl PackageConfig {
#[allow(dead_code)]
pub fn binary_name(&self) -> Option<String> {
#[cfg(target_os = "linux")]
{
self.product_name.as_ref().map(|n| n.to_kebab_case())
}
#[cfg(not(target_os = "linux"))]
{
self.product_name.clone()
}
}
}
#[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Config {
#[serde(rename = "$schema")]
pub schema: Option<String>,
#[serde(default)]
pub package: PackageConfig,
#[serde(default)]
pub tauri: TauriConfig,
#[serde(default = "default_build")]
pub build: BuildConfig,
#[serde(default)]
pub plugins: PluginConfig,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct PluginConfig(pub HashMap<String, JsonValue>);
fn default_build() -> BuildConfig {
BuildConfig {
runner: None,
dev_path: default_dev_path(),
dist_dir: default_dist_dir(),
before_dev_command: None,
before_build_command: None,
before_bundle_command: None,
features: None,
with_global_tauri: false,
}
}
#[cfg(feature = "build")]
mod build {
use std::{convert::identity, path::Path};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens, TokenStreamExt};
use super::*;
use serde_json::Value as JsonValue;
fn str_lit(s: impl AsRef<str>) -> TokenStream {
let s = s.as_ref();
quote! { #s.into() }
}
fn opt_lit(item: Option<&impl ToTokens>) -> TokenStream {
match item {
None => quote! { ::core::option::Option::None },
Some(item) => quote! { ::core::option::Option::Some(#item) },
}
}
fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
opt_lit(item.map(str_lit).as_ref())
}
fn opt_vec_str_lit(item: Option<impl IntoIterator<Item = impl AsRef<str>>>) -> TokenStream {
opt_lit(item.map(|list| vec_lit(list, str_lit)).as_ref())
}
fn vec_lit<Raw, Tokens>(
list: impl IntoIterator<Item = Raw>,
map: impl Fn(Raw) -> Tokens,
) -> TokenStream
where
Tokens: ToTokens,
{
let items = list.into_iter().map(map);
quote! { vec![#(#items),*] }
}
fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
let s = s.as_ref().to_string_lossy().into_owned();
quote! { ::std::path::PathBuf::from(#s) }
}
fn url_lit(url: &Url) -> TokenStream {
let url = url.as_str();
quote! { #url.parse().unwrap() }
}
fn map_lit<Map, Key, Value, TokenStreamKey, TokenStreamValue, FuncKey, FuncValue>(
map_type: TokenStream,
map: Map,
map_key: FuncKey,
map_value: FuncValue,
) -> TokenStream
where
<Map as IntoIterator>::IntoIter: ExactSizeIterator,
Map: IntoIterator<Item = (Key, Value)>,
TokenStreamKey: ToTokens,
TokenStreamValue: ToTokens,
FuncKey: Fn(Key) -> TokenStreamKey,
FuncValue: Fn(Value) -> TokenStreamValue,
{
let ident = quote::format_ident!("map");
let map = map.into_iter();
if map.len() > 0 {
let items = map.map(|(key, value)| {
let key = map_key(key);
let value = map_value(value);
quote! { #ident.insert(#key, #value); }
});
quote! {{
let mut #ident = #map_type::new();
#(#items)*
#ident
}}
} else {
quote! { #map_type::new() }
}
}
fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
let prefix = quote! { ::serde_json::Value };
if num.is_u64() {
let num = num.as_u64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else if num.is_i64() {
let num = num.as_i64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else if num.is_f64() {
let num = num.as_f64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else {
quote! { #prefix::Null }
}
}
fn json_value_lit(jv: &JsonValue) -> TokenStream {
let prefix = quote! { ::serde_json::Value };
match jv {
JsonValue::Null => quote! { #prefix::Null },
JsonValue::Bool(bool) => quote! { #prefix::Bool(#bool) },
JsonValue::Number(number) => json_value_number_lit(number),
JsonValue::String(str) => {
let s = str_lit(str);
quote! { #prefix::String(#s) }
}
JsonValue::Array(vec) => {
let items = vec.iter().map(json_value_lit);
quote! { #prefix::Array(vec![#(#items),*]) }
}
JsonValue::Object(map) => {
let map = map_lit(quote! { ::serde_json::Map }, map, str_lit, json_value_lit);
quote! { #prefix::Object(#map) }
}
}
}
macro_rules! literal_struct {
($tokens:ident, $struct:ident, $($field:ident),+) => {
$tokens.append_all(quote! {
::tauri::utils::config::$struct {
$($field: #$field),+
}
});
};
}
impl ToTokens for WindowUrl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::WindowUrl };
tokens.append_all(match self {
Self::App(path) => {
let path = path_buf_lit(path);
quote! { #prefix::App(#path) }
}
Self::External(url) => {
let url = url_lit(url);
quote! { #prefix::External(#url) }
}
})
}
}
impl ToTokens for crate::Theme {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::Theme };
tokens.append_all(match self {
Self::Light => quote! { #prefix::Light },
Self::Dark => quote! { #prefix::Dark },
})
}
}
impl ToTokens for crate::TitleBarStyle {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::TitleBarStyle };
tokens.append_all(match self {
Self::Visible => quote! { #prefix::Visible },
Self::Transparent => quote! { #prefix::Transparent },
Self::Overlay => quote! { #prefix::Overlay },
})
}
}
impl ToTokens for WindowConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let label = str_lit(&self.label);
let url = &self.url;
let user_agent = opt_str_lit(self.user_agent.as_ref());
let file_drop_enabled = self.file_drop_enabled;
let center = self.center;
let x = opt_lit(self.x.as_ref());
let y = opt_lit(self.y.as_ref());
let width = self.width;
let height = self.height;
let min_width = opt_lit(self.min_width.as_ref());
let min_height = opt_lit(self.min_height.as_ref());
let max_width = opt_lit(self.max_width.as_ref());
let max_height = opt_lit(self.max_height.as_ref());
let resizable = self.resizable;
let title = str_lit(&self.title);
let fullscreen = self.fullscreen;
let focus = self.focus;
let transparent = self.transparent;
let maximized = self.maximized;
let visible = self.visible;
let decorations = self.decorations;
let always_on_top = self.always_on_top;
let content_protected = self.content_protected;
let skip_taskbar = self.skip_taskbar;
let theme = opt_lit(self.theme.as_ref());
let title_bar_style = &self.title_bar_style;
let hidden_title = self.hidden_title;
let accept_first_mouse = self.accept_first_mouse;
let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
let shadow = self.shadow;
literal_struct!(
tokens,
WindowConfig,
label,
url,
user_agent,
file_drop_enabled,
center,
x,
y,
width,
height,
min_width,
min_height,
max_width,
max_height,
resizable,
title,
fullscreen,
focus,
transparent,
maximized,
visible,
decorations,
always_on_top,
content_protected,
skip_taskbar,
theme,
title_bar_style,
hidden_title,
accept_first_mouse,
tabbing_identifier,
additional_browser_args,
shadow
);
}
}
impl ToTokens for CliArg {
fn to_tokens(&self, tokens: &mut TokenStream) {
let short = opt_lit(self.short.as_ref());
let name = str_lit(&self.name);
let description = opt_str_lit(self.description.as_ref());
let long_description = opt_str_lit(self.long_description.as_ref());
let takes_value = self.takes_value;
let multiple = self.multiple;
let multiple_occurrences = self.multiple_occurrences;
let number_of_values = opt_lit(self.number_of_values.as_ref());
let possible_values = opt_vec_str_lit(self.possible_values.as_ref());
let min_values = opt_lit(self.min_values.as_ref());
let max_values = opt_lit(self.max_values.as_ref());
let required = self.required;
let required_unless_present = opt_str_lit(self.required_unless_present.as_ref());
let required_unless_present_all = opt_vec_str_lit(self.required_unless_present_all.as_ref());
let required_unless_present_any = opt_vec_str_lit(self.required_unless_present_any.as_ref());
let conflicts_with = opt_str_lit(self.conflicts_with.as_ref());
let conflicts_with_all = opt_vec_str_lit(self.conflicts_with_all.as_ref());
let requires = opt_str_lit(self.requires.as_ref());
let requires_all = opt_vec_str_lit(self.requires_all.as_ref());
let requires_if = opt_vec_str_lit(self.requires_if.as_ref());
let required_if_eq = opt_vec_str_lit(self.required_if_eq.as_ref());
let require_equals = opt_lit(self.require_equals.as_ref());
let index = opt_lit(self.index.as_ref());
literal_struct!(
tokens,
CliArg,
short,
name,
description,
long_description,
takes_value,
multiple,
multiple_occurrences,
number_of_values,
possible_values,
min_values,
max_values,
required,
required_unless_present,
required_unless_present_all,
required_unless_present_any,
conflicts_with,
conflicts_with_all,
requires,
requires_all,
requires_if,
required_if_eq,
require_equals,
index
);
}
}
impl ToTokens for CliConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let description = opt_str_lit(self.description.as_ref());
let long_description = opt_str_lit(self.long_description.as_ref());
let before_help = opt_str_lit(self.before_help.as_ref());
let after_help = opt_str_lit(self.after_help.as_ref());
let args = {
let args = self.args.as_ref().map(|args| {
let arg = args.iter().map(|a| quote! { #a });
quote! { vec![#(#arg),*] }
});
opt_lit(args.as_ref())
};
let subcommands = opt_lit(
self
.subcommands
.as_ref()
.map(|map| {
map_lit(
quote! { ::std::collections::HashMap },
map,
str_lit,
identity,
)
})
.as_ref(),
);
literal_struct!(
tokens,
CliConfig,
description,
long_description,
before_help,
after_help,
args,
subcommands
);
}
}
impl ToTokens for PatternKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::PatternKind };
tokens.append_all(match self {
Self::Brownfield => quote! { #prefix::Brownfield },
#[cfg(not(feature = "isolation"))]
Self::Isolation { dir: _ } => quote! { #prefix::Brownfield },
#[cfg(feature = "isolation")]
Self::Isolation { dir } => {
let dir = path_buf_lit(dir);
quote! { #prefix::Isolation { dir: #dir } }
}
})
}
}
impl ToTokens for WebviewInstallMode {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
tokens.append_all(match self {
Self::Skip => quote! { #prefix::Skip },
Self::DownloadBootstrapper { silent } => {
quote! { #prefix::DownloadBootstrapper { silent: #silent } }
}
Self::EmbedBootstrapper { silent } => {
quote! { #prefix::EmbedBootstrapper { silent: #silent } }
}
Self::OfflineInstaller { silent } => {
quote! { #prefix::OfflineInstaller { silent: #silent } }
}
Self::FixedRuntime { path } => {
let path = path_buf_lit(path);
quote! { #prefix::FixedRuntime { path: #path } }
}
})
}
}
impl ToTokens for WindowsConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let webview_install_mode = if let Some(fixed_runtime_path) = &self.webview_fixed_runtime_path
{
WebviewInstallMode::FixedRuntime {
path: fixed_runtime_path.clone(),
}
} else {
self.webview_install_mode.clone()
};
tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
webview_install_mode: #webview_install_mode,
..Default::default()
}})
}
}
impl ToTokens for BundleConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let identifier = str_lit(&self.identifier);
let publisher = quote!(None);
let icon = vec_lit(&self.icon, str_lit);
let active = self.active;
let targets = quote!(Default::default());
let resources = quote!(None);
let copyright = quote!(None);
let category = quote!(None);
let short_description = quote!(None);
let long_description = quote!(None);
let appimage = quote!(Default::default());
let deb = quote!(Default::default());
let macos = quote!(Default::default());
let external_bin = opt_vec_str_lit(self.external_bin.as_ref());
let windows = &self.windows;
let ios = quote!(Default::default());
let android = quote!(Default::default());
literal_struct!(
tokens,
BundleConfig,
active,
identifier,
publisher,
icon,
targets,
resources,
copyright,
category,
short_description,
long_description,
appimage,
deb,
macos,
external_bin,
windows,
ios,
android
);
}
}
impl ToTokens for AppUrl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::AppUrl };
tokens.append_all(match self {
Self::Url(url) => {
quote! { #prefix::Url(#url) }
}
Self::Files(files) => {
let files = vec_lit(files, path_buf_lit);
quote! { #prefix::Files(#files) }
}
})
}
}
impl ToTokens for BuildConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let dev_path = &self.dev_path;
let dist_dir = &self.dist_dir;
let with_global_tauri = self.with_global_tauri;
let runner = quote!(None);
let before_dev_command = quote!(None);
let before_build_command = quote!(None);
let before_bundle_command = quote!(None);
let features = quote!(None);
literal_struct!(
tokens,
BuildConfig,
runner,
dev_path,
dist_dir,
with_global_tauri,
before_dev_command,
before_build_command,
before_bundle_command,
features
);
}
}
impl ToTokens for WindowsUpdateInstallMode {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::WindowsUpdateInstallMode };
tokens.append_all(match self {
Self::BasicUi => quote! { #prefix::BasicUi },
Self::Quiet => quote! { #prefix::Quiet },
Self::Passive => quote! { #prefix::Passive },
})
}
}
impl ToTokens for UpdaterWindowsConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let install_mode = &self.install_mode;
let installer_args = vec_lit(&self.installer_args, str_lit);
literal_struct!(tokens, UpdaterWindowsConfig, install_mode, installer_args);
}
}
impl ToTokens for UpdaterConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let active = self.active;
let dialog = self.dialog;
let pubkey = str_lit(&self.pubkey);
let endpoints = opt_lit(
self
.endpoints
.as_ref()
.map(|list| {
vec_lit(list, |url| {
let url = url.0.as_str();
quote! { ::tauri::utils::config::UpdaterEndpoint(#url.parse().unwrap()) }
})
})
.as_ref(),
);
let windows = &self.windows;
literal_struct!(
tokens,
UpdaterConfig,
active,
dialog,
pubkey,
endpoints,
windows
);
}
}
impl ToTokens for CspDirectiveSources {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::CspDirectiveSources };
tokens.append_all(match self {
Self::Inline(sources) => {
let sources = sources.as_str();
quote!(#prefix::Inline(#sources.into()))
}
Self::List(list) => {
let list = vec_lit(list, str_lit);
quote!(#prefix::List(#list))
}
})
}
}
impl ToTokens for Csp {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::Csp };
tokens.append_all(match self {
Self::Policy(policy) => {
let policy = policy.as_str();
quote!(#prefix::Policy(#policy.into()))
}
Self::DirectiveMap(list) => {
let map = map_lit(
quote! { ::std::collections::HashMap },
list,
str_lit,
identity,
);
quote!(#prefix::DirectiveMap(#map))
}
})
}
}
impl ToTokens for DisabledCspModificationKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::DisabledCspModificationKind };
tokens.append_all(match self {
Self::Flag(flag) => {
quote! { #prefix::Flag(#flag) }
}
Self::List(directives) => {
let directives = vec_lit(directives, str_lit);
quote! { #prefix::List(#directives) }
}
});
}
}
impl ToTokens for SecurityConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let csp = opt_lit(self.csp.as_ref());
let dev_csp = opt_lit(self.dev_csp.as_ref());
let freeze_prototype = self.freeze_prototype;
let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
literal_struct!(
tokens,
SecurityConfig,
csp,
dev_csp,
freeze_prototype,
dangerous_disable_asset_csp_modification
);
}
}
impl ToTokens for SystemTrayConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let icon_as_template = self.icon_as_template;
let menu_on_left_click = self.menu_on_left_click;
let icon_path = path_buf_lit(&self.icon_path);
let title = opt_str_lit(self.title.as_ref());
literal_struct!(
tokens,
SystemTrayConfig,
icon_path,
icon_as_template,
menu_on_left_click,
title
);
}
}
impl ToTokens for FsAllowlistScope {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::FsAllowlistScope };
tokens.append_all(match self {
Self::AllowedPaths(allow) => {
let allowed_paths = vec_lit(allow, path_buf_lit);
quote! { #prefix::AllowedPaths(#allowed_paths) }
}
Self::Scope { allow, deny } => {
let allow = vec_lit(allow, path_buf_lit);
let deny = vec_lit(deny, path_buf_lit);
quote! { #prefix::Scope { allow: #allow, deny: #deny } }
}
});
}
}
impl ToTokens for FsAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scope = &self.scope;
tokens.append_all(quote! { ::tauri::utils::config::FsAllowlistConfig { scope: #scope, ..Default::default() } })
}
}
impl ToTokens for ProtocolAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let asset_scope = &self.asset_scope;
tokens.append_all(quote! { ::tauri::utils::config::ProtocolAllowlistConfig { asset_scope: #asset_scope, ..Default::default() } })
}
}
impl ToTokens for HttpAllowlistScope {
fn to_tokens(&self, tokens: &mut TokenStream) {
let allowed_urls = vec_lit(&self.0, url_lit);
tokens.append_all(quote! { ::tauri::utils::config::HttpAllowlistScope(#allowed_urls) })
}
}
impl ToTokens for HttpAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scope = &self.scope;
tokens.append_all(quote! { ::tauri::utils::config::HttpAllowlistConfig { scope: #scope, ..Default::default() } })
}
}
impl ToTokens for ShellAllowedCommand {
fn to_tokens(&self, tokens: &mut TokenStream) {
let name = str_lit(&self.name);
let command = path_buf_lit(&self.command);
let args = &self.args;
let sidecar = &self.sidecar;
literal_struct!(tokens, ShellAllowedCommand, name, command, args, sidecar);
}
}
impl ToTokens for ShellAllowedArgs {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::ShellAllowedArgs };
tokens.append_all(match self {
Self::Flag(flag) => quote!(#prefix::Flag(#flag)),
Self::List(list) => {
let list = vec_lit(list, identity);
quote!(#prefix::List(#list))
}
})
}
}
impl ToTokens for ShellAllowedArg {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::ShellAllowedArg };
tokens.append_all(match self {
Self::Fixed(fixed) => {
let fixed = str_lit(fixed);
quote!(#prefix::Fixed(#fixed))
}
Self::Var { validator } => {
let validator = str_lit(validator);
quote!(#prefix::Var { validator: #validator })
}
})
}
}
impl ToTokens for ShellAllowlistOpen {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::ShellAllowlistOpen };
tokens.append_all(match self {
Self::Flag(flag) => quote!(#prefix::Flag(#flag)),
Self::Validate(regex) => quote!(#prefix::Validate(#regex)),
})
}
}
impl ToTokens for ShellAllowlistScope {
fn to_tokens(&self, tokens: &mut TokenStream) {
let allowed_commands = vec_lit(&self.0, identity);
tokens.append_all(quote! { ::tauri::utils::config::ShellAllowlistScope(#allowed_commands) })
}
}
impl ToTokens for ShellAllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scope = &self.scope;
tokens.append_all(quote! { ::tauri::utils::config::ShellAllowlistConfig { scope: #scope, ..Default::default() } })
}
}
impl ToTokens for AllowlistConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let fs = &self.fs;
let protocol = &self.protocol;
let http = &self.http;
let shell = &self.shell;
tokens.append_all(
quote! { ::tauri::utils::config::AllowlistConfig { fs: #fs, protocol: #protocol, http: #http, shell: #shell, ..Default::default() } },
)
}
}
impl ToTokens for TauriConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let pattern = &self.pattern;
let windows = vec_lit(&self.windows, identity);
let cli = opt_lit(self.cli.as_ref());
let bundle = &self.bundle;
let updater = &self.updater;
let security = &self.security;
let system_tray = opt_lit(self.system_tray.as_ref());
let allowlist = &self.allowlist;
let macos_private_api = self.macos_private_api;
literal_struct!(
tokens,
TauriConfig,
pattern,
windows,
cli,
bundle,
updater,
security,
system_tray,
allowlist,
macos_private_api
);
}
}
impl ToTokens for PluginConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let config = map_lit(
quote! { ::std::collections::HashMap },
&self.0,
str_lit,
json_value_lit,
);
tokens.append_all(quote! { ::tauri::utils::config::PluginConfig(#config) })
}
}
impl ToTokens for PackageConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let product_name = opt_str_lit(self.product_name.as_ref());
let version = opt_str_lit(self.version.as_ref());
literal_struct!(tokens, PackageConfig, product_name, version);
}
}
impl ToTokens for Config {
fn to_tokens(&self, tokens: &mut TokenStream) {
let schema = quote!(None);
let package = &self.package;
let tauri = &self.tauri;
let build = &self.build;
let plugins = &self.plugins;
literal_struct!(tokens, Config, schema, package, tauri, build, plugins);
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_defaults() {
let t_config = TauriConfig::default();
let b_config = BuildConfig::default();
let d_path = default_dev_path();
let d_windows: Vec<WindowConfig> = vec![];
let d_bundle = BundleConfig::default();
let d_updater = UpdaterConfig::default();
let tauri = TauriConfig {
pattern: Default::default(),
windows: vec![],
bundle: BundleConfig {
active: false,
targets: Default::default(),
identifier: String::from(""),
publisher: None,
icon: Vec::new(),
resources: None,
copyright: None,
category: None,
short_description: None,
long_description: None,
appimage: Default::default(),
deb: Default::default(),
macos: Default::default(),
external_bin: None,
windows: Default::default(),
ios: Default::default(),
android: Default::default(),
},
cli: None,
updater: UpdaterConfig {
active: false,
dialog: true,
pubkey: "".into(),
endpoints: None,
windows: Default::default(),
},
security: SecurityConfig {
csp: None,
dev_csp: None,
freeze_prototype: false,
dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
},
allowlist: AllowlistConfig::default(),
system_tray: None,
macos_private_api: false,
};
let build = BuildConfig {
runner: None,
dev_path: AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap(),
)),
dist_dir: AppUrl::Url(WindowUrl::App("../dist".into())),
before_dev_command: None,
before_build_command: None,
before_bundle_command: None,
features: None,
with_global_tauri: false,
};
assert_eq!(t_config, tauri);
assert_eq!(b_config, build);
assert_eq!(d_bundle, tauri.bundle);
assert_eq!(d_updater, tauri.updater);
assert_eq!(
d_path,
AppUrl::Url(WindowUrl::External(
Url::parse("http://localhost:8080").unwrap()
))
);
assert_eq!(d_windows, tauri.windows);
}
}