From c23f139ba86628fe0217a966bc8676afe7202a05 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 24 Apr 2022 10:08:05 -0700 Subject: [PATCH 1/7] perf(core): improve binary size with api enum serde refactor (#3952) --- .changes/binary-size-perf.md | 5 + Cargo.toml | 1 + core/tauri-macros/src/command_module.rs | 203 ++++++++++++++----- core/tauri-macros/src/lib.rs | 14 +- core/tauri/build.rs | 2 + core/tauri/scripts/bundle.js | 2 +- core/tauri/src/endpoints.rs | 1 - core/tauri/src/endpoints/app.rs | 3 +- core/tauri/src/endpoints/cli.rs | 18 +- core/tauri/src/endpoints/clipboard.rs | 27 ++- core/tauri/src/endpoints/dialog.rs | 32 +-- core/tauri/src/endpoints/event.rs | 5 +- core/tauri/src/endpoints/file_system.rs | 32 ++- core/tauri/src/endpoints/global_shortcut.rs | 26 ++- core/tauri/src/endpoints/http.rs | 19 +- core/tauri/src/endpoints/notification.rs | 8 +- core/tauri/src/endpoints/operating_system.rs | 45 +++- core/tauri/src/endpoints/path.rs | 58 +++--- core/tauri/src/endpoints/process.rs | 17 +- core/tauri/src/endpoints/shell.rs | 44 ++-- core/tauri/src/endpoints/window.rs | 12 +- core/tauri/src/error.rs | 6 +- tooling/api/src/clipboard.ts | 5 +- 23 files changed, 388 insertions(+), 197 deletions(-) create mode 100644 .changes/binary-size-perf.md diff --git a/.changes/binary-size-perf.md b/.changes/binary-size-perf.md new file mode 100644 index 000000000..ae94d94a3 --- /dev/null +++ b/.changes/binary-size-perf.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Reduce the amount of generated code for the API endpoints. diff --git a/Cargo.toml b/Cargo.toml index 338e785b7..318929490 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ exclude = [ # default to small, optimized workspace release binaries [profile.release] +strip = true panic = "abort" codegen-units = 1 lto = true diff --git a/core/tauri-macros/src/command_module.rs b/core/tauri-macros/src/command_module.rs index b28887058..1203b351e 100644 --- a/core/tauri-macros/src/command_module.rs +++ b/core/tauri-macros/src/command_module.rs @@ -2,35 +2,115 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use heck::ToSnakeCase; +use heck::{ToLowerCamelCase, ToSnakeCase}; use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree}; +use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote, quote_spanned}; use syn::{ parse::{Parse, ParseStream}, + parse_quote, spanned::Spanned, - Data, DeriveInput, Error, Fields, FnArg, Ident, ItemFn, LitStr, Pat, Token, + Data, DeriveInput, Error, Fields, Ident, ItemFn, LitStr, Token, }; -pub fn generate_run_fn(input: DeriveInput) -> TokenStream { +pub(crate) fn generate_command_enum(mut input: DeriveInput) -> TokenStream { + let mut deserialize_functions = TokenStream2::new(); + let mut errors = TokenStream2::new(); + + input.attrs.push(parse_quote!(#[allow(dead_code)])); + + match &mut input.data { + Data::Enum(data_enum) => { + for variant in &mut data_enum.variants { + let mut feature: Option = None; + let mut error_message: Option = None; + + for attr in &variant.attrs { + if attr.path.is_ident("cmd") { + let r = attr + .parse_args_with(|input: ParseStream| { + if let Ok(f) = input.parse::() { + feature.replace(f); + input.parse::()?; + let error_message_raw: LitStr = input.parse()?; + error_message.replace(error_message_raw.value()); + } + Ok(quote!()) + }) + .unwrap_or_else(syn::Error::into_compile_error); + errors.extend(r); + } + } + + if let Some(f) = feature { + let error_message = if let Some(e) = error_message { + let e = e.to_string(); + quote!(#e) + } else { + quote!("This API is not enabled in the allowlist.") + }; + + let deserialize_function_name = quote::format_ident!("__{}_deserializer", variant.ident); + deserialize_functions.extend(quote! { + #[cfg(not(#f))] + #[allow(non_snake_case)] + fn #deserialize_function_name<'de, D, T>(deserializer: D) -> ::std::result::Result + where + D: ::serde::de::Deserializer<'de>, + { + ::std::result::Result::Err(::serde::de::Error::custom(crate::Error::ApiNotAllowlisted(#error_message.into()).to_string())) + } + }); + + let deserialize_function_name = deserialize_function_name.to_string(); + + variant + .attrs + .push(parse_quote!(#[cfg_attr(not(#f), serde(deserialize_with = #deserialize_function_name))])); + } + } + } + _ => { + return Error::new( + Span::call_site(), + "`command_enum` is only implemented for enums", + ) + .to_compile_error() + .into() + } + }; + + TokenStream::from(quote! { + #errors + #input + #deserialize_functions + }) +} + +pub(crate) fn generate_run_fn(input: DeriveInput) -> TokenStream { let name = &input.ident; let data = &input.data; + let mut errors = TokenStream2::new(); + let mut is_async = false; + let attrs = input.attrs; for attr in attrs { if attr.path.is_ident("cmd") { - let _ = attr.parse_args_with(|input: ParseStream| { - while let Some(token) = input.parse()? { - if let TokenTree::Ident(ident) = token { - is_async |= ident == "async"; + let r = attr + .parse_args_with(|input: ParseStream| { + if let Ok(token) = input.parse::() { + is_async = token == "async"; } - } - Ok(()) - }); + Ok(quote!()) + }) + .unwrap_or_else(syn::Error::into_compile_error); + errors.extend(r); } } + let maybe_await = if is_async { quote!(.await) } else { quote!() }; let maybe_async = if is_async { quote!(async) } else { quote!() }; @@ -43,6 +123,30 @@ pub fn generate_run_fn(input: DeriveInput) -> TokenStream { for variant in &data_enum.variants { let variant_name = &variant.ident; + let mut feature = None; + + for attr in &variant.attrs { + if attr.path.is_ident("cmd") { + let r = attr + .parse_args_with(|input: ParseStream| { + if let Ok(f) = input.parse::() { + feature.replace(f); + input.parse::()?; + let _: LitStr = input.parse()?; + } + Ok(quote!()) + }) + .unwrap_or_else(syn::Error::into_compile_error); + errors.extend(r); + } + } + + let maybe_feature_check = if let Some(f) = feature { + quote!(#[cfg(#f)]) + } else { + quote!() + }; + let (fields_in_variant, variables) = match &variant.fields { Fields::Unit => (quote_spanned! { variant.span() => }, quote!()), Fields::Unnamed(fields) => { @@ -73,9 +177,13 @@ pub fn generate_run_fn(input: DeriveInput) -> TokenStream { variant_execute_function_name.set_span(variant_name.span()); matcher.extend(quote_spanned! { - variant.span() => #name::#variant_name #fields_in_variant => #name::#variant_execute_function_name(context, #variables)#maybe_await.map(Into::into), + variant.span() => #maybe_feature_check #name::#variant_name #fields_in_variant => #name::#variant_execute_function_name(context, #variables)#maybe_await.map(Into::into), }); } + + matcher.extend(quote! { + _ => Err(crate::error::into_anyhow("API not in the allowlist (https://tauri.studio/docs/api/config#tauri.allowlist)")), + }); } _ => { return Error::new( @@ -90,7 +198,8 @@ pub fn generate_run_fn(input: DeriveInput) -> TokenStream { let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let expanded = quote! { - impl #impl_generics #name #ty_generics #where_clause { + #errors + impl #impl_generics #name #ty_generics #where_clause { pub #maybe_async fn run(self, context: crate::endpoints::InvokeContext) -> super::Result { match self { #matcher @@ -105,26 +214,25 @@ pub fn generate_run_fn(input: DeriveInput) -> TokenStream { /// Attributes for the module enum variant handler. pub struct HandlerAttributes { allowlist: Ident, - error_message: String, } impl Parse for HandlerAttributes { fn parse(input: ParseStream) -> syn::Result { - let allowlist = input.parse()?; - input.parse::()?; - let raw: LitStr = input.parse()?; - let error_message = raw.value(); Ok(Self { - allowlist, - error_message, + allowlist: input.parse()?, }) } } +pub enum AllowlistCheckKind { + Runtime, + Serde, +} + pub struct HandlerTestAttributes { allowlist: Ident, error_message: String, - is_async: bool, + allowlist_check_kind: AllowlistCheckKind, } impl Parse for HandlerTestAttributes { @@ -133,60 +241,48 @@ impl Parse for HandlerTestAttributes { input.parse::()?; let error_message_raw: LitStr = input.parse()?; let error_message = error_message_raw.value(); - let _ = input.parse::(); - let is_async = input - .parse::() - .map(|i| i == "async") - .unwrap_or_default(); + let allowlist_check_kind = + if let (Ok(_), Ok(i)) = (input.parse::(), input.parse::()) { + if i == "runtime" { + AllowlistCheckKind::Runtime + } else { + AllowlistCheckKind::Serde + } + } else { + AllowlistCheckKind::Serde + }; Ok(Self { allowlist, error_message, - is_async, + allowlist_check_kind, }) } } pub fn command_handler(attributes: HandlerAttributes, function: ItemFn) -> TokenStream2 { let allowlist = attributes.allowlist; - let error_message = attributes.error_message.as_str(); - let signature = function.sig.clone(); quote!( #[cfg(#allowlist)] #function - - #[cfg(not(#allowlist))] - #[allow(unused_variables)] - #[allow(unused_mut)] - #signature { - Err(anyhow::anyhow!(crate::Error::ApiNotAllowlisted(#error_message.to_string()).to_string())) - } ) } pub fn command_test(attributes: HandlerTestAttributes, function: ItemFn) -> TokenStream2 { let allowlist = attributes.allowlist; - let is_async = attributes.is_async; let error_message = attributes.error_message.as_str(); let signature = function.sig.clone(); - let test_name = function.sig.ident.clone(); - let mut args = quote!(); - for arg in &function.sig.inputs { - if let FnArg::Typed(t) = arg { - if let Pat::Ident(i) = &*t.pat { - let ident = &i.ident; - args.extend(quote!(#ident,)) - } - } - } - let response = if is_async { - quote!(crate::async_runtime::block_on( - super::Cmd::#test_name(crate::test::mock_invoke_context(), #args) - )) - } else { - quote!(super::Cmd::#test_name(crate::test::mock_invoke_context(), #args)) + let enum_variant_name = function.sig.ident.to_string().to_lower_camel_case(); + let response = match attributes.allowlist_check_kind { + AllowlistCheckKind::Runtime => { + let test_name = function.sig.ident.clone(); + quote!(super::Cmd::#test_name(crate::test::mock_invoke_context())) + } + AllowlistCheckKind::Serde => quote! { + serde_json::from_str::(&format!(r#"{{ "cmd": "{}", "data": null }}"#, #enum_variant_name)) + }, }; quote!( @@ -194,6 +290,7 @@ pub fn command_test(attributes: HandlerTestAttributes, function: ItemFn) -> Toke #function #[cfg(not(#allowlist))] + #[allow(unused_variables)] #[quickcheck_macros::quickcheck] #signature { if let Err(e) = #response { diff --git a/core/tauri-macros/src/lib.rs b/core/tauri-macros/src/lib.rs index 1adae76ac..3af378078 100644 --- a/core/tauri-macros/src/lib.rs +++ b/core/tauri-macros/src/lib.rs @@ -76,6 +76,14 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre runtime::default_runtime(attributes, input).into() } +/// Prepares the command module enum. +#[doc(hidden)] +#[proc_macro_derive(CommandModule, attributes(cmd))] +pub fn derive_command_module(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + command_module::generate_run_fn(input) +} + /// Adds a `run` method to an enum (one of the tauri endpoint modules). /// The `run` method takes a `tauri::endpoints::InvokeContext` /// and returns a `tauri::Result`. @@ -83,10 +91,10 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre /// passing the the context and the variant's fields as arguments. /// That function must also return the same `Result`. #[doc(hidden)] -#[proc_macro_derive(CommandModule, attributes(cmd))] -pub fn derive_command_module(input: TokenStream) -> TokenStream { +#[proc_macro_attribute] +pub fn command_enum(_: TokenStream, input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - command_module::generate_run_fn(input) + command_module::generate_command_enum(input) } #[doc(hidden)] diff --git a/core/tauri/build.rs b/core/tauri/build.rs index c1ffed5b1..e0a798284 100644 --- a/core/tauri/build.rs +++ b/core/tauri/build.rs @@ -60,6 +60,8 @@ fn main() { shell_execute: { any(shell_all, feature = "shell-execute") }, shell_sidecar: { any(shell_all, feature = "shell-sidecar") }, shell_open: { any(shell_all, feature = "shell-open") }, + // helper for the command module macro + shell_script: { any(shell_execute, shell_sidecar) }, // helper for the shell scope functionality shell_scope: { any(shell_execute, shell_sidecar, feature = "shell-open-api") }, diff --git a/core/tauri/scripts/bundle.js b/core/tauri/scripts/bundle.js index d3bef2111..a56de0c3d 100644 --- a/core/tauri/scripts/bundle.js +++ b/core/tauri/scripts/bundle.js @@ -1 +1 @@ -function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&_setPrototypeOf(e,t)}function _setPrototypeOf(e,t){return(_setPrototypeOf=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function _createSuper(e){var t=_isNativeReflectConstruct();return function(){var r,n=_getPrototypeOf(e);if(t){var a=_getPrototypeOf(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return _possibleConstructorReturn(this,r)}}function _possibleConstructorReturn(e,t){if(t&&("object"===_typeof(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return _assertThisInitialized(e)}function _assertThisInitialized(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function _isNativeReflectConstruct(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function _getPrototypeOf(e){return(_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function _createForOfIteratorHelper(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=_unsupportedIterableToArray(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,i=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,o=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw o}}}}function _unsupportedIterableToArray(e,t){if(e){if("string"==typeof e)return _arrayLikeToArray(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?_arrayLikeToArray(e,t):void 0}}function _arrayLikeToArray(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r=0;--o){var i=this.tryEntries[o],u=i.completion;if("root"===i.tryLoc)return a("end");if(i.tryLoc<=this.prev){var s=n.call(i,"catchLoc"),c=n.call(i,"finallyLoc");if(s&&c){if(this.prev=0;--r){var a=this.tryEntries[r];if(a.tryLoc<=this.prev&&n.call(a,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),P(r),d}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var a=n.arg;P(r)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,r,n){return this.delegate={iterator:O(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),d}},e}("object"===("undefined"==typeof module?"undefined":_typeof(module))?module.exports:{});try{regeneratorRuntime=t}catch(e){"object"===("undefined"==typeof globalThis?"undefined":_typeof(globalThis))?globalThis.regeneratorRuntime=t:Function("r","regeneratorRuntime = r")(t)}function r(e){for(var t=void 0,r=e[0],n=1;n1&&void 0!==arguments[1]&&arguments[1],a=n(),o="_".concat(a);return Object.defineProperty(window,o,{value:function(n){return t&&Reflect.deleteProperty(window,o),r([e,"optionalCall",function(e){return e(n)}])},writable:!1,configurable:!0}),a}function o(e){return i.apply(this,arguments)}function i(){return(i=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",new Promise((function(e,n){var o=a((function(t){e(t),Reflect.deleteProperty(window,i)}),!0),i=a((function(e){n(e),Reflect.deleteProperty(window,o)}),!0);window.__TAURI_IPC__(_objectSpread({cmd:t,callback:o,error:i},r))})));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var u=Object.freeze({__proto__:null,transformCallback:a,invoke:o,convertFileSrc:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"asset",r=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?"https://".concat(t,".localhost/").concat(r):"".concat(t,"://").concat(r)}});function s(e){return c.apply(this,arguments)}function c(){return(c=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",o("tauri",t));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function p(){return(p=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"App",message:{cmd:"getAppVersion"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function l(){return(l=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"App",message:{cmd:"getAppName"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function f(){return(f=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"App",message:{cmd:"getTauriVersion"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var h=Object.freeze({__proto__:null,getName:function(){return l.apply(this,arguments)},getVersion:function(){return p.apply(this,arguments)},getTauriVersion:function(){return f.apply(this,arguments)}});function m(){return(m=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Cli",message:{cmd:"cliMatches"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var d=Object.freeze({__proto__:null,getMatches:function(){return m.apply(this,arguments)}});function y(){return(y=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Clipboard",message:{cmd:"writeText",data:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function g(){return(g=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Clipboard",message:{cmd:"readText"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var _=Object.freeze({__proto__:null,writeText:function(e){return y.apply(this,arguments)},readText:function(){return g.apply(this,arguments)}});function w(){return(w=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(t=r.length>0&&void 0!==r[0]?r[0]:{})&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"openDialog",options:t}}));case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function v(){return(v=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(t=r.length>0&&void 0!==r[0]?r[0]:{})&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:t}}));case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function b(){return(b=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function R(){return(R=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"askDialog",title:r,message:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function k(){return(k=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"confirmDialog",title:r,message:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var x=Object.freeze({__proto__:null,open:function(){return w.apply(this,arguments)},save:function(){return v.apply(this,arguments)},message:function(e){return b.apply(this,arguments)},ask:function(e,t){return R.apply(this,arguments)},confirm:function(e,t){return k.apply(this,arguments)}});function T(e,t){return G.apply(this,arguments)}function G(){return(G=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Event",message:{cmd:"unlisten",event:t,eventId:r}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function P(e,t,r){return M.apply(this,arguments)}function M(){return(M=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,s({__tauriModule:"Event",message:{cmd:"emit",event:t,windowLabel:r,payload:"string"==typeof n?n:JSON.stringify(n)}});case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function O(e,t,r){return A.apply(this,arguments)}function A(){return(A=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Event",message:{cmd:"listen",event:t,windowLabel:r,handler:a(n)}}).then((function(e){return _asyncToGenerator(regeneratorRuntime.mark((function r(){return regeneratorRuntime.wrap((function(r){for(;;)switch(r.prev=r.next){case 0:return r.abrupt("return",T(t,e));case 1:case"end":return r.stop()}}),r)})))})));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function C(e,t,r){return j.apply(this,arguments)}function j(){return(j=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",O(t,r,(function(e){n(e),T(t,e.id).catch((function(){}))})));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function S(e,t){return D.apply(this,arguments)}function D(){return(D=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",O(t,null,r));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function L(e,t){return z.apply(this,arguments)}function z(){return(z=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",C(t,null,r));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function E(e,t){return W.apply(this,arguments)}function W(){return(W=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",P(t,void 0,r));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var F,I=Object.freeze({__proto__:null,listen:S,once:L,emit:E});function N(){return(N=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"readTextFile",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function U(){return(U=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=a.length>1&&void 0!==a[1]?a[1]:{},e.next=3,s({__tauriModule:"Fs",message:{cmd:"readFile",path:t,options:r}});case 3:return n=e.sent,e.abrupt("return",Uint8Array.from(n));case 5:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function H(){return(H=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(r=n.length>1&&void 0!==n[1]?n[1]:{})&&Object.freeze(r),"object"===_typeof(t)&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"writeFile",path:t.path,contents:Array.from((new TextEncoder).encode(t.contents)),options:r}}));case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function V(){return(V=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(r=n.length>1&&void 0!==n[1]?n[1]:{})&&Object.freeze(r),"object"===_typeof(t)&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"writeFile",path:t.path,contents:Array.from(t.contents),options:r}}));case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function q(){return(q=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"readDir",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function B(){return(B=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"createDir",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function J(){return(J=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"removeDir",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function K(){return(K=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=a.length>2&&void 0!==a[2]?a[2]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"copyFile",source:t,destination:r,options:n}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Y(){return(Y=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"removeFile",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function $(){return($=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=a.length>2&&void 0!==a[2]?a[2]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:t,newPath:r,options:n}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}!function(e){e[e.Audio=1]="Audio";e[e.Cache=2]="Cache";e[e.Config=3]="Config";e[e.Data=4]="Data";e[e.LocalData=5]="LocalData";e[e.Desktop=6]="Desktop";e[e.Document=7]="Document";e[e.Download=8]="Download";e[e.Executable=9]="Executable";e[e.Font=10]="Font";e[e.Home=11]="Home";e[e.Picture=12]="Picture";e[e.Public=13]="Public";e[e.Runtime=14]="Runtime";e[e.Template=15]="Template";e[e.Video=16]="Video";e[e.Resource=17]="Resource";e[e.App=18]="App";e[e.Log=19]="Log";e[e.Temp=20]="Temp"}(F||(F={}));var Q=Object.freeze({__proto__:null,get BaseDirectory(){return F},get Dir(){return F},readTextFile:function(e){return N.apply(this,arguments)},readBinaryFile:function(e){return U.apply(this,arguments)},writeFile:function(e){return H.apply(this,arguments)},writeBinaryFile:function(e){return V.apply(this,arguments)},readDir:function(e){return q.apply(this,arguments)},createDir:function(e){return B.apply(this,arguments)},removeDir:function(e){return J.apply(this,arguments)},copyFile:function(e,t){return K.apply(this,arguments)},removeFile:function(e){return Y.apply(this,arguments)},renameFile:function(e,t){return $.apply(this,arguments)}});function X(){return(X=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:t,handler:a(r)}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Z(){return(Z=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:t,handler:a(r)}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ee(){return(ee=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function te(){return(te=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function re(){return(re=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var ne,ae=Object.freeze({__proto__:null,register:function(e,t){return X.apply(this,arguments)},registerAll:function(e,t){return Z.apply(this,arguments)},isRegistered:function(e){return ee.apply(this,arguments)},unregister:function(e){return te.apply(this,arguments)},unregisterAll:function(){return re.apply(this,arguments)}});function oe(e,t){return null!=e?e:t()}function ie(e){for(var t=void 0,r=e[0],n=1;n=200&&this.status<300,this.headers=t.headers,this.rawHeaders=t.rawHeaders,this.data=t.data})),ce=function(){function e(t){_classCallCheck(this,e),this.id=t}var t,r,n,a,o,i,u;return _createClass(e,[{key:"drop",value:(u=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}}));case 1:case"end":return e.stop()}}),e,this)}))),function(){return u.apply(this,arguments)})},{key:"request",value:(i=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return(r=!t.responseType||t.responseType===ne.JSON)&&(t.responseType=ne.Text),e.abrupt("return",s({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:t}}).then((function(e){var t=new se(e);if(r){try{t.data=JSON.parse(t.data)}catch(e){if(t.ok&&""===t.data)t.data={};else if(t.ok)throw Error("Failed to parse response `".concat(t.data,"` as JSON: ").concat(e,";\n try setting the `responseType` option to `ResponseType.Text` or `ResponseType.Binary` if the API does not return a JSON response."))}return t}return t})));case 3:case"end":return e.stop()}}),e,this)}))),function(e){return i.apply(this,arguments)})},{key:"get",value:(o=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"GET",url:t},r)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t){return o.apply(this,arguments)})},{key:"post",value:(a=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"POST",url:t,body:r},n)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t,r){return a.apply(this,arguments)})},{key:"put",value:(n=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"PUT",url:t,body:r},n)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t,r){return n.apply(this,arguments)})},{key:"patch",value:(r=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"PATCH",url:t},r)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t){return r.apply(this,arguments)})},{key:"delete",value:(t=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"DELETE",url:t},r)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,r){return t.apply(this,arguments)})}]),e}();function pe(e){return le.apply(this,arguments)}function le(){return(le=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Http",message:{cmd:"createClient",options:t}}).then((function(e){return new ce(e)})));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var fe=null;function he(){return(he=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(null!==fe){e.next=4;break}return e.next=3,pe();case 3:fe=e.sent;case 4:return e.abrupt("return",fe.request(_objectSpread({url:t,method:oe(ie([r,"optionalAccess",function(e){return e.method}]),(function(){return"GET"}))},r)));case 5:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var me=Object.freeze({__proto__:null,getClient:pe,fetch:function(e,t){return he.apply(this,arguments)},Body:ue,Client:ce,Response:se,get ResponseType(){return ne}});function de(){return(de=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if("default"===window.Notification.permission){e.next=2;break}return e.abrupt("return",Promise.resolve("granted"===window.Notification.permission));case 2:return e.abrupt("return",s({__tauriModule:"Notification",message:{cmd:"isNotificationPermissionGranted"}}));case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ye(){return(ye=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",window.Notification.requestPermission());case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var ge=Object.freeze({__proto__:null,sendNotification:function(e){"string"==typeof e?new window.Notification(e):new window.Notification(e.title,e)},requestPermission:function(){return ye.apply(this,arguments)},isPermissionGranted:function(){return de.apply(this,arguments)}});function _e(){return navigator.appVersion.includes("Win")}function we(){return(we=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.App}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ve(){return(ve=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Audio}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function be(){return(be=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Cache}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Re(){return(Re=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Config}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ke(){return(ke=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Data}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function xe(){return(xe=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Desktop}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Te(){return(Te=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Document}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ge(){return(Ge=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Download}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Pe(){return(Pe=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Executable}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Me(){return(Me=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Font}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Oe(){return(Oe=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Home}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ae(){return(Ae=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.LocalData}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ce(){return(Ce=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Picture}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function je(){return(je=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Public}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Se(){return(Se=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Resource}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function De(){return(De=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Runtime}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Le(){return(Le=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Template}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ze(){return(ze=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Video}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ee(){return(Ee=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Log}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var We=_e()?"\\":"/",Fe=_e()?";":":";function Ie(){return(Ie=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r,n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:for(t=a.length,r=new Array(t),n=0;n0&&void 0!==r[0]?r[0]:0,e.abrupt("return",s({__tauriModule:"Process",message:{cmd:"exit",exitCode:t}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ye(){return(Ye=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Process",message:{cmd:"relaunch"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var $e=Object.freeze({__proto__:null,exit:function(){return Ke.apply(this,arguments)},relaunch:function(){return Ye.apply(this,arguments)}});function Qe(e,t){return null!=e?e:t()}function Xe(e,t){return Ze.apply(this,arguments)}function Ze(){return(Ze=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,o,i=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=i.length>2&&void 0!==i[2]?i[2]:[],o=i.length>3?i[3]:void 0,"object"===_typeof(n)&&Object.freeze(n),e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"execute",program:r,args:n,options:o,onEventFn:a(t)}}));case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var et=function(){function e(){_classCallCheck(this,e),e.prototype.__init.call(this)}return _createClass(e,[{key:"__init",value:function(){this.eventListeners=Object.create(null)}},{key:"addEventListener",value:function(e,t){e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t]}},{key:"_emit",value:function(e,t){if(e in this.eventListeners){var r,n=_createForOfIteratorHelper(this.eventListeners[e]);try{for(n.s();!(r=n.n()).done;){(0,r.value)(t)}}catch(e){n.e(e)}finally{n.f()}}}},{key:"on",value:function(e,t){return this.addEventListener(e,t),this}}]),e}(),tt=function(){function e(t){_classCallCheck(this,e),this.pid=t}var t,r;return _createClass(e,[{key:"write",value:(r=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:"string"==typeof t?t:Array.from(t)}}));case 1:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"kill",value:(t=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}}));case 1:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),rt=function(e){_inherits(a,e);var t,r,n=_createSuper(a);function a(e){var t,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],o=arguments.length>2?arguments[2]:void 0;return _classCallCheck(this,a),t=n.call(this),a.prototype.__init2.call(_assertThisInitialized(t)),a.prototype.__init3.call(_assertThisInitialized(t)),t.program=e,t.args="string"==typeof r?[r]:r,t.options=Qe(o,(function(){return{}})),t}return _createClass(a,[{key:"__init2",value:function(){this.stdout=new et}},{key:"__init3",value:function(){this.stderr=new et}},{key:"spawn",value:(r=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t=this;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",Xe((function(e){switch(e.event){case"Error":t._emit("error",e.payload);break;case"Terminated":t._emit("close",e.payload);break;case"Stdout":t.stdout._emit("data",e.payload);break;case"Stderr":t.stderr._emit("data",e.payload)}}),this.program,this.args,this.options).then((function(e){return new tt(e)})));case 1:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"execute",value:(t=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t=this;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",new Promise((function(e,r){t.on("error",r);var n=[],a=[];t.stdout.on("data",(function(e){n.push(e)})),t.stderr.on("data",(function(e){a.push(e)})),t.on("close",(function(t){e({code:t.code,signal:t.signal,stdout:n.join("\n"),stderr:a.join("\n")})})),t.spawn().catch(r)})));case 1:case"end":return e.stop()}}),e)}))),function(){return t.apply(this,arguments)})}],[{key:"sidecar",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],r=arguments.length>2?arguments[2]:void 0,n=new a(e,t,r);return n.options.sidecar=!0,n}}]),a}(et);function nt(){return(nt=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"open",path:t,with:r}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var at=Object.freeze({__proto__:null,Command:rt,Child:tt,EventEmitter:et,open:function(e,t){return nt.apply(this,arguments)}});function ot(e){for(var t=void 0,r=e[0],n=1;n1&&void 0!==arguments[1]?arguments[1]:{};return _classCallCheck(this,r),n=t.call(this,e),ct([a,"optionalAccess",function(e){return e.skip}])||s({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:_objectSpread({label:e},a)}}}).then(_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",n.emit("tauri://created"));case 1:case"end":return e.stop()}}),e)})))).catch(function(){var e=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",n.emit("tauri://error",t));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),n}return _createClass(r,null,[{key:"getByLabel",value:function(e){return dt().some((function(t){return t.label===e}))?new r(e,{skip:!0}):null}}]),r}(wt);function bt(){return(bt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Rt(){return(Rt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function kt(){return(kt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}"__TAURI_METADATA__"in window?yt=new vt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn('Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label.\nNote that this is not an issue if running this frontend on a browser instead of a Tauri window.'),yt=new vt("main",{skip:!0}));var xt=Object.freeze({__proto__:null,WebviewWindow:vt,WebviewWindowHandle:_t,WindowManager:wt,getCurrent:function(){return new vt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})},getAll:dt,get appWindow(){return yt},LogicalSize:lt,PhysicalSize:ft,LogicalPosition:ht,PhysicalPosition:mt,get UserAttentionType(){return pt},currentMonitor:function(){return bt.apply(this,arguments)},primaryMonitor:function(){return Rt.apply(this,arguments)},availableMonitors:function(){return kt.apply(this,arguments)}}),Tt=_e()?"\r\n":"\n";function Gt(){return(Gt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"platform"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Pt(){return(Pt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"version"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Mt(){return(Mt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"osType"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ot(){return(Ot=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"arch"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function At(){return(At=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"tempdir"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var Ct=Object.freeze({__proto__:null,EOL:Tt,platform:function(){return Gt.apply(this,arguments)},version:function(){return Pt.apply(this,arguments)},type:function(){return Mt.apply(this,arguments)},arch:function(){return Ot.apply(this,arguments)},tempdir:function(){return At.apply(this,arguments)}}),jt=o;e.app=h,e.cli=d,e.clipboard=_,e.dialog=x,e.event=I,e.fs=Q,e.globalShortcut=ae,e.http=me,e.invoke=jt,e.notification=ge,e.os=Ct,e.path=Je,e.process=$e,e.shell=at,e.tauri=u,e.updater=st,e.window=xt,Object.defineProperty(e,"__esModule",{value:!0})})); +function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&_setPrototypeOf(e,t)}function _setPrototypeOf(e,t){return(_setPrototypeOf=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function _createSuper(e){var t=_isNativeReflectConstruct();return function(){var r,n=_getPrototypeOf(e);if(t){var a=_getPrototypeOf(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return _possibleConstructorReturn(this,r)}}function _possibleConstructorReturn(e,t){if(t&&("object"===_typeof(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return _assertThisInitialized(e)}function _assertThisInitialized(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function _isNativeReflectConstruct(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function _getPrototypeOf(e){return(_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function _createForOfIteratorHelper(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=_unsupportedIterableToArray(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,i=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,o=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw o}}}}function _unsupportedIterableToArray(e,t){if(e){if("string"==typeof e)return _arrayLikeToArray(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?_arrayLikeToArray(e,t):void 0}}function _arrayLikeToArray(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r=0;--o){var i=this.tryEntries[o],u=i.completion;if("root"===i.tryLoc)return a("end");if(i.tryLoc<=this.prev){var s=n.call(i,"catchLoc"),c=n.call(i,"finallyLoc");if(s&&c){if(this.prev=0;--r){var a=this.tryEntries[r];if(a.tryLoc<=this.prev&&n.call(a,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),P(r),d}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var a=n.arg;P(r)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,r,n){return this.delegate={iterator:O(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),d}},e}("object"===("undefined"==typeof module?"undefined":_typeof(module))?module.exports:{});try{regeneratorRuntime=t}catch(e){"object"===("undefined"==typeof globalThis?"undefined":_typeof(globalThis))?globalThis.regeneratorRuntime=t:Function("r","regeneratorRuntime = r")(t)}function r(e){for(var t=void 0,r=e[0],n=1;n1&&void 0!==arguments[1]&&arguments[1],a=n(),o="_".concat(a);return Object.defineProperty(window,o,{value:function(n){return t&&Reflect.deleteProperty(window,o),r([e,"optionalCall",function(e){return e(n)}])},writable:!1,configurable:!0}),a}function o(e){return i.apply(this,arguments)}function i(){return(i=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",new Promise((function(e,n){var o=a((function(t){e(t),Reflect.deleteProperty(window,i)}),!0),i=a((function(e){n(e),Reflect.deleteProperty(window,o)}),!0);window.__TAURI_IPC__(_objectSpread({cmd:t,callback:o,error:i},r))})));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var u=Object.freeze({__proto__:null,transformCallback:a,invoke:o,convertFileSrc:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"asset",r=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?"https://".concat(t,".localhost/").concat(r):"".concat(t,"://").concat(r)}});function s(e){return c.apply(this,arguments)}function c(){return(c=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",o("tauri",t));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function p(){return(p=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"App",message:{cmd:"getAppVersion"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function l(){return(l=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"App",message:{cmd:"getAppName"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function f(){return(f=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"App",message:{cmd:"getTauriVersion"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var h=Object.freeze({__proto__:null,getName:function(){return l.apply(this,arguments)},getVersion:function(){return p.apply(this,arguments)},getTauriVersion:function(){return f.apply(this,arguments)}});function m(){return(m=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Cli",message:{cmd:"cliMatches"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var d=Object.freeze({__proto__:null,getMatches:function(){return m.apply(this,arguments)}});function y(){return(y=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Clipboard",message:{cmd:"writeText",data:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function g(){return(g=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var _=Object.freeze({__proto__:null,writeText:function(e){return y.apply(this,arguments)},readText:function(){return g.apply(this,arguments)}});function w(){return(w=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(t=r.length>0&&void 0!==r[0]?r[0]:{})&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"openDialog",options:t}}));case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function v(){return(v=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(t=r.length>0&&void 0!==r[0]?r[0]:{})&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:t}}));case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function b(){return(b=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function R(){return(R=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"askDialog",title:r,message:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function k(){return(k=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Dialog",message:{cmd:"confirmDialog",title:r,message:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var x=Object.freeze({__proto__:null,open:function(){return w.apply(this,arguments)},save:function(){return v.apply(this,arguments)},message:function(e){return b.apply(this,arguments)},ask:function(e,t){return R.apply(this,arguments)},confirm:function(e,t){return k.apply(this,arguments)}});function T(e,t){return G.apply(this,arguments)}function G(){return(G=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Event",message:{cmd:"unlisten",event:t,eventId:r}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function P(e,t,r){return M.apply(this,arguments)}function M(){return(M=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,s({__tauriModule:"Event",message:{cmd:"emit",event:t,windowLabel:r,payload:"string"==typeof n?n:JSON.stringify(n)}});case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function O(e,t,r){return A.apply(this,arguments)}function A(){return(A=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Event",message:{cmd:"listen",event:t,windowLabel:r,handler:a(n)}}).then((function(e){return _asyncToGenerator(regeneratorRuntime.mark((function r(){return regeneratorRuntime.wrap((function(r){for(;;)switch(r.prev=r.next){case 0:return r.abrupt("return",T(t,e));case 1:case"end":return r.stop()}}),r)})))})));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function C(e,t,r){return j.apply(this,arguments)}function j(){return(j=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",O(t,r,(function(e){n(e),T(t,e.id).catch((function(){}))})));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function S(e,t){return D.apply(this,arguments)}function D(){return(D=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",O(t,null,r));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function L(e,t){return z.apply(this,arguments)}function z(){return(z=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",C(t,null,r));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function E(e,t){return W.apply(this,arguments)}function W(){return(W=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",P(t,void 0,r));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var F,I=Object.freeze({__proto__:null,listen:S,once:L,emit:E});function N(){return(N=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"readTextFile",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function U(){return(U=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=a.length>1&&void 0!==a[1]?a[1]:{},e.next=3,s({__tauriModule:"Fs",message:{cmd:"readFile",path:t,options:r}});case 3:return n=e.sent,e.abrupt("return",Uint8Array.from(n));case 5:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function H(){return(H=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(r=n.length>1&&void 0!==n[1]?n[1]:{})&&Object.freeze(r),"object"===_typeof(t)&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"writeFile",path:t.path,contents:Array.from((new TextEncoder).encode(t.contents)),options:r}}));case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function V(){return(V=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(r=n.length>1&&void 0!==n[1]?n[1]:{})&&Object.freeze(r),"object"===_typeof(t)&&Object.freeze(t),e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"writeFile",path:t.path,contents:Array.from(t.contents),options:r}}));case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function q(){return(q=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"readDir",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function B(){return(B=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"createDir",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function J(){return(J=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"removeDir",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function K(){return(K=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=a.length>2&&void 0!==a[2]?a[2]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"copyFile",source:t,destination:r,options:n}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Y(){return(Y=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"removeFile",path:t,options:r}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function $(){return($=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=a.length>2&&void 0!==a[2]?a[2]:{},e.abrupt("return",s({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:t,newPath:r,options:n}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}!function(e){e[e.Audio=1]="Audio";e[e.Cache=2]="Cache";e[e.Config=3]="Config";e[e.Data=4]="Data";e[e.LocalData=5]="LocalData";e[e.Desktop=6]="Desktop";e[e.Document=7]="Document";e[e.Download=8]="Download";e[e.Executable=9]="Executable";e[e.Font=10]="Font";e[e.Home=11]="Home";e[e.Picture=12]="Picture";e[e.Public=13]="Public";e[e.Runtime=14]="Runtime";e[e.Template=15]="Template";e[e.Video=16]="Video";e[e.Resource=17]="Resource";e[e.App=18]="App";e[e.Log=19]="Log";e[e.Temp=20]="Temp"}(F||(F={}));var Q=Object.freeze({__proto__:null,get BaseDirectory(){return F},get Dir(){return F},readTextFile:function(e){return N.apply(this,arguments)},readBinaryFile:function(e){return U.apply(this,arguments)},writeFile:function(e){return H.apply(this,arguments)},writeBinaryFile:function(e){return V.apply(this,arguments)},readDir:function(e){return q.apply(this,arguments)},createDir:function(e){return B.apply(this,arguments)},removeDir:function(e){return J.apply(this,arguments)},copyFile:function(e,t){return K.apply(this,arguments)},removeFile:function(e){return Y.apply(this,arguments)},renameFile:function(e,t){return $.apply(this,arguments)}});function X(){return(X=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:t,handler:a(r)}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Z(){return(Z=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:t,handler:a(r)}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ee(){return(ee=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function te(){return(te=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:t}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function re(){return(re=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var ne,ae=Object.freeze({__proto__:null,register:function(e,t){return X.apply(this,arguments)},registerAll:function(e,t){return Z.apply(this,arguments)},isRegistered:function(e){return ee.apply(this,arguments)},unregister:function(e){return te.apply(this,arguments)},unregisterAll:function(){return re.apply(this,arguments)}});function oe(e,t){return null!=e?e:t()}function ie(e){for(var t=void 0,r=e[0],n=1;n=200&&this.status<300,this.headers=t.headers,this.rawHeaders=t.rawHeaders,this.data=t.data})),ce=function(){function e(t){_classCallCheck(this,e),this.id=t}var t,r,n,a,o,i,u;return _createClass(e,[{key:"drop",value:(u=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}}));case 1:case"end":return e.stop()}}),e,this)}))),function(){return u.apply(this,arguments)})},{key:"request",value:(i=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return(r=!t.responseType||t.responseType===ne.JSON)&&(t.responseType=ne.Text),e.abrupt("return",s({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:t}}).then((function(e){var t=new se(e);if(r){try{t.data=JSON.parse(t.data)}catch(e){if(t.ok&&""===t.data)t.data={};else if(t.ok)throw Error("Failed to parse response `".concat(t.data,"` as JSON: ").concat(e,";\n try setting the `responseType` option to `ResponseType.Text` or `ResponseType.Binary` if the API does not return a JSON response."))}return t}return t})));case 3:case"end":return e.stop()}}),e,this)}))),function(e){return i.apply(this,arguments)})},{key:"get",value:(o=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"GET",url:t},r)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t){return o.apply(this,arguments)})},{key:"post",value:(a=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"POST",url:t,body:r},n)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t,r){return a.apply(this,arguments)})},{key:"put",value:(n=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"PUT",url:t,body:r},n)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t,r){return n.apply(this,arguments)})},{key:"patch",value:(r=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"PATCH",url:t},r)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t){return r.apply(this,arguments)})},{key:"delete",value:(t=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.request(_objectSpread({method:"DELETE",url:t},r)));case 1:case"end":return e.stop()}}),e,this)}))),function(e,r){return t.apply(this,arguments)})}]),e}();function pe(e){return le.apply(this,arguments)}function le(){return(le=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Http",message:{cmd:"createClient",options:t}}).then((function(e){return new ce(e)})));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var fe=null;function he(){return(he=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(null!==fe){e.next=4;break}return e.next=3,pe();case 3:fe=e.sent;case 4:return e.abrupt("return",fe.request(_objectSpread({url:t,method:oe(ie([r,"optionalAccess",function(e){return e.method}]),(function(){return"GET"}))},r)));case 5:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var me=Object.freeze({__proto__:null,getClient:pe,fetch:function(e,t){return he.apply(this,arguments)},Body:ue,Client:ce,Response:se,get ResponseType(){return ne}});function de(){return(de=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if("default"===window.Notification.permission){e.next=2;break}return e.abrupt("return",Promise.resolve("granted"===window.Notification.permission));case 2:return e.abrupt("return",s({__tauriModule:"Notification",message:{cmd:"isNotificationPermissionGranted"}}));case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ye(){return(ye=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",window.Notification.requestPermission());case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var ge=Object.freeze({__proto__:null,sendNotification:function(e){"string"==typeof e?new window.Notification(e):new window.Notification(e.title,e)},requestPermission:function(){return ye.apply(this,arguments)},isPermissionGranted:function(){return de.apply(this,arguments)}});function _e(){return navigator.appVersion.includes("Win")}function we(){return(we=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.App}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ve(){return(ve=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Audio}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function be(){return(be=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Cache}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Re(){return(Re=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Config}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ke(){return(ke=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Data}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function xe(){return(xe=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Desktop}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Te(){return(Te=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Document}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ge(){return(Ge=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Download}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Pe(){return(Pe=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Executable}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Me(){return(Me=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Font}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Oe(){return(Oe=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Home}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ae(){return(Ae=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.LocalData}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ce(){return(Ce=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Picture}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function je(){return(je=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Public}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Se(){return(Se=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Resource}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function De(){return(De=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Runtime}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Le(){return(Le=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Template}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ze(){return(ze=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Video}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ee(){return(Ee=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:F.Log}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var We=_e()?"\\":"/",Fe=_e()?";":":";function Ie(){return(Ie=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r,n,a=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:for(t=a.length,r=new Array(t),n=0;n0&&void 0!==r[0]?r[0]:0,e.abrupt("return",s({__tauriModule:"Process",message:{cmd:"exit",exitCode:t}}));case 2:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ye(){return(Ye=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Process",message:{cmd:"relaunch"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var $e=Object.freeze({__proto__:null,exit:function(){return Ke.apply(this,arguments)},relaunch:function(){return Ye.apply(this,arguments)}});function Qe(e,t){return null!=e?e:t()}function Xe(e,t){return Ze.apply(this,arguments)}function Ze(){return(Ze=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,o,i=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=i.length>2&&void 0!==i[2]?i[2]:[],o=i.length>3?i[3]:void 0,"object"===_typeof(n)&&Object.freeze(n),e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"execute",program:r,args:n,options:o,onEventFn:a(t)}}));case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var et=function(){function e(){_classCallCheck(this,e),e.prototype.__init.call(this)}return _createClass(e,[{key:"__init",value:function(){this.eventListeners=Object.create(null)}},{key:"addEventListener",value:function(e,t){e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t]}},{key:"_emit",value:function(e,t){if(e in this.eventListeners){var r,n=_createForOfIteratorHelper(this.eventListeners[e]);try{for(n.s();!(r=n.n()).done;){(0,r.value)(t)}}catch(e){n.e(e)}finally{n.f()}}}},{key:"on",value:function(e,t){return this.addEventListener(e,t),this}}]),e}(),tt=function(){function e(t){_classCallCheck(this,e),this.pid=t}var t,r;return _createClass(e,[{key:"write",value:(r=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:"string"==typeof t?t:Array.from(t)}}));case 1:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"kill",value:(t=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}}));case 1:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),rt=function(e){_inherits(a,e);var t,r,n=_createSuper(a);function a(e){var t,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],o=arguments.length>2?arguments[2]:void 0;return _classCallCheck(this,a),t=n.call(this),a.prototype.__init2.call(_assertThisInitialized(t)),a.prototype.__init3.call(_assertThisInitialized(t)),t.program=e,t.args="string"==typeof r?[r]:r,t.options=Qe(o,(function(){return{}})),t}return _createClass(a,[{key:"__init2",value:function(){this.stdout=new et}},{key:"__init3",value:function(){this.stderr=new et}},{key:"spawn",value:(r=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t=this;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",Xe((function(e){switch(e.event){case"Error":t._emit("error",e.payload);break;case"Terminated":t._emit("close",e.payload);break;case"Stdout":t.stdout._emit("data",e.payload);break;case"Stderr":t.stderr._emit("data",e.payload)}}),this.program,this.args,this.options).then((function(e){return new tt(e)})));case 1:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"execute",value:(t=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t=this;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",new Promise((function(e,r){t.on("error",r);var n=[],a=[];t.stdout.on("data",(function(e){n.push(e)})),t.stderr.on("data",(function(e){a.push(e)})),t.on("close",(function(t){e({code:t.code,signal:t.signal,stdout:n.join("\n"),stderr:a.join("\n")})})),t.spawn().catch(r)})));case 1:case"end":return e.stop()}}),e)}))),function(){return t.apply(this,arguments)})}],[{key:"sidecar",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],r=arguments.length>2?arguments[2]:void 0,n=new a(e,t,r);return n.options.sidecar=!0,n}}]),a}(et);function nt(){return(nt=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Shell",message:{cmd:"open",path:t,with:r}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var at=Object.freeze({__proto__:null,Command:rt,Child:tt,EventEmitter:et,open:function(e,t){return nt.apply(this,arguments)}});function ot(e){for(var t=void 0,r=e[0],n=1;n1&&void 0!==arguments[1]?arguments[1]:{};return _classCallCheck(this,r),n=t.call(this,e),ct([a,"optionalAccess",function(e){return e.skip}])||s({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:_objectSpread({label:e},a)}}}).then(_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",n.emit("tauri://created"));case 1:case"end":return e.stop()}}),e)})))).catch(function(){var e=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",n.emit("tauri://error",t));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),n}return _createClass(r,null,[{key:"getByLabel",value:function(e){return dt().some((function(t){return t.label===e}))?new r(e,{skip:!0}):null}}]),r}(wt);function bt(){return(bt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Rt(){return(Rt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function kt(){return(kt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}"__TAURI_METADATA__"in window?yt=new vt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn('Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label.\nNote that this is not an issue if running this frontend on a browser instead of a Tauri window.'),yt=new vt("main",{skip:!0}));var xt=Object.freeze({__proto__:null,WebviewWindow:vt,WebviewWindowHandle:_t,WindowManager:wt,getCurrent:function(){return new vt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})},getAll:dt,get appWindow(){return yt},LogicalSize:lt,PhysicalSize:ft,LogicalPosition:ht,PhysicalPosition:mt,get UserAttentionType(){return pt},currentMonitor:function(){return bt.apply(this,arguments)},primaryMonitor:function(){return Rt.apply(this,arguments)},availableMonitors:function(){return kt.apply(this,arguments)}}),Tt=_e()?"\r\n":"\n";function Gt(){return(Gt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"platform"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Pt(){return(Pt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"version"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Mt(){return(Mt=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"osType"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ot(){return(Ot=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"arch"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function At(){return(At=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",s({__tauriModule:"Os",message:{cmd:"tempdir"}}));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var Ct=Object.freeze({__proto__:null,EOL:Tt,platform:function(){return Gt.apply(this,arguments)},version:function(){return Pt.apply(this,arguments)},type:function(){return Mt.apply(this,arguments)},arch:function(){return Ot.apply(this,arguments)},tempdir:function(){return At.apply(this,arguments)}}),jt=o;e.app=h,e.cli=d,e.clipboard=_,e.dialog=x,e.event=I,e.fs=Q,e.globalShortcut=ae,e.http=me,e.invoke=jt,e.notification=ge,e.os=Ct,e.path=Je,e.process=$e,e.shell=at,e.tauri=u,e.updater=st,e.window=xt,Object.defineProperty(e,"__esModule",{value:!0})})); diff --git a/core/tauri/src/endpoints.rs b/core/tauri/src/endpoints.rs index 001c06475..af79c43cc 100644 --- a/core/tauri/src/endpoints.rs +++ b/core/tauri/src/endpoints.rs @@ -17,7 +17,6 @@ mod cli; mod clipboard; mod dialog; mod event; -#[allow(unused_imports)] mod file_system; mod global_shortcut; mod http; diff --git a/core/tauri/src/endpoints/app.rs b/core/tauri/src/endpoints/app.rs index 57bdbc33d..58e6fe4bc 100644 --- a/core/tauri/src/endpoints/app.rs +++ b/core/tauri/src/endpoints/app.rs @@ -5,9 +5,10 @@ use super::InvokeContext; use crate::Runtime; use serde::Deserialize; -use tauri_macros::CommandModule; +use tauri_macros::{command_enum, CommandModule}; /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] #[allow(clippy::enum_variant_names)] diff --git a/core/tauri/src/endpoints/cli.rs b/core/tauri/src/endpoints/cli.rs index ad6217695..ef9f7b44f 100644 --- a/core/tauri/src/endpoints/cli.rs +++ b/core/tauri/src/endpoints/cli.rs @@ -2,13 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::{InvokeContext, InvokeResponse}; use crate::Runtime; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; /// The API descriptor. -#[derive(Deserialize, CommandModule)] +#[command_enum] +#[derive(CommandModule, Deserialize)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { /// The get CLI matches API. @@ -16,21 +19,26 @@ pub enum Cmd { } impl Cmd { - #[module_command_handler(cli, "CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)")] + #[module_command_handler(cli)] fn cli_matches(context: InvokeContext) -> super::Result { if let Some(cli) = &context.config.tauri.cli { crate::api::cli::get_matches(cli, &context.package_info) .map(Into::into) .map_err(Into::into) } else { - Err(crate::Error::ApiNotAllowlisted("CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)".into()).into_anyhow()) + Err(crate::error::into_anyhow("CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)")) } } + + #[cfg(not(cli))] + fn cli_matches(_: InvokeContext) -> super::Result { + Err(crate::error::into_anyhow("CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)")) + } } #[cfg(test)] mod tests { - #[tauri_macros::module_command_test(cli, "CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)")] + #[tauri_macros::module_command_test(cli, "CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)", runtime)] #[quickcheck_macros::quickcheck] fn cli_matches() { let res = super::Cmd::cli_matches(crate::test::mock_invoke_context()); diff --git a/core/tauri/src/endpoints/clipboard.rs b/core/tauri/src/endpoints/clipboard.rs index 491703a2c..396061b93 100644 --- a/core/tauri/src/endpoints/clipboard.rs +++ b/core/tauri/src/endpoints/clipboard.rs @@ -2,25 +2,29 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; #[cfg(any(clipboard_write_text, clipboard_read_text))] use crate::runtime::ClipboardManager; use crate::Runtime; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", content = "data", rename_all = "camelCase")] pub enum Cmd { /// Write a text string to the clipboard. + #[cmd(clipboard_write_text, "clipboard > writeText")] WriteText(String), /// Read clipboard content as text. ReadText, } impl Cmd { - #[module_command_handler(clipboard_write_text, "clipboard > writeText")] + #[module_command_handler(clipboard_write_text)] fn write_text(context: InvokeContext, text: String) -> super::Result<()> { context .window @@ -30,7 +34,7 @@ impl Cmd { .map_err(crate::error::into_anyhow) } - #[module_command_handler(clipboard_read_text, "clipboard > readText")] + #[module_command_handler(clipboard_read_text)] fn read_text(context: InvokeContext) -> super::Result> { context .window @@ -39,6 +43,11 @@ impl Cmd { .read_text() .map_err(crate::error::into_anyhow) } + + #[cfg(not(clipboard_read_text))] + fn read_text(_: InvokeContext) -> super::Result<()> { + Err(crate::Error::ApiNotAllowlisted("clipboard > readText".into()).into_anyhow()) + } } #[cfg(test)] @@ -48,16 +57,20 @@ mod tests { fn write_text(text: String) { let ctx = crate::test::mock_invoke_context(); super::Cmd::write_text(ctx.clone(), text.clone()).unwrap(); + #[cfg(clipboard_read_text)] assert_eq!(super::Cmd::read_text(ctx).unwrap(), Some(text)); } - #[tauri_macros::module_command_test(clipboard_read_text, "clipboard > readText")] + #[tauri_macros::module_command_test(clipboard_read_text, "clipboard > readText", runtime)] #[quickcheck_macros::quickcheck] fn read_text() { let ctx = crate::test::mock_invoke_context(); assert_eq!(super::Cmd::read_text(ctx.clone()).unwrap(), None); - let text = "Tauri!".to_string(); - super::Cmd::write_text(ctx.clone(), text.clone()).unwrap(); - assert_eq!(super::Cmd::read_text(ctx).unwrap(), Some(text)); + #[cfg(clipboard_write_text)] + { + let text = "Tauri!".to_string(); + super::Cmd::write_text(ctx.clone(), text.clone()).unwrap(); + assert_eq!(super::Cmd::read_text(ctx).unwrap(), Some(text)); + } } } diff --git a/core/tauri/src/endpoints/dialog.rs b/core/tauri/src/endpoints/dialog.rs index 29708beb4..51435bf47 100644 --- a/core/tauri/src/endpoints/dialog.rs +++ b/core/tauri/src/endpoints/dialog.rs @@ -2,12 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::{InvokeContext, InvokeResponse}; use crate::Runtime; #[cfg(any(dialog_open, dialog_save))] use crate::{api::dialog::blocking::FileDialogBuilder, Manager, Scopes}; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; use std::path::PathBuf; @@ -56,25 +58,25 @@ pub struct SaveDialogOptions { } /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] #[allow(clippy::enum_variant_names)] pub enum Cmd { /// The open dialog API. - OpenDialog { - options: OpenDialogOptions, - }, + #[cmd(dialog_open, "dialog > open")] + OpenDialog { options: OpenDialogOptions }, /// The save dialog API. - SaveDialog { - options: SaveDialogOptions, - }, - MessageDialog { - message: String, - }, + #[cmd(dialog_save, "dialog > save")] + SaveDialog { options: SaveDialogOptions }, + #[cmd(dialog_message, "dialog > message")] + MessageDialog { message: String }, + #[cmd(dialog_ask, "dialog > ask")] AskDialog { title: Option, message: String, }, + #[cmd(dialog_confirm, "dialog > confirm")] ConfirmDialog { title: Option, message: String, @@ -82,7 +84,7 @@ pub enum Cmd { } impl Cmd { - #[module_command_handler(dialog_open, "dialog > open")] + #[module_command_handler(dialog_open)] #[allow(unused_variables)] fn open_dialog( context: InvokeContext, @@ -130,7 +132,7 @@ impl Cmd { Ok(res) } - #[module_command_handler(dialog_save, "dialog > save")] + #[module_command_handler(dialog_save)] #[allow(unused_variables)] fn save_dialog( context: InvokeContext, @@ -159,7 +161,7 @@ impl Cmd { Ok(path) } - #[module_command_handler(dialog_message, "dialog > message")] + #[module_command_handler(dialog_message)] fn message_dialog(context: InvokeContext, message: String) -> super::Result<()> { crate::api::dialog::blocking::message( Some(&context.window), @@ -169,7 +171,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(dialog_ask, "dialog > ask")] + #[module_command_handler(dialog_ask)] fn ask_dialog( context: InvokeContext, title: Option, @@ -182,7 +184,7 @@ impl Cmd { )) } - #[module_command_handler(dialog_confirm, "dialog > confirm")] + #[module_command_handler(dialog_confirm)] fn confirm_dialog( context: InvokeContext, title: Option, diff --git a/core/tauri/src/endpoints/event.rs b/core/tauri/src/endpoints/event.rs index b1adea0ab..8b0522562 100644 --- a/core/tauri/src/endpoints/event.rs +++ b/core/tauri/src/endpoints/event.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; use crate::{ api::ipc::CallbackFn, @@ -12,7 +14,7 @@ use crate::{ Manager, Runtime, }; use serde::{de::Deserializer, Deserialize}; -use tauri_macros::CommandModule; +use tauri_macros::{command_enum, CommandModule}; pub struct EventId(String); @@ -51,6 +53,7 @@ impl<'de> Deserialize<'de> for WindowLabel { } /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { diff --git a/core/tauri/src/endpoints/file_system.rs b/core/tauri/src/endpoints/file_system.rs index 53d6c2a5c..c54c3cfc9 100644 --- a/core/tauri/src/endpoints/file_system.rs +++ b/core/tauri/src/endpoints/file_system.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use crate::{ api::{ dir, @@ -19,7 +21,7 @@ use serde::{ de::{Deserializer, Error as DeError}, Deserialize, Serialize, }; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; use std::fmt::{Debug, Formatter}; use std::{ @@ -50,52 +52,62 @@ pub struct FileOperationOptions { } /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub(crate) enum Cmd { /// The read binary file API. + #[cmd(fs_read_file, "fs > readFile")] ReadFile { path: SafePathBuf, options: Option, }, /// The read binary file API. + #[cmd(fs_read_file, "fs > readFile")] ReadTextFile { path: SafePathBuf, options: Option, }, /// The write file API. + #[cmd(fs_write_file, "fs > writeFile")] WriteFile { path: SafePathBuf, contents: Vec, options: Option, }, /// The read dir API. + #[cmd(fs_read_dir, "fs > readDir")] ReadDir { path: SafePathBuf, options: Option, }, /// The copy file API. + #[cmd(fs_copy_file, "fs > copyFile")] CopyFile { source: SafePathBuf, destination: SafePathBuf, options: Option, }, /// The create dir API. + #[cmd(fs_create_dir, "fs > createDir")] CreateDir { path: SafePathBuf, options: Option, }, /// The remove dir API. + #[cmd(fs_remove_dir, "fs > removeDir")] RemoveDir { path: SafePathBuf, options: Option, }, /// The remove file API. + #[cmd(fs_remove_file, "fs > removeFile")] RemoveFile { path: SafePathBuf, options: Option, }, /// The rename file API. + #[cmd(fs_rename_file, "fs > renameFile")] #[serde(rename_all = "camelCase")] RenameFile { old_path: SafePathBuf, @@ -105,7 +117,7 @@ pub(crate) enum Cmd { } impl Cmd { - #[module_command_handler(fs_read_file, "fs > readFile")] + #[module_command_handler(fs_read_file)] fn read_file( context: InvokeContext, path: SafePathBuf, @@ -123,7 +135,7 @@ impl Cmd { .map_err(Into::into) } - #[module_command_handler(fs_read_file, "fs > readFile")] + #[module_command_handler(fs_read_file)] fn read_text_file( context: InvokeContext, path: SafePathBuf, @@ -141,7 +153,7 @@ impl Cmd { .map_err(Into::into) } - #[module_command_handler(fs_write_file, "fs > writeFile")] + #[module_command_handler(fs_write_file)] fn write_file( context: InvokeContext, path: SafePathBuf, @@ -161,7 +173,7 @@ impl Cmd { .and_then(|mut f| f.write_all(&contents).map_err(|err| err.into())) } - #[module_command_handler(fs_read_dir, "fs > readDir")] + #[module_command_handler(fs_read_dir)] fn read_dir( context: InvokeContext, path: SafePathBuf, @@ -184,7 +196,7 @@ impl Cmd { .map_err(Into::into) } - #[module_command_handler(fs_copy_file, "fs > copyFile")] + #[module_command_handler(fs_copy_file)] fn copy_file( context: InvokeContext, source: SafePathBuf, @@ -215,7 +227,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(fs_create_dir, "fs > createDir")] + #[module_command_handler(fs_create_dir)] fn create_dir( context: InvokeContext, path: SafePathBuf, @@ -244,7 +256,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(fs_remove_dir, "fs > removeDir")] + #[module_command_handler(fs_remove_dir)] fn remove_dir( context: InvokeContext, path: SafePathBuf, @@ -273,7 +285,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(fs_remove_file, "fs > removeFile")] + #[module_command_handler(fs_remove_file)] fn remove_file( context: InvokeContext, path: SafePathBuf, @@ -291,7 +303,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(fs_rename_file, "fs > renameFile")] + #[module_command_handler(fs_rename_file)] fn rename_file( context: InvokeContext, old_path: SafePathBuf, diff --git a/core/tauri/src/endpoints/global_shortcut.rs b/core/tauri/src/endpoints/global_shortcut.rs index f74a42166..dbde64b71 100644 --- a/core/tauri/src/endpoints/global_shortcut.rs +++ b/core/tauri/src/endpoints/global_shortcut.rs @@ -2,38 +2,45 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; use crate::{api::ipc::CallbackFn, Runtime}; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; #[cfg(global_shortcut_all)] use crate::runtime::GlobalShortcutManager; /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { /// Register a global shortcut. + #[cmd(global_shortcut_all, "globalShortcut > all")] Register { shortcut: String, handler: CallbackFn, }, /// Register a list of global shortcuts. + #[cmd(global_shortcut_all, "globalShortcut > all")] RegisterAll { shortcuts: Vec, handler: CallbackFn, }, /// Unregister a global shortcut. + #[cmd(global_shortcut_all, "globalShortcut > all")] Unregister { shortcut: String }, /// Unregisters all registered shortcuts. UnregisterAll, /// Determines whether the given hotkey is registered or not. + #[cmd(global_shortcut_all, "globalShortcut > all")] IsRegistered { shortcut: String }, } impl Cmd { - #[module_command_handler(global_shortcut_all, "globalShortcut > all")] + #[module_command_handler(global_shortcut_all)] fn register( context: InvokeContext, shortcut: String, @@ -44,7 +51,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(global_shortcut_all, "globalShortcut > all")] + #[module_command_handler(global_shortcut_all)] fn register_all( context: InvokeContext, shortcuts: Vec, @@ -57,7 +64,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(global_shortcut_all, "globalShortcut > all")] + #[module_command_handler(global_shortcut_all)] fn unregister(context: InvokeContext, shortcut: String) -> super::Result<()> { context .window @@ -68,7 +75,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(global_shortcut_all, "globalShortcut > all")] + #[module_command_handler(global_shortcut_all)] fn unregister_all(context: InvokeContext) -> super::Result<()> { context .window @@ -79,7 +86,12 @@ impl Cmd { Ok(()) } - #[module_command_handler(global_shortcut_all, "globalShortcut > all")] + #[cfg(not(global_shortcut_all))] + fn unregister_all(_: InvokeContext) -> super::Result<()> { + Err(crate::Error::ApiNotAllowlisted("globalShortcut > all".into()).into_anyhow()) + } + + #[module_command_handler(global_shortcut_all)] fn is_registered(context: InvokeContext, shortcut: String) -> super::Result { context .window @@ -139,7 +151,7 @@ mod tests { assert!(!super::Cmd::is_registered(ctx, shortcut).unwrap()); } - #[tauri_macros::module_command_test(global_shortcut_all, "globalShortcut > all")] + #[tauri_macros::module_command_test(global_shortcut_all, "globalShortcut > all", runtime)] #[quickcheck_macros::quickcheck] fn unregister_all() { let shortcuts = vec!["CTRL+X".to_string(), "SUPER+C".to_string(), "D".to_string()]; diff --git a/core/tauri/src/endpoints/http.rs b/core/tauri/src/endpoints/http.rs index 751af8761..05a7d4e44 100644 --- a/core/tauri/src/endpoints/http.rs +++ b/core/tauri/src/endpoints/http.rs @@ -2,10 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; use crate::Runtime; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; #[cfg(http_request)] use std::{ @@ -20,6 +22,7 @@ type ClientBuilder = (); #[cfg(not(http_request))] type HttpRequestBuilder = (); #[cfg(not(http_request))] +#[allow(dead_code)] type ResponseData = (); type ClientId = u32; @@ -34,15 +37,19 @@ fn clients() -> &'static ClientStore { } /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[cmd(async)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { /// Create a new HTTP client. + #[cmd(http_request, "http > request")] CreateClient { options: Option }, /// Drop a HTTP client. + #[cmd(http_request, "http > request")] DropClient { client: ClientId }, /// The HTTP request API. + #[cmd(http_request, "http > request")] HttpRequest { client: ClientId, options: Box, @@ -50,7 +57,7 @@ pub enum Cmd { } impl Cmd { - #[module_command_handler(http_request, "http > request")] + #[module_command_handler(http_request)] async fn create_client( _context: InvokeContext, options: Option, @@ -62,7 +69,7 @@ impl Cmd { Ok(id) } - #[module_command_handler(http_request, "http > request")] + #[module_command_handler(http_request)] async fn drop_client( _context: InvokeContext, client: ClientId, @@ -72,7 +79,7 @@ impl Cmd { Ok(()) } - #[module_command_handler(http_request, "http > request")] + #[module_command_handler(http_request)] async fn http_request( context: InvokeContext, client_id: ClientId, @@ -115,7 +122,7 @@ impl Cmd { mod tests { use super::{ClientBuilder, ClientId}; - #[tauri_macros::module_command_test(http_request, "http > request", async)] + #[tauri_macros::module_command_test(http_request, "http > request")] #[quickcheck_macros::quickcheck] fn create_client(options: Option) { assert!(crate::async_runtime::block_on(super::Cmd::create_client( @@ -125,7 +132,7 @@ mod tests { .is_ok()); } - #[tauri_macros::module_command_test(http_request, "http > request", async)] + #[tauri_macros::module_command_test(http_request, "http > request")] #[quickcheck_macros::quickcheck] fn drop_client(client_id: ClientId) { crate::async_runtime::block_on(async move { diff --git a/core/tauri/src/endpoints/notification.rs b/core/tauri/src/endpoints/notification.rs index 826fd7e3c..f07bc142c 100644 --- a/core/tauri/src/endpoints/notification.rs +++ b/core/tauri/src/endpoints/notification.rs @@ -2,10 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; use crate::Runtime; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; #[cfg(notification_all)] use crate::{api::notification::Notification, Env, Manager}; @@ -28,10 +30,12 @@ pub struct NotificationOptions { } /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { /// The show notification API. + #[cmd(notification_all, "notification > all")] Notification { options: NotificationOptions }, /// The request notification permission API. RequestNotificationPermission, @@ -40,7 +44,7 @@ pub enum Cmd { } impl Cmd { - #[module_command_handler(notification_all, "notification > all")] + #[module_command_handler(notification_all)] fn notification( context: InvokeContext, options: NotificationOptions, diff --git a/core/tauri/src/endpoints/operating_system.rs b/core/tauri/src/endpoints/operating_system.rs index 010eac78c..984f8c5ab 100644 --- a/core/tauri/src/endpoints/operating_system.rs +++ b/core/tauri/src/endpoints/operating_system.rs @@ -2,13 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; use crate::Runtime; use serde::Deserialize; use std::path::PathBuf; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { @@ -19,33 +22,52 @@ pub enum Cmd { Tempdir, } +#[cfg(os_all)] impl Cmd { - #[module_command_handler(os_all, "os > all")] fn platform(_context: InvokeContext) -> super::Result<&'static str> { Ok(os_platform()) } - #[module_command_handler(os_all, "os > all")] fn version(_context: InvokeContext) -> super::Result { Ok(os_info::get().version().to_string()) } - #[module_command_handler(os_all, "os > all")] fn os_type(_context: InvokeContext) -> super::Result<&'static str> { Ok(os_type()) } - #[module_command_handler(os_all, "os > all")] fn arch(_context: InvokeContext) -> super::Result<&'static str> { Ok(std::env::consts::ARCH) } - #[module_command_handler(os_all, "os > all")] fn tempdir(_context: InvokeContext) -> super::Result { Ok(std::env::temp_dir()) } } +#[cfg(not(os_all))] +impl Cmd { + fn platform(_context: InvokeContext) -> super::Result<&'static str> { + Err(crate::Error::ApiNotAllowlisted("os > all".into()).into_anyhow()) + } + + fn version(_context: InvokeContext) -> super::Result { + Err(crate::Error::ApiNotAllowlisted("os > all".into()).into_anyhow()) + } + + fn os_type(_context: InvokeContext) -> super::Result<&'static str> { + Err(crate::Error::ApiNotAllowlisted("os > all".into()).into_anyhow()) + } + + fn arch(_context: InvokeContext) -> super::Result<&'static str> { + Err(crate::Error::ApiNotAllowlisted("os > all".into()).into_anyhow()) + } + + fn tempdir(_context: InvokeContext) -> super::Result { + Err(crate::Error::ApiNotAllowlisted("os > all".into()).into_anyhow()) + } +} + #[cfg(os_all)] fn os_type() -> &'static str { #[cfg(target_os = "linux")] @@ -55,6 +77,7 @@ fn os_type() -> &'static str { #[cfg(target_os = "macos")] return "Darwin"; } + #[cfg(os_all)] fn os_platform() -> &'static str { match std::env::consts::OS { @@ -66,23 +89,23 @@ fn os_platform() -> &'static str { #[cfg(test)] mod tests { - #[tauri_macros::module_command_test(os_all, "os > all")] + #[tauri_macros::module_command_test(os_all, "os > all", runtime)] #[quickcheck_macros::quickcheck] fn platform() {} - #[tauri_macros::module_command_test(os_all, "os > all")] + #[tauri_macros::module_command_test(os_all, "os > all", runtime)] #[quickcheck_macros::quickcheck] fn version() {} - #[tauri_macros::module_command_test(os_all, "os > all")] + #[tauri_macros::module_command_test(os_all, "os > all", runtime)] #[quickcheck_macros::quickcheck] fn os_type() {} - #[tauri_macros::module_command_test(os_all, "os > all")] + #[tauri_macros::module_command_test(os_all, "os > all", runtime)] #[quickcheck_macros::quickcheck] fn arch() {} - #[tauri_macros::module_command_test(os_all, "os > all")] + #[tauri_macros::module_command_test(os_all, "os > all", runtime)] #[quickcheck_macros::quickcheck] fn tempdir() {} } diff --git a/core/tauri/src/endpoints/path.rs b/core/tauri/src/endpoints/path.rs index f66d7c088..6d0c3038a 100644 --- a/core/tauri/src/endpoints/path.rs +++ b/core/tauri/src/endpoints/path.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use crate::{api::path::BaseDirectory, Runtime}; #[cfg(path_all)] use crate::{Env, Manager}; @@ -11,42 +13,36 @@ use std::path::{Component, Path, MAIN_SEPARATOR}; use super::InvokeContext; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { + #[cmd(path_all, "path > all")] ResolvePath { path: String, directory: Option, }, - Resolve { - paths: Vec, - }, - Normalize { - path: String, - }, - Join { - paths: Vec, - }, - Dirname { - path: String, - }, - Extname { - path: String, - }, - Basename { - path: String, - ext: Option, - }, - IsAbsolute { - path: String, - }, + #[cmd(path_all, "path > all")] + Resolve { paths: Vec }, + #[cmd(path_all, "path > all")] + Normalize { path: String }, + #[cmd(path_all, "path > all")] + Join { paths: Vec }, + #[cmd(path_all, "path > all")] + Dirname { path: String }, + #[cmd(path_all, "path > all")] + Extname { path: String }, + #[cmd(path_all, "path > all")] + Basename { path: String, ext: Option }, + #[cmd(path_all, "path > all")] + IsAbsolute { path: String }, } impl Cmd { - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn resolve_path( context: InvokeContext, path: String, @@ -62,7 +58,7 @@ impl Cmd { .map_err(Into::into) } - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn resolve(_context: InvokeContext, paths: Vec) -> super::Result { // Start with current directory then start adding paths from the vector one by one using `PathBuf.push()` which // will ensure that if an absolute path is encountered in the iteration, it will be used as the current full path. @@ -77,7 +73,7 @@ impl Cmd { Ok(normalize_path(&path)) } - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn normalize(_context: InvokeContext, path: String) -> super::Result { let mut p = normalize_path_no_absolute(Path::new(&path)) .to_string_lossy() @@ -101,7 +97,7 @@ impl Cmd { ) } - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn join(_context: InvokeContext, mut paths: Vec) -> super::Result { let path = PathBuf::from( paths @@ -125,7 +121,7 @@ impl Cmd { Ok(if p.is_empty() { ".".into() } else { p }) } - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn dirname(_context: InvokeContext, path: String) -> super::Result { match Path::new(&path).parent() { Some(p) => Ok(p.to_path_buf()), @@ -135,7 +131,7 @@ impl Cmd { } } - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn extname(_context: InvokeContext, path: String) -> super::Result { match Path::new(&path) .extension() @@ -148,7 +144,7 @@ impl Cmd { } } - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn basename( _context: InvokeContext, path: String, @@ -169,7 +165,7 @@ impl Cmd { } } - #[module_command_handler(path_all, "path > all")] + #[module_command_handler(path_all)] fn is_absolute(_context: InvokeContext, path: String) -> super::Result { Ok(Path::new(&path).is_absolute()) } diff --git a/core/tauri/src/endpoints/process.rs b/core/tauri/src/endpoints/process.rs index 8383eca0d..76f5279a6 100644 --- a/core/tauri/src/endpoints/process.rs +++ b/core/tauri/src/endpoints/process.rs @@ -2,32 +2,41 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; #[cfg(process_relaunch)] use crate::Manager; use crate::Runtime; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { /// Relaunch application Relaunch, /// Close application with provided exit_code + #[cmd(process_exit, "process > exit")] #[serde(rename_all = "camelCase")] Exit { exit_code: i32 }, } impl Cmd { - #[module_command_handler(process_relaunch, "process > relaunch")] + #[module_command_handler(process_relaunch)] fn relaunch(context: InvokeContext) -> super::Result<()> { context.window.app_handle().restart(); Ok(()) } - #[module_command_handler(process_exit, "process > exit")] + #[cfg(not(process_relaunch))] + fn relaunch(_: InvokeContext) -> super::Result<()> { + Err(crate::Error::ApiNotAllowlisted("process > relaunch".into()).into_anyhow()) + } + + #[module_command_handler(process_exit)] fn exit(_context: InvokeContext, exit_code: i32) -> super::Result<()> { // would be great if we can have a handler inside tauri // who close all window and emit an event that user can catch @@ -38,7 +47,7 @@ impl Cmd { #[cfg(test)] mod tests { - #[tauri_macros::module_command_test(process_relaunch, "process > relaunch")] + #[tauri_macros::module_command_test(process_relaunch, "process > relaunch", runtime)] #[quickcheck_macros::quickcheck] fn relaunch() {} diff --git a/core/tauri/src/endpoints/shell.rs b/core/tauri/src/endpoints/shell.rs index 6e208dcb1..abd55644d 100644 --- a/core/tauri/src/endpoints/shell.rs +++ b/core/tauri/src/endpoints/shell.rs @@ -2,12 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::InvokeContext; use crate::{api::ipc::CallbackFn, Runtime}; #[cfg(shell_scope)] use crate::{Manager, Scopes}; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; #[cfg(shell_scope)] use crate::ExecuteArgs; @@ -55,10 +57,12 @@ pub struct CommandOptions { } /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { /// The execute script API. + #[cmd(shell_script, "shell > execute or shell > sidecar")] #[serde(rename_all = "camelCase")] Execute { program: String, @@ -67,20 +71,16 @@ pub enum Cmd { #[serde(default)] options: CommandOptions, }, - StdinWrite { - pid: ChildId, - buffer: Buffer, - }, - KillChild { - pid: ChildId, - }, - Open { - path: String, - with: Option, - }, + #[cmd(shell_script, "shell > execute or shell > sidecar")] + StdinWrite { pid: ChildId, buffer: Buffer }, + #[cmd(shell_script, "shell > execute or shell > sidecar")] + KillChild { pid: ChildId }, + #[cmd(shell_open, "shell > open")] + Open { path: String, with: Option }, } impl Cmd { + #[module_command_handler(shell_script)] #[allow(unused_variables)] fn execute( context: InvokeContext, @@ -169,7 +169,7 @@ impl Cmd { } } - #[cfg(any(shell_execute, shell_sidecar))] + #[module_command_handler(shell_script)] fn stdin_write( _context: InvokeContext, pid: ChildId, @@ -184,16 +184,7 @@ impl Cmd { Ok(()) } - #[cfg(not(any(shell_execute, shell_sidecar)))] - fn stdin_write( - _context: InvokeContext, - _pid: ChildId, - _buffer: Buffer, - ) -> super::Result<()> { - Err(crate::Error::ApiNotAllowlisted("shell > execute or shell > sidecar".into()).into_anyhow()) - } - - #[cfg(any(shell_execute, shell_sidecar))] + #[module_command_handler(shell_script)] fn kill_child(_context: InvokeContext, pid: ChildId) -> super::Result<()> { if let Some(child) = command_childs().lock().unwrap().remove(&pid) { child.kill()?; @@ -201,15 +192,10 @@ impl Cmd { Ok(()) } - #[cfg(not(any(shell_execute, shell_sidecar)))] - fn kill_child(_context: InvokeContext, _pid: ChildId) -> super::Result<()> { - Err(crate::Error::ApiNotAllowlisted("shell > execute or shell > sidecar".into()).into_anyhow()) - } - /// Open a (url) path with a default or specific browser opening program. /// /// See [`crate::api::shell::open`] for how it handles security-related measures. - #[module_command_handler(shell_open, "shell > open")] + #[module_command_handler(shell_open)] fn open( context: InvokeContext, path: String, diff --git a/core/tauri/src/endpoints/window.rs b/core/tauri/src/endpoints/window.rs index 662a77e56..85e69d689 100644 --- a/core/tauri/src/endpoints/window.rs +++ b/core/tauri/src/endpoints/window.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +#![allow(unused_imports)] + use super::{InvokeContext, InvokeResponse}; #[cfg(window_create)] use crate::runtime::{webview::WindowBuilder, Dispatch}; @@ -14,7 +16,7 @@ use crate::{ CursorIcon, Icon, Manager, Runtime, }; use serde::Deserialize; -use tauri_macros::{module_command_handler, CommandModule}; +use tauri_macros::{command_enum, module_command_handler, CommandModule}; #[derive(Deserialize)] #[serde(untagged)] @@ -168,13 +170,13 @@ impl WindowManagerCmd { } /// The API descriptor. +#[command_enum] #[derive(Deserialize, CommandModule)] #[cmd(async)] #[serde(tag = "cmd", content = "data", rename_all = "camelCase")] pub enum Cmd { - CreateWebview { - options: Box, - }, + #[cmd(window_create, "window > create")] + CreateWebview { options: Box }, Manage { label: Option, cmd: WindowManagerCmd, @@ -182,7 +184,7 @@ pub enum Cmd { } impl Cmd { - #[module_command_handler(window_create, "window > create")] + #[module_command_handler(window_create)] async fn create_webview( context: InvokeContext, options: Box, diff --git a/core/tauri/src/error.rs b/core/tauri/src/error.rs index 16d7a3fcf..fb084a918 100644 --- a/core/tauri/src/error.rs +++ b/core/tauri/src/error.rs @@ -71,11 +71,8 @@ pub enum Error { /// Client with specified ID not found. #[error("http client dropped or not initialized")] HttpClientNotInitialized, - /// API not enabled by Tauri. - #[error("{0}")] - ApiNotEnabled(String), /// API not whitelisted on tauri.conf.json - #[error("'{0}' not on the allowlist (https://tauri.studio/docs/api/config#tauri.allowlist)")] + #[error("'{0}' not in the allowlist (https://tauri.studio/docs/api/config#tauri.allowlist)")] ApiNotAllowlisted(String), /// Invalid args when running a command. #[error("invalid args `{1}` for command `{0}`: {2}")] @@ -138,6 +135,7 @@ pub(crate) fn into_anyhow(err: T) -> anyhow::Error { } impl Error { + #[allow(dead_code)] pub(crate) fn into_anyhow(self) -> anyhow::Error { anyhow::anyhow!(self.to_string()) } diff --git a/tooling/api/src/clipboard.ts b/tooling/api/src/clipboard.ts index 84f5c4d25..ac456c5ce 100644 --- a/tooling/api/src/clipboard.ts +++ b/tooling/api/src/clipboard.ts @@ -35,7 +35,10 @@ async function readText(): Promise { return invokeTauriCommand({ __tauriModule: 'Clipboard', message: { - cmd: 'readText' + cmd: 'readText', + // if data is not set, `serde` will ignore the custom deserializer + // that is set when the API is not allowlisted + data: null } }) } From e11878bcf7174b261a1fa146fc7d564d12e6312a Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 24 Apr 2022 11:11:14 -0700 Subject: [PATCH 2/7] refactor(core): add global-shortcut Cargo feature, enhancing binary size (#3956) --- .changes/global-shortcut-feature.md | 7 + core/tauri-codegen/src/context.rs | 1 + core/tauri-runtime-wry/Cargo.toml | 1 + core/tauri-runtime-wry/src/global_shortcut.rs | 164 ++++++++++++++ core/tauri-runtime-wry/src/lib.rs | 207 +++++------------- core/tauri-runtime/Cargo.toml | 1 + core/tauri-runtime/src/lib.rs | 4 + core/tauri/Cargo.toml | 3 +- core/tauri/src/app.rs | 10 + core/tauri/src/lib.rs | 3 +- core/tauri/src/test/mock_runtime.rs | 12 +- 11 files changed, 261 insertions(+), 152 deletions(-) create mode 100644 .changes/global-shortcut-feature.md create mode 100644 core/tauri-runtime-wry/src/global_shortcut.rs diff --git a/.changes/global-shortcut-feature.md b/.changes/global-shortcut-feature.md new file mode 100644 index 000000000..debbf891a --- /dev/null +++ b/.changes/global-shortcut-feature.md @@ -0,0 +1,7 @@ +--- +"tauri": patch +"tauri-runtime": minor +"tauri-runtime-wry": minor +--- + +**Breaking change::* Added the `global-shortcut` Cargo feature. diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index a27e2e196..a4d1c3582 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -56,6 +56,7 @@ fn map_core_assets( if path.extension() == Some(OsStr::new("html")) { let mut document = parse_html(String::from_utf8_lossy(input).into_owned()); + #[allow(clippy::collapsible_if)] if csp { #[cfg(target_os = "linux")] ::tauri_utils::html::inject_csp_token(&mut document); diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 1e43969c2..869342cd2 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -41,3 +41,4 @@ macos-private-api = [ objc-exception = [ "wry/objc-exception" ] gtk-tray = [ "wry/gtk-tray" ] ayatana-tray = [ "wry/ayatana-tray" ] +global-shortcut = [ "tauri-runtime/global-shortcut" ] diff --git a/core/tauri-runtime-wry/src/global_shortcut.rs b/core/tauri-runtime-wry/src/global_shortcut.rs new file mode 100644 index 000000000..2e149f5ec --- /dev/null +++ b/core/tauri-runtime-wry/src/global_shortcut.rs @@ -0,0 +1,164 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! Global shortcut implementation. + +use std::{ + collections::HashMap, + fmt, + sync::{ + mpsc::{channel, Sender}, + Arc, Mutex, + }, +}; + +use crate::{getter, Context, Message}; + +use tauri_runtime::{Error, GlobalShortcutManager, Result, UserEvent}; +pub use wry::application::global_shortcut::ShortcutManager as WryShortcutManager; +use wry::application::{ + accelerator::{Accelerator, AcceleratorId}, + global_shortcut::GlobalShortcut, +}; + +pub type GlobalShortcutListeners = Arc>>>; + +#[derive(Debug, Clone)] +pub enum GlobalShortcutMessage { + IsRegistered(Accelerator, Sender), + Register(Accelerator, Sender>), + Unregister(GlobalShortcutWrapper, Sender>), + UnregisterAll(Sender>), +} + +#[derive(Debug, Clone)] +pub struct GlobalShortcutWrapper(GlobalShortcut); + +// SAFETY: usage outside of main thread is guarded, we use the event loop on such cases. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for GlobalShortcutWrapper {} + +/// Wrapper around [`WryShortcutManager`]. +#[derive(Clone)] +pub struct GlobalShortcutManagerHandle { + pub context: Context, + pub shortcuts: Arc>>, + pub listeners: GlobalShortcutListeners, +} + +// SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Sync for GlobalShortcutManagerHandle {} + +impl fmt::Debug for GlobalShortcutManagerHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GlobalShortcutManagerHandle") + .field("context", &self.context) + .field("shortcuts", &self.shortcuts) + .finish() + } +} + +impl GlobalShortcutManager for GlobalShortcutManagerHandle { + fn is_registered(&self, accelerator: &str) -> Result { + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::GlobalShortcut(GlobalShortcutMessage::IsRegistered( + accelerator.parse().expect("invalid accelerator"), + tx + )) + ) + } + + fn register(&mut self, accelerator: &str, handler: F) -> Result<()> { + let wry_accelerator: Accelerator = accelerator.parse().expect("invalid accelerator"); + let id = wry_accelerator.clone().id(); + let (tx, rx) = channel(); + let shortcut = getter!( + self, + rx, + Message::GlobalShortcut(GlobalShortcutMessage::Register(wry_accelerator, tx)) + )??; + + self.listeners.lock().unwrap().insert(id, Box::new(handler)); + self + .shortcuts + .lock() + .unwrap() + .insert(accelerator.into(), (id, shortcut)); + + Ok(()) + } + + fn unregister_all(&mut self) -> Result<()> { + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::GlobalShortcut(GlobalShortcutMessage::UnregisterAll(tx)) + )??; + self.listeners.lock().unwrap().clear(); + self.shortcuts.lock().unwrap().clear(); + Ok(()) + } + + fn unregister(&mut self, accelerator: &str) -> Result<()> { + if let Some((accelerator_id, shortcut)) = self.shortcuts.lock().unwrap().remove(accelerator) { + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::GlobalShortcut(GlobalShortcutMessage::Unregister(shortcut, tx)) + )??; + self.listeners.lock().unwrap().remove(&accelerator_id); + } + Ok(()) + } +} + +pub fn handle_global_shortcut_message( + message: GlobalShortcutMessage, + global_shortcut_manager: &Arc>, +) { + match message { + GlobalShortcutMessage::IsRegistered(accelerator, tx) => tx + .send( + global_shortcut_manager + .lock() + .unwrap() + .is_registered(&accelerator), + ) + .unwrap(), + GlobalShortcutMessage::Register(accelerator, tx) => tx + .send( + global_shortcut_manager + .lock() + .unwrap() + .register(accelerator) + .map(GlobalShortcutWrapper) + .map_err(|e| Error::GlobalShortcut(Box::new(e))), + ) + .unwrap(), + GlobalShortcutMessage::Unregister(shortcut, tx) => tx + .send( + global_shortcut_manager + .lock() + .unwrap() + .unregister(shortcut.0) + .map_err(|e| Error::GlobalShortcut(Box::new(e))), + ) + .unwrap(), + GlobalShortcutMessage::UnregisterAll(tx) => tx + .send( + global_shortcut_manager + .lock() + .unwrap() + .unregister_all() + .map_err(|e| Error::GlobalShortcut(Box::new(e))), + ) + .unwrap(), + } +} diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 09b13f8dc..ca1a39bd0 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -16,9 +16,8 @@ use tauri_runtime::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, CursorIcon, DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent, }, - ClipboardManager, Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, - GlobalShortcutManager, Result, RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType, - UserEvent, WindowIcon, + ClipboardManager, Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Result, RunEvent, + RunIteration, Runtime, RuntimeHandle, UserAttentionType, UserEvent, WindowIcon, }; use tauri_runtime::window::MenuEvent; @@ -44,7 +43,6 @@ use tauri_utils::{config::WindowConfig, Theme}; use uuid::Uuid; use wry::{ application::{ - accelerator::{Accelerator, AcceleratorId}, clipboard::Clipboard, dpi::{ LogicalPosition as WryLogicalPosition, LogicalSize as WryLogicalSize, @@ -55,7 +53,6 @@ use wry::{ event_loop::{ ControlFlow, EventLoop, EventLoopProxy as WryEventLoopProxy, EventLoopWindowTarget, }, - global_shortcut::{GlobalShortcut, ShortcutManager as WryShortcutManager}, menu::{ AboutMetadata as WryAboutMetadata, CustomMenuItem as WryCustomMenuItem, MenuBar, MenuId as WryMenuId, MenuItem as WryMenuItem, MenuItemAttributes as WryMenuItemAttributes, @@ -109,13 +106,16 @@ mod system_tray; #[cfg(feature = "system-tray")] use system_tray::*; +#[cfg(feature = "global-shortcut")] +mod global_shortcut; +#[cfg(feature = "global-shortcut")] +use global_shortcut::*; + type WebContextStore = Arc, WebContext>>>; // window type WindowEventHandler = Box; type WindowEventListenersMap = Arc>>; type WindowEventListeners = Arc>>; -// global shortcut -type GlobalShortcutListeners = Arc>>>; // menu pub type MenuEventHandler = Box; pub type MenuEventListeners = Arc>>; @@ -134,9 +134,10 @@ impl WebviewIdStore { } } +#[macro_export] macro_rules! getter { ($self: ident, $rx: expr, $message: expr) => {{ - send_user_message(&$self.context, $message)?; + crate::send_user_message(&$self.context, $message)?; $rx.recv().map_err(|_| Error::FailedToReceiveMessage) }}; } @@ -156,6 +157,7 @@ fn send_user_message(context: &Context, message: Message) -> UserMessageContext { webview_id_map: context.webview_id_map.clone(), window_event_listeners: &context.window_event_listeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: context.main_thread.global_shortcut_manager.clone(), clipboard_manager: context.main_thread.clipboard_manager.clone(), menu_event_listeners: &context.menu_event_listeners, @@ -175,7 +177,7 @@ fn send_user_message(context: &Context, message: Message) -> } #[derive(Clone)] -struct Context { +pub struct Context { webview_id_map: WebviewIdStore, main_thread_id: ThreadId, proxy: WryEventLoopProxy>, @@ -236,6 +238,7 @@ impl Context { struct DispatcherMainThreadContext { window_target: EventLoopWindowTarget>, web_context: WebContextStore, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: Arc>, clipboard_manager: Arc>, windows: Arc>>, @@ -461,93 +464,6 @@ impl From for NativeImageWrapper { } } -#[derive(Debug, Clone)] -pub struct GlobalShortcutWrapper(GlobalShortcut); - -// SAFETY: usage outside of main thread is guarded, we use the event loop on such cases. -#[allow(clippy::non_send_fields_in_send_ty)] -unsafe impl Send for GlobalShortcutWrapper {} - -/// Wrapper around [`WryShortcutManager`]. -#[derive(Clone)] -pub struct GlobalShortcutManagerHandle { - context: Context, - shortcuts: Arc>>, - listeners: GlobalShortcutListeners, -} - -// SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`. -#[allow(clippy::non_send_fields_in_send_ty)] -unsafe impl Sync for GlobalShortcutManagerHandle {} - -impl fmt::Debug for GlobalShortcutManagerHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("GlobalShortcutManagerHandle") - .field("context", &self.context) - .field("shortcuts", &self.shortcuts) - .finish() - } -} - -impl GlobalShortcutManager for GlobalShortcutManagerHandle { - fn is_registered(&self, accelerator: &str) -> Result { - let (tx, rx) = channel(); - getter!( - self, - rx, - Message::GlobalShortcut(GlobalShortcutMessage::IsRegistered( - accelerator.parse().expect("invalid accelerator"), - tx - )) - ) - } - - fn register(&mut self, accelerator: &str, handler: F) -> Result<()> { - let wry_accelerator: Accelerator = accelerator.parse().expect("invalid accelerator"); - let id = wry_accelerator.clone().id(); - let (tx, rx) = channel(); - let shortcut = getter!( - self, - rx, - Message::GlobalShortcut(GlobalShortcutMessage::Register(wry_accelerator, tx)) - )??; - - self.listeners.lock().unwrap().insert(id, Box::new(handler)); - self - .shortcuts - .lock() - .unwrap() - .insert(accelerator.into(), (id, shortcut)); - - Ok(()) - } - - fn unregister_all(&mut self) -> Result<()> { - let (tx, rx) = channel(); - getter!( - self, - rx, - Message::GlobalShortcut(GlobalShortcutMessage::UnregisterAll(tx)) - )??; - self.listeners.lock().unwrap().clear(); - self.shortcuts.lock().unwrap().clear(); - Ok(()) - } - - fn unregister(&mut self, accelerator: &str) -> Result<()> { - if let Some((accelerator_id, shortcut)) = self.shortcuts.lock().unwrap().remove(accelerator) { - let (tx, rx) = channel(); - getter!( - self, - rx, - Message::GlobalShortcut(GlobalShortcutMessage::Unregister(shortcut, tx)) - )??; - self.listeners.lock().unwrap().remove(&accelerator_id); - } - Ok(()) - } -} - #[derive(Debug, Clone)] pub struct ClipboardManagerWrapper { context: Context, @@ -1162,14 +1078,6 @@ pub enum TrayMessage { Close, } -#[derive(Debug, Clone)] -pub enum GlobalShortcutMessage { - IsRegistered(Accelerator, Sender), - Register(Accelerator, Sender>), - Unregister(GlobalShortcutWrapper, Sender>), - UnregisterAll(Sender>), -} - #[derive(Debug, Clone)] pub enum ClipboardMessage { WriteText(String, Sender<()>), @@ -1192,6 +1100,7 @@ pub enum Message { Box (String, WryWindowBuilder) + Send>, Sender>>, ), + #[cfg(feature = "global-shortcut")] GlobalShortcut(GlobalShortcutMessage), Clipboard(ClipboardMessage), UserEvent(T), @@ -1204,6 +1113,7 @@ impl Clone for Message { Self::Webview(i, m) => Self::Webview(*i, m.clone()), #[cfg(feature = "system-tray")] Self::Tray(m) => Self::Tray(m.clone()), + #[cfg(feature = "global-shortcut")] Self::GlobalShortcut(m) => Self::GlobalShortcut(m.clone()), Self::Clipboard(m) => Self::Clipboard(m.clone()), Self::UserEvent(t) => Self::UserEvent(t.clone()), @@ -1680,7 +1590,9 @@ impl EventLoopProxy for EventProxy { /// A Tauri [`Runtime`] wrapper around wry. pub struct Wry { main_thread_id: ThreadId, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: Arc>, + #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: GlobalShortcutManagerHandle, clipboard_manager: Arc>, clipboard_manager_handle: ClipboardManagerWrapper, @@ -1698,11 +1610,6 @@ impl fmt::Debug for Wry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("Wry"); d.field("main_thread_id", &self.main_thread_id) - .field("global_shortcut_manager", &self.global_shortcut_manager) - .field( - "global_shortcut_manager_handle", - &self.global_shortcut_manager_handle, - ) .field("clipboard_manager", &self.clipboard_manager) .field("clipboard_manager_handle", &self.clipboard_manager_handle) .field("event_loop", &self.event_loop) @@ -1710,6 +1617,12 @@ impl fmt::Debug for Wry { .field("web_context", &self.web_context); #[cfg(feature = "system-tray")] d.field("tray_context", &self.tray_context); + #[cfg(feature = "global-shortcut")] + d.field("global_shortcut_manager", &self.global_shortcut_manager) + .field( + "global_shortcut_manager_handle", + &self.global_shortcut_manager_handle, + ); d.finish() } } @@ -1793,7 +1706,10 @@ impl Wry { let proxy = event_loop.create_proxy(); let main_thread_id = current_thread().id(); let web_context = WebContextStore::default(); + + #[cfg(feature = "global-shortcut")] let global_shortcut_manager = Arc::new(Mutex::new(WryShortcutManager::new(&event_loop))); + let clipboard_manager = Arc::new(Mutex::new(Clipboard::new())); let windows = Arc::new(Mutex::new(HashMap::default())); let webview_id_map = WebviewIdStore::default(); @@ -1812,6 +1728,7 @@ impl Wry { main_thread: DispatcherMainThreadContext { window_target: event_loop.deref().clone(), web_context: web_context.clone(), + #[cfg(feature = "global-shortcut")] global_shortcut_manager: global_shortcut_manager.clone(), clipboard_manager: clipboard_manager.clone(), windows: windows.clone(), @@ -1820,14 +1737,19 @@ impl Wry { }, }; + #[cfg(feature = "global-shortcut")] let global_shortcut_listeners = GlobalShortcutListeners::default(); + + #[allow(clippy::redundant_clone)] let clipboard_manager_handle = ClipboardManagerWrapper { context: event_loop_context.clone(), }; Ok(Self { main_thread_id, + #[cfg(feature = "global-shortcut")] global_shortcut_manager, + #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: GlobalShortcutManagerHandle { context: event_loop_context, shortcuts: Default::default(), @@ -1850,6 +1772,7 @@ impl Wry { impl Runtime for Wry { type Dispatcher = WryDispatcher; type Handle = WryHandle; + #[cfg(feature = "global-shortcut")] type GlobalShortcutManager = GlobalShortcutManagerHandle; type ClipboardManager = ClipboardManagerWrapper; #[cfg(feature = "system-tray")] @@ -1886,6 +1809,7 @@ impl Runtime for Wry { main_thread: DispatcherMainThreadContext { window_target: self.event_loop.deref().clone(), web_context: self.web_context.clone(), + #[cfg(feature = "global-shortcut")] global_shortcut_manager: self.global_shortcut_manager.clone(), clipboard_manager: self.clipboard_manager.clone(), windows: self.windows.clone(), @@ -1896,6 +1820,7 @@ impl Runtime for Wry { } } + #[cfg(feature = "global-shortcut")] fn global_shortcut_manager(&self) -> Self::GlobalShortcutManager { self.global_shortcut_manager_handle.clone() } @@ -1920,6 +1845,7 @@ impl Runtime for Wry { main_thread: DispatcherMainThreadContext { window_target: self.event_loop.deref().clone(), web_context: self.web_context.clone(), + #[cfg(feature = "global-shortcut")] global_shortcut_manager: self.global_shortcut_manager.clone(), clipboard_manager: self.clipboard_manager.clone(), windows: self.windows.clone(), @@ -2021,8 +1947,12 @@ impl Runtime for Wry { let menu_event_listeners = self.menu_event_listeners.clone(); #[cfg(feature = "system-tray")] let tray_context = self.tray_context.clone(); + + #[cfg(feature = "global-shortcut")] let global_shortcut_manager = self.global_shortcut_manager.clone(); + #[cfg(feature = "global-shortcut")] let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone(); + let clipboard_manager = self.clipboard_manager.clone(); let mut iteration = RunIteration::default(); @@ -2043,7 +1973,9 @@ impl Runtime for Wry { windows: windows.clone(), webview_id_map: webview_id_map.clone(), window_event_listeners: &window_event_listeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: global_shortcut_manager.clone(), + #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: &global_shortcut_manager_handle, clipboard_manager: clipboard_manager.clone(), menu_event_listeners: &menu_event_listeners, @@ -2063,10 +1995,15 @@ impl Runtime for Wry { let web_context = self.web_context; let window_event_listeners = self.window_event_listeners.clone(); let menu_event_listeners = self.menu_event_listeners.clone(); + #[cfg(feature = "system-tray")] let tray_context = self.tray_context; + + #[cfg(feature = "global-shortcut")] let global_shortcut_manager = self.global_shortcut_manager.clone(); + #[cfg(feature = "global-shortcut")] let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone(); + let clipboard_manager = self.clipboard_manager.clone(); self.event_loop.run(move |event, event_loop, control_flow| { @@ -2079,7 +2016,9 @@ impl Runtime for Wry { webview_id_map: webview_id_map.clone(), windows: windows.clone(), window_event_listeners: &window_event_listeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: global_shortcut_manager.clone(), + #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: &global_shortcut_manager_handle, clipboard_manager: clipboard_manager.clone(), menu_event_listeners: &menu_event_listeners, @@ -2097,7 +2036,9 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> { webview_id_map: WebviewIdStore, windows: Arc>>, window_event_listeners: &'a WindowEventListeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: Arc>, + #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle, clipboard_manager: Arc>, menu_event_listeners: &'a MenuEventListeners, @@ -2108,6 +2049,7 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> { struct UserMessageContext<'a> { webview_id_map: WebviewIdStore, window_event_listeners: &'a WindowEventListeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: Arc>, clipboard_manager: Arc>, menu_event_listeners: &'a MenuEventListeners, @@ -2126,6 +2068,7 @@ fn handle_user_message( webview_id_map, window_event_listeners, menu_event_listeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager, clipboard_manager, windows, @@ -2427,44 +2370,10 @@ fn handle_user_message( tray_context.items.lock().unwrap().clear(); } }, - Message::GlobalShortcut(message) => match message { - GlobalShortcutMessage::IsRegistered(accelerator, tx) => tx - .send( - global_shortcut_manager - .lock() - .unwrap() - .is_registered(&accelerator), - ) - .unwrap(), - GlobalShortcutMessage::Register(accelerator, tx) => tx - .send( - global_shortcut_manager - .lock() - .unwrap() - .register(accelerator) - .map(GlobalShortcutWrapper) - .map_err(|e| Error::GlobalShortcut(Box::new(e))), - ) - .unwrap(), - GlobalShortcutMessage::Unregister(shortcut, tx) => tx - .send( - global_shortcut_manager - .lock() - .unwrap() - .unregister(shortcut.0) - .map_err(|e| Error::GlobalShortcut(Box::new(e))), - ) - .unwrap(), - GlobalShortcutMessage::UnregisterAll(tx) => tx - .send( - global_shortcut_manager - .lock() - .unwrap() - .unregister_all() - .map_err(|e| Error::GlobalShortcut(Box::new(e))), - ) - .unwrap(), - }, + #[cfg(feature = "global-shortcut")] + Message::GlobalShortcut(message) => { + handle_global_shortcut_message(message, &global_shortcut_manager) + } Message::Clipboard(message) => match message { ClipboardMessage::WriteText(text, tx) => { clipboard_manager.lock().unwrap().write_text(text); @@ -2495,7 +2404,9 @@ fn handle_event_loop( webview_id_map, windows, window_event_listeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager, + #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle, clipboard_manager, menu_event_listeners, @@ -2523,6 +2434,7 @@ fn handle_event_loop( callback(RunEvent::Exit); } + #[cfg(feature = "global-shortcut")] Event::GlobalShortcutEvent(accelerator_id) => { for (id, handler) in &*global_shortcut_manager_handle.listeners.lock().unwrap() { if accelerator_id == *id { @@ -2701,6 +2613,7 @@ fn handle_event_loop( UserMessageContext { webview_id_map, window_event_listeners, + #[cfg(feature = "global-shortcut")] global_shortcut_manager, clipboard_manager, menu_event_listeners, diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index 7180fad6d..ed75140d9 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -46,3 +46,4 @@ gtk = { version = "0.15", features = [ "v3_20" ] } devtools = [ ] system-tray = [ ] macos-private-api = [ ] +global-shortcut = [ ] diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index 8d5749cb5..ddbc2afe1 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/core/tauri-runtime/src/lib.rs @@ -127,6 +127,7 @@ pub enum Error { #[error("failed to get monitor")] FailedToGetMonitor, /// Global shortcut error. + #[cfg(feature = "global-shortcut")] #[error(transparent)] GlobalShortcut(Box), #[error("Invalid header name: {0}")] @@ -294,6 +295,7 @@ pub trait RuntimeHandle: Debug + Clone + Send + Sync + Sized + 'st } /// A global shortcut manager. +#[cfg(feature = "global-shortcut")] pub trait GlobalShortcutManager: Debug + Clone + Send + Sync { /// Whether the application has registered the given `accelerator`. fn is_registered(&self, accelerator: &str) -> Result; @@ -327,6 +329,7 @@ pub trait Runtime: Debug + Sized + 'static { /// The runtime handle type. type Handle: RuntimeHandle; /// The global shortcut manager type. + #[cfg(feature = "global-shortcut")] type GlobalShortcutManager: GlobalShortcutManager; /// The clipboard manager type. type ClipboardManager: ClipboardManager; @@ -351,6 +354,7 @@ pub trait Runtime: Debug + Sized + 'static { fn handle(&self) -> Self::Handle; /// Gets the global shortcut manager. + #[cfg(feature = "global-shortcut")] fn global_shortcut_manager(&self) -> Self::GlobalShortcutManager; /// Gets the clipboard manager. diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index fe084147e..142760022 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -141,6 +141,7 @@ shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ] fs-extract-api = [ "zip" ] reqwest-client = [ "reqwest", "bytes" ] process-command-api = [ "shared_child", "os_pipe" ] +global-shortcut = [ "tauri-runtime/global-shortcut", "tauri-runtime-wry/global-shortcut" ] dialog = [ "rfd" ] notification = [ "notify-rust" ] cli = [ "clap" ] @@ -193,7 +194,7 @@ fs-remove-dir = [ ] fs-remove-file = [ ] fs-rename-file = [ ] fs-write-file = [ ] -global-shortcut-all = [ ] +global-shortcut-all = [ "global-shortcut" ] http-all = [ "http-request" ] http-request = [ "http-api" ] notification-all = [ "notification", "dialog-ask" ] diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index c00cd5a90..090955cd1 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -287,6 +287,7 @@ impl AssetResolver { pub struct AppHandle { runtime_handle: R::Handle, manager: WindowManager, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: R::GlobalShortcutManager, clipboard_manager: R::ClipboardManager, #[cfg(feature = "system-tray")] @@ -337,6 +338,7 @@ impl Clone for AppHandle { Self { runtime_handle: self.runtime_handle.clone(), manager: self.manager.clone(), + #[cfg(feature = "global-shortcut")] global_shortcut_manager: self.global_shortcut_manager.clone(), clipboard_manager: self.clipboard_manager.clone(), #[cfg(feature = "system-tray")] @@ -442,6 +444,7 @@ impl ManagerBase for AppHandle { pub struct App { runtime: Option, manager: WindowManager, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: R::GlobalShortcutManager, clipboard_manager: R::ClipboardManager, #[cfg(feature = "system-tray")] @@ -510,6 +513,8 @@ macro_rules! shared_app_impl { } /// Gets a copy of the global shortcut manager instance. + #[cfg(feature = "global-shortcut")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "global-shortcut")))] pub fn global_shortcut_manager(&self) -> R::GlobalShortcutManager { self.global_shortcut_manager.clone() } @@ -1233,12 +1238,16 @@ impl Builder { let runtime = R::new()?; let runtime_handle = runtime.handle(); + + #[cfg(feature = "global-shortcut")] let global_shortcut_manager = runtime.global_shortcut_manager(); + let clipboard_manager = runtime.clipboard_manager(); let mut app = App { runtime: Some(runtime), manager: manager.clone(), + #[cfg(feature = "global-shortcut")] global_shortcut_manager: global_shortcut_manager.clone(), clipboard_manager: clipboard_manager.clone(), #[cfg(feature = "system-tray")] @@ -1246,6 +1255,7 @@ impl Builder { handle: AppHandle { runtime_handle, manager, + #[cfg(feature = "global-shortcut")] global_shortcut_manager, clipboard_manager, #[cfg(feature = "system-tray")] diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 74aa936ad..3fcdf1931 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -23,6 +23,7 @@ //! - **http-multipart**: Adds support to `multipart/form-data` requests. //! - **reqwest-client**: Uses `reqwest` as HTTP client on the `http` APIs. Improves performance, but increases the bundle size. //! - **process-command-api**: Enables the [`api::process::Command`] APIs. +//! - **global-shortcut**: Enables the global shortcut APIs. //! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`api::process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website). //! - **dialog**: Enables the [`api::dialog`] module. //! - **notification**: Enables the [`api::notification`] module. @@ -232,7 +233,7 @@ pub use { dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size}, CursorIcon, FileDropEvent, }, - ClipboardManager, GlobalShortcutManager, RunIteration, TrayIcon, UserAttentionType, + ClipboardManager, RunIteration, TrayIcon, UserAttentionType, }, self::state::{State, StateManager}, self::utils::{ diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index d6942f6d2..fcb166a67 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -12,8 +12,8 @@ use tauri_runtime::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, CursorIcon, DetachedWindow, MenuEvent, PendingWindow, WindowEvent, }, - ClipboardManager, Dispatch, EventLoopProxy, GlobalShortcutManager, Result, RunEvent, Runtime, - RuntimeHandle, UserAttentionType, UserEvent, WindowIcon, + ClipboardManager, Dispatch, EventLoopProxy, Result, RunEvent, Runtime, RuntimeHandle, + UserAttentionType, UserEvent, WindowIcon, }; #[cfg(feature = "system-tray")] use tauri_runtime::{ @@ -92,12 +92,14 @@ pub struct MockDispatcher { context: RuntimeContext, } +#[cfg(feature = "global-shortcut")] #[derive(Debug, Clone)] pub struct MockGlobalShortcutManager { context: RuntimeContext, } -impl GlobalShortcutManager for MockGlobalShortcutManager { +#[cfg(feature = "global-shortcut")] +impl tauri_runtime::GlobalShortcutManager for MockGlobalShortcutManager { fn is_registered(&self, accelerator: &str) -> Result { Ok( self @@ -543,6 +545,7 @@ impl EventLoopProxy for EventProxy { #[derive(Debug)] pub struct MockRuntime { pub context: RuntimeContext, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: MockGlobalShortcutManager, clipboard_manager: MockClipboardManager, #[cfg(feature = "system-tray")] @@ -556,6 +559,7 @@ impl MockRuntime { clipboard: Default::default(), }; Self { + #[cfg(feature = "global-shortcut")] global_shortcut_manager: MockGlobalShortcutManager { context: context.clone(), }, @@ -574,6 +578,7 @@ impl MockRuntime { impl Runtime for MockRuntime { type Dispatcher = MockDispatcher; type Handle = MockRuntimeHandle; + #[cfg(feature = "global-shortcut")] type GlobalShortcutManager = MockGlobalShortcutManager; type ClipboardManager = MockClipboardManager; #[cfg(feature = "system-tray")] @@ -599,6 +604,7 @@ impl Runtime for MockRuntime { } } + #[cfg(feature = "global-shortcut")] fn global_shortcut_manager(&self) -> Self::GlobalShortcutManager { self.global_shortcut_manager.clone() } From 24e4ff208ee0fe1a4cc5b10667ea0922ac63dfb5 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 24 Apr 2022 11:50:42 -0700 Subject: [PATCH 3/7] refactor(core): add clipboard Cargo feature, enhancing binary size (#3957) --- .changes/clipboard-feature.md | 7 ++ core/tauri-runtime-wry/Cargo.toml | 1 + core/tauri-runtime-wry/src/clipboard.rs | 62 ++++++++++++++ core/tauri-runtime-wry/src/lib.rs | 103 +++++++++++++----------- core/tauri-runtime/Cargo.toml | 1 + core/tauri-runtime/src/lib.rs | 3 + core/tauri/Cargo.toml | 5 +- core/tauri/src/app.rs | 8 ++ core/tauri/src/lib.rs | 3 +- core/tauri/src/test/mock_runtime.rs | 12 ++- 10 files changed, 150 insertions(+), 55 deletions(-) create mode 100644 .changes/clipboard-feature.md create mode 100644 core/tauri-runtime-wry/src/clipboard.rs diff --git a/.changes/clipboard-feature.md b/.changes/clipboard-feature.md new file mode 100644 index 000000000..7be1cbf66 --- /dev/null +++ b/.changes/clipboard-feature.md @@ -0,0 +1,7 @@ +--- +"tauri": patch +"tauri-runtime": minor +"tauri-runtime-wry": minor +--- + +**Breaking change::* Added the `clipboard` Cargo feature. diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 869342cd2..4865778ac 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -42,3 +42,4 @@ objc-exception = [ "wry/objc-exception" ] gtk-tray = [ "wry/gtk-tray" ] ayatana-tray = [ "wry/ayatana-tray" ] global-shortcut = [ "tauri-runtime/global-shortcut" ] +clipboard = [ "tauri-runtime/clipboard" ] diff --git a/core/tauri-runtime-wry/src/clipboard.rs b/core/tauri-runtime-wry/src/clipboard.rs new file mode 100644 index 000000000..e0315c556 --- /dev/null +++ b/core/tauri-runtime-wry/src/clipboard.rs @@ -0,0 +1,62 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! Clipboard implementation. + +use crate::{getter, Context, Message}; + +use std::sync::{ + mpsc::{channel, Sender}, + Arc, Mutex, +}; + +use tauri_runtime::{ClipboardManager, Result, UserEvent}; +pub use wry::application::clipboard::Clipboard; + +#[derive(Debug, Clone)] +pub enum ClipboardMessage { + WriteText(String, Sender<()>), + ReadText(Sender>), +} + +#[derive(Debug, Clone)] +pub struct ClipboardManagerWrapper { + pub context: Context, +} + +// SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Sync for ClipboardManagerWrapper {} + +impl ClipboardManager for ClipboardManagerWrapper { + fn read_text(&self) -> Result> { + let (tx, rx) = channel(); + getter!(self, rx, Message::Clipboard(ClipboardMessage::ReadText(tx))) + } + + fn write_text>(&mut self, text: V) -> Result<()> { + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::Clipboard(ClipboardMessage::WriteText(text.into(), tx)) + )?; + Ok(()) + } +} + +pub fn handle_clipboard_message( + message: ClipboardMessage, + clipboard_manager: &Arc>, +) { + match message { + ClipboardMessage::WriteText(text, tx) => { + clipboard_manager.lock().unwrap().write_text(text); + tx.send(()).unwrap(); + } + ClipboardMessage::ReadText(tx) => tx + .send(clipboard_manager.lock().unwrap().read_text()) + .unwrap(), + } +} diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index ca1a39bd0..b230a5b84 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -16,8 +16,8 @@ use tauri_runtime::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, CursorIcon, DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent, }, - ClipboardManager, Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Result, RunEvent, - RunIteration, Runtime, RuntimeHandle, UserAttentionType, UserEvent, WindowIcon, + Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Result, RunEvent, RunIteration, + Runtime, RuntimeHandle, UserAttentionType, UserEvent, WindowIcon, }; use tauri_runtime::window::MenuEvent; @@ -43,7 +43,6 @@ use tauri_utils::{config::WindowConfig, Theme}; use uuid::Uuid; use wry::{ application::{ - clipboard::Clipboard, dpi::{ LogicalPosition as WryLogicalPosition, LogicalSize as WryLogicalSize, PhysicalPosition as WryPhysicalPosition, PhysicalSize as WryPhysicalSize, @@ -111,6 +110,11 @@ mod global_shortcut; #[cfg(feature = "global-shortcut")] use global_shortcut::*; +#[cfg(feature = "clipboard")] +mod clipboard; +#[cfg(feature = "clipboard")] +use clipboard::*; + type WebContextStore = Arc, WebContext>>>; // window type WindowEventHandler = Box; @@ -138,7 +142,7 @@ impl WebviewIdStore { macro_rules! getter { ($self: ident, $rx: expr, $message: expr) => {{ crate::send_user_message(&$self.context, $message)?; - $rx.recv().map_err(|_| Error::FailedToReceiveMessage) + $rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage) }}; } @@ -159,6 +163,7 @@ fn send_user_message(context: &Context, message: Message) -> window_event_listeners: &context.window_event_listeners, #[cfg(feature = "global-shortcut")] global_shortcut_manager: context.main_thread.global_shortcut_manager.clone(), + #[cfg(feature = "clipboard")] clipboard_manager: context.main_thread.clipboard_manager.clone(), menu_event_listeners: &context.menu_event_listeners, windows: context.main_thread.windows.clone(), @@ -240,6 +245,7 @@ struct DispatcherMainThreadContext { web_context: WebContextStore, #[cfg(feature = "global-shortcut")] global_shortcut_manager: Arc>, + #[cfg(feature = "clipboard")] clipboard_manager: Arc>, windows: Arc>>, #[cfg(feature = "system-tray")] @@ -464,32 +470,6 @@ impl From for NativeImageWrapper { } } -#[derive(Debug, Clone)] -pub struct ClipboardManagerWrapper { - context: Context, -} - -// SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`. -#[allow(clippy::non_send_fields_in_send_ty)] -unsafe impl Sync for ClipboardManagerWrapper {} - -impl ClipboardManager for ClipboardManagerWrapper { - fn read_text(&self) -> Result> { - let (tx, rx) = channel(); - getter!(self, rx, Message::Clipboard(ClipboardMessage::ReadText(tx))) - } - - fn write_text>(&mut self, text: V) -> Result<()> { - let (tx, rx) = channel(); - getter!( - self, - rx, - Message::Clipboard(ClipboardMessage::WriteText(text.into(), tx)) - )?; - Ok(()) - } -} - /// Wrapper around a [`wry::application::window::Icon`] that can be created from an [`WindowIcon`]. pub struct WryIcon(WryWindowIcon); @@ -1078,12 +1058,6 @@ pub enum TrayMessage { Close, } -#[derive(Debug, Clone)] -pub enum ClipboardMessage { - WriteText(String, Sender<()>), - ReadText(Sender>), -} - pub type CreateWebviewClosure = Box< dyn FnOnce(&EventLoopWindowTarget>, &WebContextStore) -> Result + Send, >; @@ -1102,6 +1076,7 @@ pub enum Message { ), #[cfg(feature = "global-shortcut")] GlobalShortcut(GlobalShortcutMessage), + #[cfg(feature = "clipboard")] Clipboard(ClipboardMessage), UserEvent(T), } @@ -1115,6 +1090,7 @@ impl Clone for Message { Self::Tray(m) => Self::Tray(m.clone()), #[cfg(feature = "global-shortcut")] Self::GlobalShortcut(m) => Self::GlobalShortcut(m.clone()), + #[cfg(feature = "clipboard")] Self::Clipboard(m) => Self::Clipboard(m.clone()), Self::UserEvent(t) => Self::UserEvent(t.clone()), _ => unimplemented!(), @@ -1590,12 +1566,17 @@ impl EventLoopProxy for EventProxy { /// A Tauri [`Runtime`] wrapper around wry. pub struct Wry { main_thread_id: ThreadId, + #[cfg(feature = "global-shortcut")] global_shortcut_manager: Arc>, #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: GlobalShortcutManagerHandle, + + #[cfg(feature = "clipboard")] clipboard_manager: Arc>, + #[cfg(feature = "clipboard")] clipboard_manager_handle: ClipboardManagerWrapper, + event_loop: EventLoop>, windows: Arc>>, webview_id_map: WebviewIdStore, @@ -1610,19 +1591,24 @@ impl fmt::Debug for Wry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("Wry"); d.field("main_thread_id", &self.main_thread_id) - .field("clipboard_manager", &self.clipboard_manager) - .field("clipboard_manager_handle", &self.clipboard_manager_handle) .field("event_loop", &self.event_loop) .field("windows", &self.windows) .field("web_context", &self.web_context); + #[cfg(feature = "system-tray")] d.field("tray_context", &self.tray_context); + #[cfg(feature = "global-shortcut")] d.field("global_shortcut_manager", &self.global_shortcut_manager) .field( "global_shortcut_manager_handle", &self.global_shortcut_manager_handle, ); + + #[cfg(feature = "clipboard")] + d.field("clipboard_manager", &self.clipboard_manager) + .field("clipboard_manager_handle", &self.clipboard_manager_handle); + d.finish() } } @@ -1703,14 +1689,15 @@ impl RuntimeHandle for WryHandle { impl Wry { fn init(event_loop: EventLoop>) -> Result { - let proxy = event_loop.create_proxy(); let main_thread_id = current_thread().id(); let web_context = WebContextStore::default(); #[cfg(feature = "global-shortcut")] let global_shortcut_manager = Arc::new(Mutex::new(WryShortcutManager::new(&event_loop))); + #[cfg(feature = "clipboard")] let clipboard_manager = Arc::new(Mutex::new(Clipboard::new())); + let windows = Arc::new(Mutex::new(HashMap::default())); let webview_id_map = WebviewIdStore::default(); let window_event_listeners = WindowEventListeners::default(); @@ -1719,10 +1706,11 @@ impl Wry { #[cfg(feature = "system-tray")] let tray_context = TrayContext::default(); + #[allow(unused_variables)] let event_loop_context = Context { webview_id_map: webview_id_map.clone(), main_thread_id, - proxy, + proxy: event_loop.create_proxy(), window_event_listeners: window_event_listeners.clone(), menu_event_listeners: menu_event_listeners.clone(), main_thread: DispatcherMainThreadContext { @@ -1730,6 +1718,7 @@ impl Wry { web_context: web_context.clone(), #[cfg(feature = "global-shortcut")] global_shortcut_manager: global_shortcut_manager.clone(), + #[cfg(feature = "clipboard")] clipboard_manager: clipboard_manager.clone(), windows: windows.clone(), #[cfg(feature = "system-tray")] @@ -1740,6 +1729,7 @@ impl Wry { #[cfg(feature = "global-shortcut")] let global_shortcut_listeners = GlobalShortcutListeners::default(); + #[cfg(feature = "clipboard")] #[allow(clippy::redundant_clone)] let clipboard_manager_handle = ClipboardManagerWrapper { context: event_loop_context.clone(), @@ -1747,6 +1737,7 @@ impl Wry { Ok(Self { main_thread_id, + #[cfg(feature = "global-shortcut")] global_shortcut_manager, #[cfg(feature = "global-shortcut")] @@ -1755,8 +1746,12 @@ impl Wry { shortcuts: Default::default(), listeners: global_shortcut_listeners, }, + + #[cfg(feature = "clipboard")] clipboard_manager, + #[cfg(feature = "clipboard")] clipboard_manager_handle, + event_loop, windows, webview_id_map, @@ -1772,11 +1767,16 @@ impl Wry { impl Runtime for Wry { type Dispatcher = WryDispatcher; type Handle = WryHandle; + #[cfg(feature = "global-shortcut")] type GlobalShortcutManager = GlobalShortcutManagerHandle; + + #[cfg(feature = "clipboard")] type ClipboardManager = ClipboardManagerWrapper; + #[cfg(feature = "system-tray")] type TrayHandler = SystemTrayHandle; + type EventLoopProxy = EventProxy; fn new() -> Result { @@ -1811,6 +1811,7 @@ impl Runtime for Wry { web_context: self.web_context.clone(), #[cfg(feature = "global-shortcut")] global_shortcut_manager: self.global_shortcut_manager.clone(), + #[cfg(feature = "clipboard")] clipboard_manager: self.clipboard_manager.clone(), windows: self.windows.clone(), #[cfg(feature = "system-tray")] @@ -1825,6 +1826,7 @@ impl Runtime for Wry { self.global_shortcut_manager_handle.clone() } + #[cfg(feature = "clipboard")] fn clipboard_manager(&self) -> Self::ClipboardManager { self.clipboard_manager_handle.clone() } @@ -1847,6 +1849,7 @@ impl Runtime for Wry { web_context: self.web_context.clone(), #[cfg(feature = "global-shortcut")] global_shortcut_manager: self.global_shortcut_manager.clone(), + #[cfg(feature = "clipboard")] clipboard_manager: self.clipboard_manager.clone(), windows: self.windows.clone(), #[cfg(feature = "system-tray")] @@ -1953,6 +1956,7 @@ impl Runtime for Wry { #[cfg(feature = "global-shortcut")] let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone(); + #[cfg(feature = "clipboard")] let clipboard_manager = self.clipboard_manager.clone(); let mut iteration = RunIteration::default(); @@ -1977,6 +1981,7 @@ impl Runtime for Wry { global_shortcut_manager: global_shortcut_manager.clone(), #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: &global_shortcut_manager_handle, + #[cfg(feature = "clipboard")] clipboard_manager: clipboard_manager.clone(), menu_event_listeners: &menu_event_listeners, #[cfg(feature = "system-tray")] @@ -2004,6 +2009,7 @@ impl Runtime for Wry { #[cfg(feature = "global-shortcut")] let global_shortcut_manager_handle = self.global_shortcut_manager_handle.clone(); + #[cfg(feature = "clipboard")] let clipboard_manager = self.clipboard_manager.clone(); self.event_loop.run(move |event, event_loop, control_flow| { @@ -2020,6 +2026,7 @@ impl Runtime for Wry { global_shortcut_manager: global_shortcut_manager.clone(), #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: &global_shortcut_manager_handle, + #[cfg(feature = "clipboard")] clipboard_manager: clipboard_manager.clone(), menu_event_listeners: &menu_event_listeners, #[cfg(feature = "system-tray")] @@ -2040,6 +2047,7 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> { global_shortcut_manager: Arc>, #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle, + #[cfg(feature = "clipboard")] clipboard_manager: Arc>, menu_event_listeners: &'a MenuEventListeners, #[cfg(feature = "system-tray")] @@ -2051,6 +2059,7 @@ struct UserMessageContext<'a> { window_event_listeners: &'a WindowEventListeners, #[cfg(feature = "global-shortcut")] global_shortcut_manager: Arc>, + #[cfg(feature = "clipboard")] clipboard_manager: Arc>, menu_event_listeners: &'a MenuEventListeners, windows: Arc>>, @@ -2070,6 +2079,7 @@ fn handle_user_message( menu_event_listeners, #[cfg(feature = "global-shortcut")] global_shortcut_manager, + #[cfg(feature = "clipboard")] clipboard_manager, windows, #[cfg(feature = "system-tray")] @@ -2374,15 +2384,8 @@ fn handle_user_message( Message::GlobalShortcut(message) => { handle_global_shortcut_message(message, &global_shortcut_manager) } - Message::Clipboard(message) => match message { - ClipboardMessage::WriteText(text, tx) => { - clipboard_manager.lock().unwrap().write_text(text); - tx.send(()).unwrap(); - } - ClipboardMessage::ReadText(tx) => tx - .send(clipboard_manager.lock().unwrap().read_text()) - .unwrap(), - }, + #[cfg(feature = "clipboard")] + Message::Clipboard(message) => handle_clipboard_message(message, &clipboard_manager), Message::UserEvent(_) => (), } @@ -2408,6 +2411,7 @@ fn handle_event_loop( global_shortcut_manager, #[cfg(feature = "global-shortcut")] global_shortcut_manager_handle, + #[cfg(feature = "clipboard")] clipboard_manager, menu_event_listeners, #[cfg(feature = "system-tray")] @@ -2615,6 +2619,7 @@ fn handle_event_loop( window_event_listeners, #[cfg(feature = "global-shortcut")] global_shortcut_manager, + #[cfg(feature = "clipboard")] clipboard_manager, menu_event_listeners, windows, diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index ed75140d9..f2603c028 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -47,3 +47,4 @@ devtools = [ ] system-tray = [ ] macos-private-api = [ ] global-shortcut = [ ] +clipboard = [ ] diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index ddbc2afe1..bf4948dda 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/core/tauri-runtime/src/lib.rs @@ -311,6 +311,7 @@ pub trait GlobalShortcutManager: Debug + Clone + Send + Sync { } /// Clipboard manager. +#[cfg(feature = "clipboard")] pub trait ClipboardManager: Debug + Clone + Send + Sync { /// Writes the text into the clipboard as plain text. fn write_text>(&mut self, text: T) -> Result<()>; @@ -332,6 +333,7 @@ pub trait Runtime: Debug + Sized + 'static { #[cfg(feature = "global-shortcut")] type GlobalShortcutManager: GlobalShortcutManager; /// The clipboard manager type. + #[cfg(feature = "clipboard")] type ClipboardManager: ClipboardManager; /// The tray handler type. #[cfg(feature = "system-tray")] @@ -358,6 +360,7 @@ pub trait Runtime: Debug + Sized + 'static { fn global_shortcut_manager(&self) -> Self::GlobalShortcutManager; /// Gets the clipboard manager. + #[cfg(feature = "clipboard")] fn clipboard_manager(&self) -> Self::ClipboardManager; /// Create a new webview window. diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 142760022..7bac7e299 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -142,6 +142,7 @@ fs-extract-api = [ "zip" ] reqwest-client = [ "reqwest", "bytes" ] process-command-api = [ "shared_child", "os_pipe" ] global-shortcut = [ "tauri-runtime/global-shortcut", "tauri-runtime-wry/global-shortcut" ] +clipboard = [ "tauri-runtime/clipboard", "tauri-runtime-wry/clipboard" ] dialog = [ "rfd" ] notification = [ "notify-rust" ] cli = [ "clap" ] @@ -168,8 +169,8 @@ api-all = [ "window-all" ] clipboard-all = [ "clipboard-write-text", "clipboard-read-text" ] -clipboard-read-text = [ ] -clipboard-write-text = [ ] +clipboard-read-text = [ "clipboard" ] +clipboard-write-text = [ "clipboard" ] dialog-all = [ "dialog-open", "dialog-save", "dialog-message", "dialog-ask" ] dialog-ask = [ "dialog" ] dialog-confirm = [ "dialog" ] diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 090955cd1..f42aad53e 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -289,6 +289,7 @@ pub struct AppHandle { manager: WindowManager, #[cfg(feature = "global-shortcut")] global_shortcut_manager: R::GlobalShortcutManager, + #[cfg(feature = "clipboard")] clipboard_manager: R::ClipboardManager, #[cfg(feature = "system-tray")] tray_handle: Option>, @@ -340,6 +341,7 @@ impl Clone for AppHandle { manager: self.manager.clone(), #[cfg(feature = "global-shortcut")] global_shortcut_manager: self.global_shortcut_manager.clone(), + #[cfg(feature = "clipboard")] clipboard_manager: self.clipboard_manager.clone(), #[cfg(feature = "system-tray")] tray_handle: self.tray_handle.clone(), @@ -446,6 +448,7 @@ pub struct App { manager: WindowManager, #[cfg(feature = "global-shortcut")] global_shortcut_manager: R::GlobalShortcutManager, + #[cfg(feature = "clipboard")] clipboard_manager: R::ClipboardManager, #[cfg(feature = "system-tray")] tray_handle: Option>, @@ -520,6 +523,8 @@ macro_rules! shared_app_impl { } /// Gets a copy of the clipboard manager instance. + #[cfg(feature = "clipboard")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "clipboard")))] pub fn clipboard_manager(&self) -> R::ClipboardManager { self.clipboard_manager.clone() } @@ -1242,6 +1247,7 @@ impl Builder { #[cfg(feature = "global-shortcut")] let global_shortcut_manager = runtime.global_shortcut_manager(); + #[cfg(feature = "clipboard")] let clipboard_manager = runtime.clipboard_manager(); let mut app = App { @@ -1249,6 +1255,7 @@ impl Builder { manager: manager.clone(), #[cfg(feature = "global-shortcut")] global_shortcut_manager: global_shortcut_manager.clone(), + #[cfg(feature = "clipboard")] clipboard_manager: clipboard_manager.clone(), #[cfg(feature = "system-tray")] tray_handle: None, @@ -1257,6 +1264,7 @@ impl Builder { manager, #[cfg(feature = "global-shortcut")] global_shortcut_manager, + #[cfg(feature = "clipboard")] clipboard_manager, #[cfg(feature = "system-tray")] tray_handle: None, diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 3fcdf1931..8d8ca25f0 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -24,6 +24,7 @@ //! - **reqwest-client**: Uses `reqwest` as HTTP client on the `http` APIs. Improves performance, but increases the bundle size. //! - **process-command-api**: Enables the [`api::process::Command`] APIs. //! - **global-shortcut**: Enables the global shortcut APIs. +//! - **clipboard**: Enables the clipboard APIs. //! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`api::process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website). //! - **dialog**: Enables the [`api::dialog`] module. //! - **notification**: Enables the [`api::notification`] module. @@ -233,7 +234,7 @@ pub use { dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size}, CursorIcon, FileDropEvent, }, - ClipboardManager, RunIteration, TrayIcon, UserAttentionType, + RunIteration, TrayIcon, UserAttentionType, }, self::state::{State, StateManager}, self::utils::{ diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index fcb166a67..6088ae4b0 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -12,8 +12,8 @@ use tauri_runtime::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, CursorIcon, DetachedWindow, MenuEvent, PendingWindow, WindowEvent, }, - ClipboardManager, Dispatch, EventLoopProxy, Result, RunEvent, Runtime, RuntimeHandle, - UserAttentionType, UserEvent, WindowIcon, + Dispatch, EventLoopProxy, Result, RunEvent, Runtime, RuntimeHandle, UserAttentionType, UserEvent, + WindowIcon, }; #[cfg(feature = "system-tray")] use tauri_runtime::{ @@ -132,12 +132,14 @@ impl tauri_runtime::GlobalShortcutManager for MockGlobalShortcutManager { } } +#[cfg(feature = "clipboard")] #[derive(Debug, Clone)] pub struct MockClipboardManager { context: RuntimeContext, } -impl ClipboardManager for MockClipboardManager { +#[cfg(feature = "clipboard")] +impl tauri_runtime::ClipboardManager for MockClipboardManager { fn write_text>(&mut self, text: T) -> Result<()> { self.context.clipboard.lock().unwrap().replace(text.into()); Ok(()) @@ -547,6 +549,7 @@ pub struct MockRuntime { pub context: RuntimeContext, #[cfg(feature = "global-shortcut")] global_shortcut_manager: MockGlobalShortcutManager, + #[cfg(feature = "clipboard")] clipboard_manager: MockClipboardManager, #[cfg(feature = "system-tray")] tray_handler: MockTrayHandler, @@ -563,6 +566,7 @@ impl MockRuntime { global_shortcut_manager: MockGlobalShortcutManager { context: context.clone(), }, + #[cfg(feature = "clipboard")] clipboard_manager: MockClipboardManager { context: context.clone(), }, @@ -580,6 +584,7 @@ impl Runtime for MockRuntime { type Handle = MockRuntimeHandle; #[cfg(feature = "global-shortcut")] type GlobalShortcutManager = MockGlobalShortcutManager; + #[cfg(feature = "clipboard")] type ClipboardManager = MockClipboardManager; #[cfg(feature = "system-tray")] type TrayHandler = MockTrayHandler; @@ -609,6 +614,7 @@ impl Runtime for MockRuntime { self.global_shortcut_manager.clone() } + #[cfg(feature = "clipboard")] fn clipboard_manager(&self) -> Self::ClipboardManager { self.clipboard_manager.clone() } From ed467c275b56317e4cbd9cb6a27cfecb999aef46 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 24 Apr 2022 15:18:22 -0700 Subject: [PATCH 4/7] perf: completely remove endpoints if none of its APIs is allowlisted (#3958) --- core/tauri-runtime-wry/src/lib.rs | 6 +- core/tauri/Cargo.toml | 3 +- core/tauri/build.rs | 248 +++++++++++++++++------------ core/tauri/src/api/file.rs | 2 + core/tauri/src/endpoints.rs | 51 +++++- core/tauri/src/endpoints/window.rs | 123 +++++++------- core/tauri/src/lib.rs | 91 ++++++++++- core/tauri/src/window.rs | 8 +- 8 files changed, 364 insertions(+), 168 deletions(-) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index b230a5b84..12a65d62f 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -141,8 +141,10 @@ impl WebviewIdStore { #[macro_export] macro_rules! getter { ($self: ident, $rx: expr, $message: expr) => {{ - crate::send_user_message(&$self.context, $message)?; - $rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage) + $crate::send_user_message(&$self.context, $message)?; + $rx + .recv() + .map_err(|_| $crate::Error::FailedToReceiveMessage) }}; } diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 7bac7e299..bccef72d8 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -104,7 +104,8 @@ version = "0.30.0" features = [ "Win32_Foundation" ] [build-dependencies] -cfg_aliases = "0.1.1" +heck = "0.4" +once_cell = "1.10" [dev-dependencies] mockito = "0.31" diff --git a/core/tauri/build.rs b/core/tauri/build.rs index e0a798284..0288c5626 100644 --- a/core/tauri/build.rs +++ b/core/tauri/build.rs @@ -2,108 +2,160 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use cfg_aliases::cfg_aliases; +use heck::ToSnakeCase; +use once_cell::sync::OnceCell; -fn main() { - cfg_aliases! { - custom_protocol: { feature = "custom-protocol" }, - dev: { not(feature = "custom-protocol") }, - updater: { any(feature = "updater", feature = "__updater-docs") }, +use std::{path::Path, sync::Mutex}; - api_all: { feature = "api-all" }, +static CHECKED_FEATURES: OnceCell>> = OnceCell::new(); - // fs - fs_all: { any(api_all, feature = "fs-all") }, - fs_read_file: { any(fs_all, feature = "fs-read-file") }, - fs_write_file: { any(fs_all, feature = "fs-write-file") }, - fs_write_binary_file: { any(fs_all, feature = "fs-write-binary-file") }, - fs_read_dir: { any(fs_all, feature = "fs-read-dir") }, - fs_copy_file: { any(fs_all, feature = "fs-copy-file") }, - fs_create_dir: { any(fs_all, feature = "fs-create_dir") }, - fs_remove_dir: { any(fs_all, feature = "fs-remove-dir") }, - fs_remove_file: { any(fs_all, feature = "fs-remove-file") }, - fs_rename_file: { any(fs_all, feature = "fs-rename-file") }, +// checks if the given Cargo feature is enabled. +fn has_feature(feature: &str) -> bool { + CHECKED_FEATURES + .get_or_init(Default::default) + .lock() + .unwrap() + .push(feature.to_string()); - // window - window_all: { any(api_all, feature = "window-all") }, - window_create: { any(window_all, feature = "window-create") }, - window_center: { any(window_all, feature = "window-center") }, - window_request_user_attention: { any(window_all, feature = "window-request-user-attention") }, - window_set_resizable: { any(window_all, feature = "window-set-resizable") }, - window_set_title: { any(window_all, feature = "window-set-title") }, - window_maximize: { any(window_all, feature = "window-maximize") }, - window_unmaximize: { any(window_all, feature = "window-unmaximize") }, - window_minimize: { any(window_all, feature = "window-minimize") }, - window_unminimize: { any(window_all, feature = "window-unminimize") }, - window_show: { any(window_all, feature = "window-show") }, - window_hide: { any(window_all, feature = "window-hide") }, - window_close: { any(window_all, feature = "window-close") }, - window_set_decorations: { any(window_all, feature = "window-set-decorations") }, - window_set_always_on_top: { any(window_all, feature = "window-set-always-on-top") }, - window_set_size: { any(window_all, feature = "window-set-size") }, - window_set_min_size: { any(window_all, feature = "window-set-min-size") }, - window_set_max_size: { any(window_all, feature = "window-set-max-size") }, - window_set_position: { any(window_all, feature = "window-set-position") }, - window_set_fullscreen: { any(window_all, feature = "window-set-fullscreen") }, - window_set_focus: { any(window_all, feature = "window-set-focus") }, - window_set_icon: { any(window_all, feature = "window-set-icon") }, - window_set_skip_taskbar: { any(window_all, feature = "window-set-skip-taskbar") }, - window_set_cursor_grab: { any(window_all, feature = "window-set-cursor-grab") }, - window_set_cursor_visible: { any(window_all, feature = "window-set-cursor-visible") }, - window_set_cursor_icon: { any(window_all, feature = "window-set-cursor-icon") }, - window_set_cursor_position: { any(window_all, feature = "window-set-cursor-position") }, - window_start_dragging: { any(window_all, feature = "window-start-dragging") }, - window_print: { any(window_all, feature = "window-print") }, + // when a feature is enabled, Cargo sets the `CARGO_FEATURE_-` +// and aliased as `_`. +// +// The `-all` feature is also aliased to `_all`. +// +// If any of the features is enabled, the `_any` alias is created. +// +// Note that both `module` and `apis` strings must be written in kebab case. +fn alias_module(module: &str, apis: &[&str], api_all: bool) { + let all_feature_name = format!("{}-all", module); + let all = api_all || has_feature(&all_feature_name); + alias(&all_feature_name.to_snake_case(), all); + + let mut any = all; + + for api in apis { + let has = all || has_feature(&format!("{}-{}", module, api)); + alias( + &format!("{}_{}", module.to_snake_case(), api.to_snake_case()), + has, + ); + any = any || has; + } + + alias(&format!("{}_any", module.to_snake_case()), any); +} diff --git a/core/tauri/src/api/file.rs b/core/tauri/src/api/file.rs index 67c4dbc7e..4de9f8b0e 100644 --- a/core/tauri/src/api/file.rs +++ b/core/tauri/src/api/file.rs @@ -34,10 +34,12 @@ impl SafePathBuf { } } + #[allow(dead_code)] pub unsafe fn new_unchecked(path: std::path::PathBuf) -> Self { Self(path) } + #[allow(dead_code)] pub fn display(&self) -> Display<'_> { self.0.display() } diff --git a/core/tauri/src/endpoints.rs b/core/tauri/src/endpoints.rs index af79c43cc..2c3444715 100644 --- a/core/tauri/src/endpoints.rs +++ b/core/tauri/src/endpoints.rs @@ -13,17 +13,27 @@ use serde_json::Value as JsonValue; use std::sync::Arc; mod app; +#[cfg(cli)] mod cli; +#[cfg(clipboard_any)] mod clipboard; +#[cfg(dialog_any)] mod dialog; mod event; +#[cfg(fs_any)] mod file_system; +#[cfg(global_shortcut_any)] mod global_shortcut; +#[cfg(http_any)] mod http; mod notification; +#[cfg(os_any)] mod operating_system; +#[cfg(path_any)] mod path; +#[cfg(process_any)] mod process; +#[cfg(shell_any)] mod shell; mod window; @@ -62,18 +72,28 @@ impl From for InvokeResponse { #[serde(tag = "module", content = "message")] enum Module { App(app::Cmd), + #[cfg(process_any)] Process(process::Cmd), + #[cfg(fs_any)] Fs(file_system::Cmd), + #[cfg(os_any)] Os(operating_system::Cmd), + #[cfg(path_any)] Path(path::Cmd), Window(Box), + #[cfg(shell_any)] Shell(shell::Cmd), Event(event::Cmd), + #[cfg(dialog_any)] Dialog(dialog::Cmd), + #[cfg(cli)] Cli(cli::Cmd), Notification(notification::Cmd), + #[cfg(http_any)] Http(http::Cmd), + #[cfg(global_shortcut_any)] GlobalShortcut(global_shortcut::Cmd), + #[cfg(clipboard_any)] Clipboard(clipboard::Cmd), } @@ -97,24 +117,28 @@ impl Module { .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(process_any)] Self::Process(cmd) => resolver.respond_async(async move { cmd .run(context) .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(fs_any)] Self::Fs(cmd) => resolver.respond_async(async move { cmd .run(context) .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(path_any)] Self::Path(cmd) => resolver.respond_async(async move { cmd .run(context) .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(os_any)] Self::Os(cmd) => resolver.respond_async(async move { cmd .run(context) @@ -128,6 +152,7 @@ impl Module { .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(shell_any)] Self::Shell(cmd) => resolver.respond_async(async move { cmd .run(context) @@ -140,12 +165,14 @@ impl Module { .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(dialog_any)] Self::Dialog(cmd) => resolver.respond_async(async move { cmd .run(context) .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(cli)] Self::Cli(cmd) => resolver.respond_async(async move { cmd .run(context) @@ -158,6 +185,7 @@ impl Module { .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(http_any)] Self::Http(cmd) => resolver.respond_async(async move { cmd .run(context) @@ -165,12 +193,14 @@ impl Module { .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(global_shortcut_any)] Self::GlobalShortcut(cmd) => resolver.respond_async(async move { cmd .run(context) .and_then(|r| r.json) .map_err(InvokeError::from_anyhow) }), + #[cfg(clipboard_any)] Self::Clipboard(cmd) => resolver.respond_async(async move { cmd .run(context) @@ -195,11 +225,28 @@ pub(crate) fn handle( } = message; if let JsonValue::Object(ref mut obj) = payload { - obj.insert("module".to_string(), JsonValue::String(module)); + obj.insert("module".to_string(), JsonValue::String(module.clone())); } match serde_json::from_value::(payload) { Ok(module) => module.run(window, resolver, config, package_info.clone()), - Err(e) => resolver.reject(e.to_string()), + Err(e) => { + let message = e.to_string(); + if message.starts_with("unknown variant") { + let mut s = message.split('`'); + s.next(); + if let Some(unknown_variant_name) = s.next() { + if unknown_variant_name == module { + return resolver.reject(format!( + "The `{}` module is not enabled. You must enable one of its APIs in the allowlist.", + module + )); + } else if module == "Window" { + return resolver.reject(window::into_allowlist_error(unknown_variant_name).to_string()); + } + } + } + resolver.reject(message); + } } } diff --git a/core/tauri/src/endpoints/window.rs b/core/tauri/src/endpoints/window.rs index 85e69d689..0fdadeb94 100644 --- a/core/tauri/src/endpoints/window.rs +++ b/core/tauri/src/endpoints/window.rs @@ -72,38 +72,67 @@ pub enum WindowManagerCmd { AvailableMonitors, Theme, // Setters + #[cfg(window_center)] Center, + #[cfg(window_request_user_attention)] RequestUserAttention(Option), + #[cfg(window_set_resizable)] SetResizable(bool), + #[cfg(window_set_title)] SetTitle(String), + #[cfg(window_maximize)] Maximize, + #[cfg(window_unmaximize)] Unmaximize, + #[cfg(all(window_maximize, window_unmaximize))] ToggleMaximize, + #[cfg(window_minimize)] Minimize, + #[cfg(window_unminimize)] Unminimize, + #[cfg(window_show)] Show, + #[cfg(window_hide)] Hide, + #[cfg(window_close)] Close, + #[cfg(window_set_decorations)] SetDecorations(bool), + #[cfg(window_set_always_on_top)] #[serde(rename_all = "camelCase")] SetAlwaysOnTop(bool), + #[cfg(window_set_size)] SetSize(Size), + #[cfg(window_set_min_size)] SetMinSize(Option), + #[cfg(window_set_max_size)] SetMaxSize(Option), + #[cfg(window_set_position)] SetPosition(Position), + #[cfg(window_set_fullscreen)] SetFullscreen(bool), + #[cfg(window_set_focus)] SetFocus, + #[cfg(window_set_icon)] SetIcon { icon: IconDto, }, + #[cfg(window_set_skip_taskbar)] SetSkipTaskbar(bool), + #[cfg(window_set_cursor_grab)] SetCursorGrab(bool), + #[cfg(window_set_cursor_visible)] SetCursorVisible(bool), + #[cfg(window_set_cursor_icon)] SetCursorIcon(CursorIcon), + #[cfg(window_set_cursor_position)] SetCursorPosition(Position), + #[cfg(window_start_dragging)] StartDragging, + #[cfg(window_print)] Print, // internals + #[cfg(all(window_maximize, window_unmaximize))] #[serde(rename = "__toggleMaximize")] InternalToggleMaximize, #[cfg(any(debug_assertions, feature = "devtools"))] @@ -111,61 +140,45 @@ pub enum WindowManagerCmd { InternalToggleDevtools, } -impl WindowManagerCmd { - fn into_allowlist_error(self) -> crate::Error { - match self { - Self::Center => crate::Error::ApiNotAllowlisted("window > center".to_string()), - Self::RequestUserAttention(_) => { - crate::Error::ApiNotAllowlisted("window > requestUserAttention".to_string()) - } - Self::SetResizable(_) => crate::Error::ApiNotAllowlisted("window > setResizable".to_string()), - Self::SetTitle(_) => crate::Error::ApiNotAllowlisted("window > setTitle".to_string()), - Self::Maximize => crate::Error::ApiNotAllowlisted("window > maximize".to_string()), - Self::Unmaximize => crate::Error::ApiNotAllowlisted("window > unmaximize".to_string()), - Self::ToggleMaximize => { - crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string()) - } - Self::Minimize => crate::Error::ApiNotAllowlisted("window > minimize".to_string()), - Self::Unminimize => crate::Error::ApiNotAllowlisted("window > unminimize".to_string()), - Self::Show => crate::Error::ApiNotAllowlisted("window > show".to_string()), - Self::Hide => crate::Error::ApiNotAllowlisted("window > hide".to_string()), - Self::Close => crate::Error::ApiNotAllowlisted("window > close".to_string()), - Self::SetDecorations(_) => { - crate::Error::ApiNotAllowlisted("window > setDecorations".to_string()) - } - Self::SetAlwaysOnTop(_) => { - crate::Error::ApiNotAllowlisted("window > setAlwaysOnTop".to_string()) - } - Self::SetSize(_) => crate::Error::ApiNotAllowlisted("window > setSize".to_string()), - Self::SetMinSize(_) => crate::Error::ApiNotAllowlisted("window > setMinSize".to_string()), - Self::SetMaxSize(_) => crate::Error::ApiNotAllowlisted("window > setMaxSize".to_string()), - Self::SetPosition(_) => crate::Error::ApiNotAllowlisted("window > setPosition".to_string()), - Self::SetFullscreen(_) => { - crate::Error::ApiNotAllowlisted("window > setFullscreen".to_string()) - } - Self::SetIcon { .. } => crate::Error::ApiNotAllowlisted("window > setIcon".to_string()), - Self::SetSkipTaskbar(_) => { - crate::Error::ApiNotAllowlisted("window > setSkipTaskbar".to_string()) - } - Self::SetCursorGrab(_) => { - crate::Error::ApiNotAllowlisted("window > setCursorGrab".to_string()) - } - Self::SetCursorVisible(_) => { - crate::Error::ApiNotAllowlisted("window > setCursorVisible".to_string()) - } - Self::SetCursorIcon(_) => { - crate::Error::ApiNotAllowlisted("window > setCursorIcon".to_string()) - } - Self::SetCursorPosition(_) => { - crate::Error::ApiNotAllowlisted("window > setCursorPosition".to_string()) - } - Self::StartDragging => crate::Error::ApiNotAllowlisted("window > startDragging".to_string()), - Self::Print => crate::Error::ApiNotAllowlisted("window > print".to_string()), - Self::InternalToggleMaximize => { - crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string()) - } - _ => crate::Error::ApiNotAllowlisted("window > all".to_string()), +pub fn into_allowlist_error(variant: &str) -> crate::Error { + match variant { + "center" => crate::Error::ApiNotAllowlisted("window > center".to_string()), + "requestUserAttention" => { + crate::Error::ApiNotAllowlisted("window > requestUserAttention".to_string()) } + "setResizable" => crate::Error::ApiNotAllowlisted("window > setResizable".to_string()), + "setTitle" => crate::Error::ApiNotAllowlisted("window > setTitle".to_string()), + "maximize" => crate::Error::ApiNotAllowlisted("window > maximize".to_string()), + "unmaximize" => crate::Error::ApiNotAllowlisted("window > unmaximize".to_string()), + "toggleMaximize" => { + crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string()) + } + "minimize" => crate::Error::ApiNotAllowlisted("window > minimize".to_string()), + "nnminimize" => crate::Error::ApiNotAllowlisted("window > unminimize".to_string()), + "show" => crate::Error::ApiNotAllowlisted("window > show".to_string()), + "hide" => crate::Error::ApiNotAllowlisted("window > hide".to_string()), + "close" => crate::Error::ApiNotAllowlisted("window > close".to_string()), + "setDecorations" => crate::Error::ApiNotAllowlisted("window > setDecorations".to_string()), + "setAlwaysOnTop" => crate::Error::ApiNotAllowlisted("window > setAlwaysOnTop".to_string()), + "setSize" => crate::Error::ApiNotAllowlisted("window > setSize".to_string()), + "setMinSize" => crate::Error::ApiNotAllowlisted("window > setMinSize".to_string()), + "setMaxSize" => crate::Error::ApiNotAllowlisted("window > setMaxSize".to_string()), + "setPosition" => crate::Error::ApiNotAllowlisted("window > setPosition".to_string()), + "setFullscreen" => crate::Error::ApiNotAllowlisted("window > setFullscreen".to_string()), + "setIcon" => crate::Error::ApiNotAllowlisted("window > setIcon".to_string()), + "setSkipTaskbar" => crate::Error::ApiNotAllowlisted("window > setSkipTaskbar".to_string()), + "setCursorGrab" => crate::Error::ApiNotAllowlisted("window > setCursorGrab".to_string()), + "setCursorVisible" => crate::Error::ApiNotAllowlisted("window > setCursorVisible".to_string()), + "setCursorIcon" => crate::Error::ApiNotAllowlisted("window > setCursorIcon".to_string()), + "setCursorPosition" => { + crate::Error::ApiNotAllowlisted("window > setCursorPosition".to_string()) + } + "startDragging" => crate::Error::ApiNotAllowlisted("window > startDragging".to_string()), + "print" => crate::Error::ApiNotAllowlisted("window > print".to_string()), + "internalToggleMaximize" => { + crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string()) + } + _ => crate::Error::ApiNotAllowlisted("window".to_string()), } } @@ -318,8 +331,6 @@ impl Cmd { window.open_devtools(); } } - #[allow(unreachable_patterns)] - _ => return Err(cmd.into_allowlist_error()), } #[allow(unreachable_code)] Ok(().into()) diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 8d8ca25f0..e8731edf2 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -803,23 +803,100 @@ pub mod test; #[cfg(test)] mod tests { + use cargo_toml::Manifest; + use once_cell::sync::OnceCell; + use std::{env::var, fs::read_to_string, path::PathBuf}; + + static MANIFEST: OnceCell = OnceCell::new(); + const CHECKED_FEATURES: &str = include_str!(concat!(env!("OUT_DIR"), "/checked_features")); + + fn get_manifest() -> &'static Manifest { + MANIFEST.get_or_init(|| { + let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap()); + let manifest = Manifest::from_path(manifest_dir.join("Cargo.toml")) + .expect("failed to parse Cargo manifest"); + manifest + }) + } + #[test] fn features_are_documented() { - use cargo_toml::Manifest; - use std::{env::var, fs::read_to_string, path::PathBuf}; - // this env var is always set by Cargo let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap()); - let manifest = - Manifest::from_path(manifest_dir.join("Cargo.toml")).expect("failed to parse Cargo manifest"); - let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs"); - for (f, _) in manifest.features { + for (f, _) in &get_manifest().features { if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{}**", f))) { panic!("Feature {} is not documented", f); } } } + + #[test] + fn aliased_features_exist() { + let checked_features = CHECKED_FEATURES.split(','); + let manifest = get_manifest(); + for checked_feature in checked_features { + if !manifest.features.iter().any(|(f, _)| f == checked_feature) { + panic!( + "Feature {} was checked in the alias build step but it does not exist in core/tauri/Cargo.toml", + checked_feature + ); + } + } + } + + #[test] + fn all_allowlist_features_are_aliased() { + let manifest = get_manifest(); + let all_modules = manifest + .features + .iter() + .find(|(f, _)| f.as_str() == "api-all") + .map(|(_, enabled)| enabled) + .expect("api-all feature must exist"); + + let checked_features = CHECKED_FEATURES.split(',').collect::>(); + assert!( + checked_features.contains(&"api-all"), + "`api-all` is not aliased" + ); + + // features that look like an allowlist feature, but are not + let allowed = [ + "fs-extract-api", + "http-api", + "http-multipart", + "process-command-api", + "process-relaunch-dangerous-allow-symlink-macos", + "window-data-url", + ]; + + for module_all_feature in all_modules { + let module = module_all_feature.replace("-all", ""); + assert!( + checked_features.contains(&module_all_feature.as_str()), + "`{}` is not aliased", + module + ); + + let module_prefix = format!("{}-", module); + // we assume that module features are the ones that start with `-` + // though it's not 100% accurate, we have an allowed list to fix it + let module_features = manifest + .features + .iter() + .map(|(f, _)| f) + .filter(|f| f.starts_with(&module_prefix)); + for module_feature in module_features { + assert!( + allowed.contains(&module_feature.as_str()) + || checked_features.contains(&module_feature.as_str()), + "`{}` is not aliased", + module_feature + ); + } + } + } } #[cfg(test)] diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 3931e72c8..86f313f06 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -605,8 +605,12 @@ impl Window { let invoke = Invoke { message, resolver }; if let Some(module) = &payload.tauri_module { - let module = module.to_string(); - crate::endpoints::handle(module, invoke, manager.config(), manager.package_info()); + crate::endpoints::handle( + module.to_string(), + invoke, + manager.config(), + manager.package_info(), + ); } else if payload.cmd.starts_with("plugin:") { manager.extend_api(invoke); } else { From 2189416a76f99b380d72ed6ce671b71380fc1d97 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 24 Apr 2022 19:32:51 -0300 Subject: [PATCH 5/7] fix(core): properly check `CARGO_FEATURE_` env vars --- core/tauri/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tauri/build.rs b/core/tauri/build.rs index 0288c5626..408c296ec 100644 --- a/core/tauri/build.rs +++ b/core/tauri/build.rs @@ -21,7 +21,7 @@ fn has_feature(feature: &str) -> bool { // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts std::env::var(format!( "CARGO_FEATURE_{}", - feature.to_uppercase().to_snake_case() + feature.to_snake_case().to_uppercase() )) .map(|x| x == "1") .unwrap_or(false) From bcfed499cf10086c31f2a97b23b149f4ed84569a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 24 Apr 2022 19:33:23 -0300 Subject: [PATCH 6/7] fix(tests): ensure all features are checked --- core/tauri/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/tauri/build.rs b/core/tauri/build.rs index 408c296ec..b9cd073dc 100644 --- a/core/tauri/build.rs +++ b/core/tauri/build.rs @@ -100,7 +100,7 @@ fn main() { // helper for the command module macro let shell_script = has_feature("shell-execute") || has_feature("shell-sidecar"); alias("shell_script", shell_script); - alias("shell_scope", shell_script || has_feature("shell-open-api")); + alias("shell_scope", has_feature("shell-open-api") || shell_script); alias_module( "dialog", @@ -143,13 +143,13 @@ fn main() { // Note that both `module` and `apis` strings must be written in kebab case. fn alias_module(module: &str, apis: &[&str], api_all: bool) { let all_feature_name = format!("{}-all", module); - let all = api_all || has_feature(&all_feature_name); + let all = has_feature(&all_feature_name) || api_all; alias(&all_feature_name.to_snake_case(), all); let mut any = all; for api in apis { - let has = all || has_feature(&format!("{}-{}", module, api)); + let has = has_feature(&format!("{}-{}", module, api)) || all; alias( &format!("{}_{}", module.to_snake_case(), api.to_snake_case()), has, From 0ea009cfdc2a44eeca85925c28ecf30ba2ce8a94 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 24 Apr 2022 19:49:22 -0300 Subject: [PATCH 7/7] chore(lint): fix warnings --- core/tauri/src/lib.rs | 6 ++---- core/tauri/src/manager.rs | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index e8731edf2..dc5ad743b 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -813,9 +813,7 @@ mod tests { fn get_manifest() -> &'static Manifest { MANIFEST.get_or_init(|| { let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap()); - let manifest = Manifest::from_path(manifest_dir.join("Cargo.toml")) - .expect("failed to parse Cargo manifest"); - manifest + Manifest::from_path(manifest_dir.join("Cargo.toml")).expect("failed to parse Cargo manifest") }) } @@ -824,7 +822,7 @@ mod tests { let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap()); let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs"); - for (f, _) in &get_manifest().features { + for f in get_manifest().features.keys() { if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{}**", f))) { panic!("Feature {} is not documented", f); } diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index ddef57b5f..e6b26620e 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -1390,6 +1390,7 @@ mod tests { #[test] fn string_replace_with_callback() { let mut tauri_index = 0; + #[allow(clippy::single_element_loop)] for (src, pattern, replacement, result) in [( "tauri is awesome, tauri is amazing", "tauri",