From 292696fc424e9f44fead55f182ef35bc6350a347 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Sun, 16 Nov 2025 13:16:53 +0800 Subject: [PATCH 1/8] feat(macros): add support for alias command macro in tauri-macros #14173 --- .changes/add-macros-allow-alias-command.md | 5 ++ crates/tauri-macros/src/command/handler.rs | 18 +++++- crates/tauri-macros/src/command/wrapper.rs | 72 +++++++++++++++++++--- 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 .changes/add-macros-allow-alias-command.md diff --git a/.changes/add-macros-allow-alias-command.md b/.changes/add-macros-allow-alias-command.md new file mode 100644 index 000000000000..e5f9137a6710 --- /dev/null +++ b/.changes/add-macros-allow-alias-command.md @@ -0,0 +1,5 @@ +--- +"tauri-macros": minor:feat +--- + +Add support for alias command macro in tauri-macros \ No newline at end of file diff --git a/crates/tauri-macros/src/command/handler.rs b/crates/tauri-macros/src/command/handler.rs index add699b73da2..566481038b84 100644 --- a/crates/tauri-macros/src/command/handler.rs +++ b/crates/tauri-macros/src/command/handler.rs @@ -155,10 +155,26 @@ impl From for proc_macro::TokenStream { .into_iter() .map(|def| (def.path, def.attrs)) .unzip(); + // Build the module-qualified paths for the command name constants emitted by the wrapper macro. + let command_name_consts: Vec = paths + .iter() + .zip(commands.iter()) + .map(|(p, name)| { + let mut p = p.clone(); + let last = p + .segments + .last_mut() + .expect("path has at least one segment"); + let upper = name.to_string().to_uppercase(); + last.ident = format_ident!("__TAURI_COMMAND_NAME_{}", upper); + p + }) + .collect(); + quote::quote!(move |#invoke| { let #cmd = #invoke.message.command(); match #cmd { - #(#(#attrs)* stringify!(#commands) => #wrappers!(#paths, #invoke),)* + #(#(#attrs)* #command_name_consts => #wrappers!(#paths, #invoke),)* _ => { return false; }, diff --git a/crates/tauri-macros/src/command/wrapper.rs b/crates/tauri-macros/src/command/wrapper.rs index 261257d639c7..41baf7ef00b6 100644 --- a/crates/tauri-macros/src/command/wrapper.rs +++ b/crates/tauri-macros/src/command/wrapper.rs @@ -40,6 +40,7 @@ struct WrapperAttributes { root: TokenStream2, execution_context: ExecutionContext, argument_case: ArgumentCase, + alias: Option, } impl Parse for WrapperAttributes { @@ -48,6 +49,7 @@ impl Parse for WrapperAttributes { root: quote!(::tauri), execution_context: ExecutionContext::Blocking, argument_case: ArgumentCase::Camel, + alias: None, }; let attrs = Punctuated::::parse_terminated(input)?; @@ -74,6 +76,19 @@ impl Parse for WrapperAttributes { } }; } + } else if v.path.is_ident("alias") { + if let Expr::Lit(ExprLit { + lit: Lit::Str(s), .. + }) = v.value + { + let lit = s.value(); + wrapper_attributes.alias = Some(quote!(#lit)); + } else { + return Err(syn::Error::new( + v.span(), + "expected string literal for alias", + )); + } } else if v.path.is_ident("root") { if let Expr::Lit(ExprLit { lit: Lit::Str(s), @@ -94,7 +109,7 @@ impl Parse for WrapperAttributes { WrapperAttributeKind::Meta(Meta::Path(_)) => { return Err(syn::Error::new( input.span(), - "unexpected input, expected one of `rename_all`, `root`, `async`", + "unexpected input, expected one of `rename_all`, `alias`, `root`, `async`", )); } WrapperAttributeKind::Async => { @@ -138,10 +153,16 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { attrs.execution_context = ExecutionContext::Async; } - // macros used with `pub use my_macro;` need to be exported with `#[macro_export]` - let maybe_macro_export = match &function.vis { - Visibility::Public(_) | Visibility::Restricted(_) => quote!(#[macro_export]), - _ => TokenStream2::default(), + // macros used with `pub use my_macro;` need to be exported with `#[macro_export]`. + // To avoid crate-root name collisions for same-named commands across modules, + // only export non-renamed commands at crate root. Renamed commands remain module-scoped. + let maybe_macro_export = if attrs.alias.is_none() { + match &function.vis { + Visibility::Public(_) | Visibility::Restricted(_) => quote!(#[macro_export]), + _ => TokenStream2::default(), + } + } else { + TokenStream2::default() }; let invoke = Invoke { @@ -270,6 +291,28 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { TokenStream2::default() }; + // For renamed commands (no crate-root export), restrict alias visibility to crate-only + // to avoid public re-export errors for non-exported macros. + let alias_visibility = if attrs.alias.is_some() { + quote!(pub(crate)) + } else { + quote!(#visibility) + }; + + // Always define a hidden constant holding the externally invoked command name. + // This lets the handler match on the renamed string while the original function + // identifier remains usable in `generate_handler![original_fn_name]`. + let command_name_const_ident = { + let upper = function.sig.ident.to_string().to_uppercase(); + format_ident!("__TAURI_COMMAND_NAME_{}", upper) + }; + let command_name_const_value = if let Some(ref alias) = attrs.alias { + quote!(#alias) + } else { + let ident = &function.sig.ident; + quote!(stringify!(#ident)) + }; + // Rely on rust 2018 edition to allow importing a macro from a path. quote!( #async_command_check @@ -277,6 +320,11 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { #maybe_allow_unused #function + // Command name constant used by the handler for pattern matching. + #[doc(hidden)] + #maybe_allow_unused + pub const #command_name_const_ident: &str = #command_name_const_value; + #maybe_allow_unused #maybe_macro_export #[doc(hidden)] @@ -303,7 +351,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { // allow the macro to be resolved with the same path as the command function #[allow(unused_imports)] - #visibility use #wrapper; + #alias_visibility use #wrapper; ) .into() } @@ -467,11 +515,21 @@ fn parse_arg( } let root = &attributes.root; + let command_name = if let Some(r) = &attributes.alias { + let r_string = match r.clone().into_iter().next() { + Some(proc_macro2::TokenTree::Literal(lit)) => lit.to_string(), + Some(proc_macro2::TokenTree::Ident(ident)) => ident.to_string(), + _ => quote!(#r).to_string(), + }; + quote!(#r_string) + } else { + quote!(stringify!(#command)) + }; Ok(quote!(#root::ipc::CommandArg::from_command( #root::ipc::CommandItem { plugin: #plugin_name, - name: stringify!(#command), + name: #command_name, key: #key, message: &#message, acl: &#acl, From 78d0a17032b1f1127bed96b69176449bad7cab65 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Mon, 1 Dec 2025 23:18:20 +0800 Subject: [PATCH 2/8] feat(macro): rename alias command to improve clarity in tauri-macros --- .changes/add-macros-allow-alias-command.md | 5 ---- .changes/add-macros-allow-rename-command.md | 5 ++++ crates/tauri-macros/src/command/wrapper.rs | 26 ++++++++++----------- 3 files changed, 18 insertions(+), 18 deletions(-) delete mode 100644 .changes/add-macros-allow-alias-command.md create mode 100644 .changes/add-macros-allow-rename-command.md diff --git a/.changes/add-macros-allow-alias-command.md b/.changes/add-macros-allow-alias-command.md deleted file mode 100644 index e5f9137a6710..000000000000 --- a/.changes/add-macros-allow-alias-command.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"tauri-macros": minor:feat ---- - -Add support for alias command macro in tauri-macros \ No newline at end of file diff --git a/.changes/add-macros-allow-rename-command.md b/.changes/add-macros-allow-rename-command.md new file mode 100644 index 000000000000..d5d316126fcb --- /dev/null +++ b/.changes/add-macros-allow-rename-command.md @@ -0,0 +1,5 @@ +--- +"tauri-macros": minor:feat +--- + +Add support for rename command macro in tauri-macros \ No newline at end of file diff --git a/crates/tauri-macros/src/command/wrapper.rs b/crates/tauri-macros/src/command/wrapper.rs index 41baf7ef00b6..deef6e89adba 100644 --- a/crates/tauri-macros/src/command/wrapper.rs +++ b/crates/tauri-macros/src/command/wrapper.rs @@ -40,7 +40,7 @@ struct WrapperAttributes { root: TokenStream2, execution_context: ExecutionContext, argument_case: ArgumentCase, - alias: Option, + rename: Option, } impl Parse for WrapperAttributes { @@ -49,7 +49,7 @@ impl Parse for WrapperAttributes { root: quote!(::tauri), execution_context: ExecutionContext::Blocking, argument_case: ArgumentCase::Camel, - alias: None, + rename: None, }; let attrs = Punctuated::::parse_terminated(input)?; @@ -76,17 +76,17 @@ impl Parse for WrapperAttributes { } }; } - } else if v.path.is_ident("alias") { + } else if v.path.is_ident("rename") { if let Expr::Lit(ExprLit { lit: Lit::Str(s), .. }) = v.value { let lit = s.value(); - wrapper_attributes.alias = Some(quote!(#lit)); + wrapper_attributes.rename = Some(quote!(#lit)); } else { return Err(syn::Error::new( v.span(), - "expected string literal for alias", + "expected string literal for rename", )); } } else if v.path.is_ident("root") { @@ -109,7 +109,7 @@ impl Parse for WrapperAttributes { WrapperAttributeKind::Meta(Meta::Path(_)) => { return Err(syn::Error::new( input.span(), - "unexpected input, expected one of `rename_all`, `alias`, `root`, `async`", + "unexpected input, expected one of `rename_all`, `rename`, `root`, `async`", )); } WrapperAttributeKind::Async => { @@ -156,7 +156,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { // macros used with `pub use my_macro;` need to be exported with `#[macro_export]`. // To avoid crate-root name collisions for same-named commands across modules, // only export non-renamed commands at crate root. Renamed commands remain module-scoped. - let maybe_macro_export = if attrs.alias.is_none() { + let maybe_macro_export = if attrs.rename.is_none() { match &function.vis { Visibility::Public(_) | Visibility::Restricted(_) => quote!(#[macro_export]), _ => TokenStream2::default(), @@ -291,9 +291,9 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { TokenStream2::default() }; - // For renamed commands (no crate-root export), restrict alias visibility to crate-only + // For renamed commands (no crate-root export), restrict rename visibility to crate-only // to avoid public re-export errors for non-exported macros. - let alias_visibility = if attrs.alias.is_some() { + let rename_visibility = if attrs.rename.is_some() { quote!(pub(crate)) } else { quote!(#visibility) @@ -306,8 +306,8 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { let upper = function.sig.ident.to_string().to_uppercase(); format_ident!("__TAURI_COMMAND_NAME_{}", upper) }; - let command_name_const_value = if let Some(ref alias) = attrs.alias { - quote!(#alias) + let command_name_const_value = if let Some(ref rename) = attrs.rename { + quote!(#rename) } else { let ident = &function.sig.ident; quote!(stringify!(#ident)) @@ -351,7 +351,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { // allow the macro to be resolved with the same path as the command function #[allow(unused_imports)] - #alias_visibility use #wrapper; + #rename_visibility use #wrapper; ) .into() } @@ -515,7 +515,7 @@ fn parse_arg( } let root = &attributes.root; - let command_name = if let Some(r) = &attributes.alias { + let command_name = if let Some(r) = &attributes.rename { let r_string = match r.clone().into_iter().next() { Some(proc_macro2::TokenTree::Literal(lit)) => lit.to_string(), Some(proc_macro2::TokenTree::Ident(ident)) => ident.to_string(), From 895cf58f972a1c8fa91a3dbbad6fa9ef5180435e Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:11:50 +0800 Subject: [PATCH 3/8] feat(wrapper): refactor rename handling in WrapperAttributes for improved clarity --- crates/tauri-macros/src/command/wrapper.rs | 30 ++++++++++++---------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/crates/tauri-macros/src/command/wrapper.rs b/crates/tauri-macros/src/command/wrapper.rs index deef6e89adba..be7e4fd93046 100644 --- a/crates/tauri-macros/src/command/wrapper.rs +++ b/crates/tauri-macros/src/command/wrapper.rs @@ -6,7 +6,7 @@ use std::{env::var, sync::OnceLock}; use heck::{ToLowerCamelCase, ToSnakeCase}; use proc_macro::TokenStream; -use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; +use proc_macro2::{Ident, Literal, Span, TokenStream as TokenStream2}; use quote::{format_ident, quote, quote_spanned}; use syn::{ ext::IdentExt, @@ -40,7 +40,7 @@ struct WrapperAttributes { root: TokenStream2, execution_context: ExecutionContext, argument_case: ArgumentCase, - rename: Option, + rename: RenamePolicy, } impl Parse for WrapperAttributes { @@ -49,7 +49,7 @@ impl Parse for WrapperAttributes { root: quote!(::tauri), execution_context: ExecutionContext::Blocking, argument_case: ArgumentCase::Camel, - rename: None, + rename: RenamePolicy::Keep, }; let attrs = Punctuated::::parse_terminated(input)?; @@ -82,7 +82,7 @@ impl Parse for WrapperAttributes { }) = v.value { let lit = s.value(); - wrapper_attributes.rename = Some(quote!(#lit)); + wrapper_attributes.rename = RenamePolicy::Rename(lit); } else { return Err(syn::Error::new( v.span(), @@ -135,6 +135,12 @@ enum ArgumentCase { Camel, } +/// The rename policy for the command. +enum RenamePolicy { + Keep, + Rename(String), +} + /// The bindings we attach to `tauri::Invoke`. struct Invoke { message: Ident, @@ -156,7 +162,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { // macros used with `pub use my_macro;` need to be exported with `#[macro_export]`. // To avoid crate-root name collisions for same-named commands across modules, // only export non-renamed commands at crate root. Renamed commands remain module-scoped. - let maybe_macro_export = if attrs.rename.is_none() { + let maybe_macro_export = if let RenamePolicy::Keep = attrs.rename { match &function.vis { Visibility::Public(_) | Visibility::Restricted(_) => quote!(#[macro_export]), _ => TokenStream2::default(), @@ -293,7 +299,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { // For renamed commands (no crate-root export), restrict rename visibility to crate-only // to avoid public re-export errors for non-exported macros. - let rename_visibility = if attrs.rename.is_some() { + let rename_visibility = if let RenamePolicy::Rename(_) = &attrs.rename { quote!(pub(crate)) } else { quote!(#visibility) @@ -306,7 +312,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { let upper = function.sig.ident.to_string().to_uppercase(); format_ident!("__TAURI_COMMAND_NAME_{}", upper) }; - let command_name_const_value = if let Some(ref rename) = attrs.rename { + let command_name_const_value = if let RenamePolicy::Rename(ref rename) = attrs.rename { quote!(#rename) } else { let ident = &function.sig.ident; @@ -515,13 +521,9 @@ fn parse_arg( } let root = &attributes.root; - let command_name = if let Some(r) = &attributes.rename { - let r_string = match r.clone().into_iter().next() { - Some(proc_macro2::TokenTree::Literal(lit)) => lit.to_string(), - Some(proc_macro2::TokenTree::Ident(ident)) => ident.to_string(), - _ => quote!(#r).to_string(), - }; - quote!(#r_string) + let command_name = if let RenamePolicy::Rename(r) = &attributes.rename { + let r_literal = Literal::string(r.as_str()); + quote!(#r_literal) } else { quote!(stringify!(#command)) }; From c9bc53df66389da6ae1b63136fc5badb8946d0ab Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:24:39 +0800 Subject: [PATCH 4/8] feat(wrapper): update rename policy to use TokenStream2 for improved flexibility --- crates/tauri-macros/src/command/wrapper.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/tauri-macros/src/command/wrapper.rs b/crates/tauri-macros/src/command/wrapper.rs index be7e4fd93046..9da9663a2832 100644 --- a/crates/tauri-macros/src/command/wrapper.rs +++ b/crates/tauri-macros/src/command/wrapper.rs @@ -6,7 +6,7 @@ use std::{env::var, sync::OnceLock}; use heck::{ToLowerCamelCase, ToSnakeCase}; use proc_macro::TokenStream; -use proc_macro2::{Ident, Literal, Span, TokenStream as TokenStream2}; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{format_ident, quote, quote_spanned}; use syn::{ ext::IdentExt, @@ -82,7 +82,7 @@ impl Parse for WrapperAttributes { }) = v.value { let lit = s.value(); - wrapper_attributes.rename = RenamePolicy::Rename(lit); + wrapper_attributes.rename = RenamePolicy::Rename(quote!(#lit)); } else { return Err(syn::Error::new( v.span(), @@ -138,7 +138,7 @@ enum ArgumentCase { /// The rename policy for the command. enum RenamePolicy { Keep, - Rename(String), + Rename(TokenStream2), } /// The bindings we attach to `tauri::Invoke`. @@ -522,8 +522,7 @@ fn parse_arg( let root = &attributes.root; let command_name = if let RenamePolicy::Rename(r) = &attributes.rename { - let r_literal = Literal::string(r.as_str()); - quote!(#r_literal) + quote!(stringify!(#r)) } else { quote!(stringify!(#command)) }; From 25bacebb14149a70f4a14c80c469e5ebda369f34 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:36:06 +0800 Subject: [PATCH 5/8] feat(handler): streamline command definition parsing for improved efficiency --- crates/tauri-macros/src/command/handler.rs | 36 ++++++++++------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/crates/tauri-macros/src/command/handler.rs b/crates/tauri-macros/src/command/handler.rs index 566481038b84..1c646289bf26 100644 --- a/crates/tauri-macros/src/command/handler.rs +++ b/crates/tauri-macros/src/command/handler.rs @@ -151,25 +151,23 @@ impl From for proc_macro::TokenStream { ) -> Self { let cmd = format_ident!("__tauri_cmd__"); let invoke = format_ident!("__tauri_invoke__"); - let (paths, attrs): (Vec, Vec>) = command_defs - .into_iter() - .map(|def| (def.path, def.attrs)) - .unzip(); - // Build the module-qualified paths for the command name constants emitted by the wrapper macro. - let command_name_consts: Vec = paths - .iter() - .zip(commands.iter()) - .map(|(p, name)| { - let mut p = p.clone(); - let last = p - .segments - .last_mut() - .expect("path has at least one segment"); - let upper = name.to_string().to_uppercase(); - last.ident = format_ident!("__TAURI_COMMAND_NAME_{}", upper); - p - }) - .collect(); + let (paths, attrs, command_name_consts): (Vec, Vec>, Vec) = + command_defs + .into_iter() + .zip(commands.into_iter()) + .map(|(def, command)| { + let path = def.path; + let attrs = def.attrs; + let mut const_path = path.clone(); + let last = const_path + .segments + .last_mut() + .expect("path has at least one segment"); + let upper = command.to_string().to_uppercase(); + last.ident = format_ident!("__TAURI_COMMAND_NAME_{}", upper); + (path, attrs, const_path) + }) + .collect(); quote::quote!(move |#invoke| { let #cmd = #invoke.message.command(); From fc41cd425ce9a12e24b5f2fe9c3999d44c7cd690 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:46:47 +0800 Subject: [PATCH 6/8] feat(wrapper): simplify macro export logic in wrapper function for clarity --- crates/tauri-macros/src/command/wrapper.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/tauri-macros/src/command/wrapper.rs b/crates/tauri-macros/src/command/wrapper.rs index 9da9663a2832..18856f0a86c0 100644 --- a/crates/tauri-macros/src/command/wrapper.rs +++ b/crates/tauri-macros/src/command/wrapper.rs @@ -162,13 +162,11 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { // macros used with `pub use my_macro;` need to be exported with `#[macro_export]`. // To avoid crate-root name collisions for same-named commands across modules, // only export non-renamed commands at crate root. Renamed commands remain module-scoped. - let maybe_macro_export = if let RenamePolicy::Keep = attrs.rename { - match &function.vis { - Visibility::Public(_) | Visibility::Restricted(_) => quote!(#[macro_export]), - _ => TokenStream2::default(), + let maybe_macro_export = match (&attrs.rename, &function.vis) { + (RenamePolicy::Keep, Visibility::Public(_) | Visibility::Restricted(_)) => { + quote!(#[macro_export]) } - } else { - TokenStream2::default() + _ => TokenStream2::default(), }; let invoke = Invoke { From 5f6385d6ca534d165f3821a5a036bc58b828a778 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Tue, 2 Dec 2025 05:53:48 +0800 Subject: [PATCH 7/8] fix(handler): optimize command zipping for improved readability --- crates/tauri-macros/src/command/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/tauri-macros/src/command/handler.rs b/crates/tauri-macros/src/command/handler.rs index 1c646289bf26..d65028ce797c 100644 --- a/crates/tauri-macros/src/command/handler.rs +++ b/crates/tauri-macros/src/command/handler.rs @@ -154,7 +154,7 @@ impl From for proc_macro::TokenStream { let (paths, attrs, command_name_consts): (Vec, Vec>, Vec) = command_defs .into_iter() - .zip(commands.into_iter()) + .zip(commands) .map(|(def, command)| { let path = def.path; let attrs = def.attrs; From 5d4430709cc6fb69f4b62ac8c74861f1826bd088 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Tue, 2 Dec 2025 19:20:08 +0800 Subject: [PATCH 8/8] fix: code style compectiable with rust 1.77.2 --- crates/tauri-macros/src/command/handler.rs | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/tauri-macros/src/command/handler.rs b/crates/tauri-macros/src/command/handler.rs index d65028ce797c..e286e02b5c02 100644 --- a/crates/tauri-macros/src/command/handler.rs +++ b/crates/tauri-macros/src/command/handler.rs @@ -151,23 +151,23 @@ impl From for proc_macro::TokenStream { ) -> Self { let cmd = format_ident!("__tauri_cmd__"); let invoke = format_ident!("__tauri_invoke__"); - let (paths, attrs, command_name_consts): (Vec, Vec>, Vec) = - command_defs - .into_iter() - .zip(commands) - .map(|(def, command)| { - let path = def.path; - let attrs = def.attrs; - let mut const_path = path.clone(); - let last = const_path - .segments - .last_mut() - .expect("path has at least one segment"); - let upper = command.to_string().to_uppercase(); - last.ident = format_ident!("__TAURI_COMMAND_NAME_{}", upper); - (path, attrs, const_path) - }) - .collect(); + let mut paths: Vec = Vec::new(); + let mut attrs: Vec> = Vec::new(); + let mut command_name_consts: Vec = Vec::new(); + for (def, command) in command_defs.into_iter().zip(commands) { + let path = def.path; + let attrs_vec = def.attrs; + let mut const_path = path.clone(); + let last = const_path + .segments + .last_mut() + .expect("path has at least one segment"); + let upper = command.to_string().to_uppercase(); + last.ident = format_ident!("__TAURI_COMMAND_NAME_{}", upper); + paths.push(path); + attrs.push(attrs_vec); + command_name_consts.push(const_path); + } quote::quote!(move |#invoke| { let #cmd = #invoke.message.command();