mirror of
https://github.com/aphrody-labs/tauri.git
synced 2026-10-17 12:45:56 +00:00
* feat(macros): add support for alias command macro in tauri-macros #14173 * feat(macro): rename alias command to improve clarity in tauri-macros * feat(wrapper): refactor rename handling in WrapperAttributes for improved clarity * feat(wrapper): update rename policy to use TokenStream2 for improved flexibility * feat(handler): streamline command definition parsing for improved efficiency * feat(wrapper): simplify macro export logic in wrapper function for clarity * fix(handler): optimize command zipping for improved readability * fix: code style compectiable with rust 1.77.2 * fix const not in scope when command is defined in another mod * update examples * update change file * fmt * style: fix pnpm:check errors --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
1 parent
764b9139a3
commit
c00a3dbffc
6 files changed
+101
-13
No files matched your search
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"tauri-macros": minor:feat
|
||||
"tauri": minor:feat
|
||||
---
|
||||
|
||||
Add support for the `rename` attribute in the `tauri::command` macro to allow renaming the command to something other than the function name.
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use quote::format_ident;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse::{Parse, ParseBuffer, ParseStream},
|
||||
Attribute, Ident, Path, Token,
|
||||
@@ -151,14 +151,30 @@ impl From<Handler> for proc_macro::TokenStream {
|
||||
) -> Self {
|
||||
let cmd = format_ident!("__tauri_cmd__");
|
||||
let invoke = format_ident!("__tauri_invoke__");
|
||||
let (paths, attrs): (Vec<Path>, Vec<Vec<Attribute>>) = command_defs
|
||||
.into_iter()
|
||||
.map(|def| (def.path, def.attrs))
|
||||
.unzip();
|
||||
let mut paths: Vec<Path> = Vec::new();
|
||||
let mut attrs: Vec<Vec<Attribute>> = Vec::new();
|
||||
let mut command_name_macros: Vec<proc_macro2::TokenStream> = Vec::new();
|
||||
for (def, command) in command_defs.into_iter().zip(commands) {
|
||||
let path = def.path;
|
||||
let attrs_vec = def.attrs;
|
||||
|
||||
let mut command_name_macro_path = path.clone();
|
||||
let last = command_name_macro_path
|
||||
.segments
|
||||
.last_mut()
|
||||
.expect("path has at least one segment");
|
||||
last.ident = format_ident!("__tauri_command_name_{command}");
|
||||
|
||||
paths.push(path);
|
||||
attrs.push(attrs_vec);
|
||||
// Call the macro to get the command name string literal
|
||||
command_name_macros.push(quote!(#command_name_macro_path!()));
|
||||
}
|
||||
|
||||
quote::quote!(move |#invoke| {
|
||||
let #cmd = #invoke.message.command();
|
||||
match #cmd {
|
||||
#(#(#attrs)* stringify!(#commands) => #wrappers!(#paths, #invoke),)*
|
||||
#(#(#attrs)* #command_name_macros => #wrappers!(#paths, #invoke),)*
|
||||
_ => {
|
||||
return false;
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ struct WrapperAttributes {
|
||||
root: TokenStream2,
|
||||
execution_context: ExecutionContext,
|
||||
argument_case: ArgumentCase,
|
||||
rename: RenamePolicy,
|
||||
}
|
||||
|
||||
impl Parse for WrapperAttributes {
|
||||
@@ -48,6 +49,7 @@ impl Parse for WrapperAttributes {
|
||||
root: quote!(::tauri),
|
||||
execution_context: ExecutionContext::Blocking,
|
||||
argument_case: ArgumentCase::Camel,
|
||||
rename: RenamePolicy::Keep,
|
||||
};
|
||||
|
||||
let attrs = Punctuated::<WrapperAttributeKind, Token![,]>::parse_terminated(input)?;
|
||||
@@ -74,6 +76,19 @@ impl Parse for WrapperAttributes {
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if v.path.is_ident("rename") {
|
||||
if let Expr::Lit(ExprLit {
|
||||
lit: Lit::Str(s), ..
|
||||
}) = v.value
|
||||
{
|
||||
let lit = s.value();
|
||||
wrapper_attributes.rename = RenamePolicy::Rename(quote!(#lit));
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
v.span(),
|
||||
"expected string literal for rename",
|
||||
));
|
||||
}
|
||||
} 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`, `rename`, `root`, `async`",
|
||||
));
|
||||
}
|
||||
WrapperAttributeKind::Async => {
|
||||
@@ -120,6 +135,12 @@ enum ArgumentCase {
|
||||
Camel,
|
||||
}
|
||||
|
||||
/// The rename policy for the command.
|
||||
enum RenamePolicy {
|
||||
Keep,
|
||||
Rename(TokenStream2),
|
||||
}
|
||||
|
||||
/// The bindings we attach to `tauri::Invoke`.
|
||||
struct Invoke {
|
||||
message: Ident,
|
||||
@@ -138,9 +159,11 @@ 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]`
|
||||
// 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]),
|
||||
Visibility::Public(_) | Visibility::Restricted(_) => {
|
||||
quote!(#[macro_export])
|
||||
}
|
||||
_ => TokenStream2::default(),
|
||||
};
|
||||
|
||||
@@ -270,6 +293,17 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
TokenStream2::default()
|
||||
};
|
||||
|
||||
// Always define a hidden macro that returns 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_macro_ident = format_ident!("__tauri_command_name_{}", function.sig.ident);
|
||||
let command_name_value = if let RenamePolicy::Rename(ref rename) = attrs.rename {
|
||||
quote!(#rename)
|
||||
} 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 +311,17 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
#maybe_allow_unused
|
||||
#function
|
||||
|
||||
// Command name macro used by the handler for pattern matching.
|
||||
// This macro returns the command name string literal (renamed or original).
|
||||
#maybe_allow_unused
|
||||
#maybe_macro_export
|
||||
#[doc(hidden)]
|
||||
macro_rules! #command_name_macro_ident {
|
||||
() => {
|
||||
#command_name_value
|
||||
};
|
||||
}
|
||||
|
||||
#maybe_allow_unused
|
||||
#maybe_macro_export
|
||||
#[doc(hidden)]
|
||||
@@ -303,7 +348,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;
|
||||
#visibility use {#wrapper, #command_name_macro_ident};
|
||||
)
|
||||
.into()
|
||||
}
|
||||
@@ -467,11 +512,16 @@ fn parse_arg(
|
||||
}
|
||||
|
||||
let root = &attributes.root;
|
||||
let command_name = if let RenamePolicy::Rename(r) = &attributes.rename {
|
||||
quote!(stringify!(#r))
|
||||
} 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,
|
||||
|
||||
@@ -25,3 +25,8 @@ pub fn simple_command(the_argument: String) {
|
||||
pub fn stateful_command(the_argument: Option<String>, state: State<'_, super::MyState>) {
|
||||
println!("{:?} {:?}", the_argument, state.inner());
|
||||
}
|
||||
|
||||
#[command(rename = "renamed_command_in_mod_new")]
|
||||
pub fn renamed_command_in_mod() {
|
||||
println!("renamed command in mod called");
|
||||
}
|
||||
@@ -63,7 +63,11 @@
|
||||
{
|
||||
name: 'command_arguments_tuple_struct',
|
||||
args: { inlinePerson: ['ferris', 6] }
|
||||
}
|
||||
},
|
||||
{ name: 'renamed_command' },
|
||||
{ name: 'renamed_command_new' },
|
||||
{ name: 'renamed_command_in_mod' },
|
||||
{ name: 'renamed_command_in_mod_new' }
|
||||
]
|
||||
|
||||
for (const command of commands) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
// we move some basic commands to a separate module just to show it works
|
||||
mod commands;
|
||||
use commands::{cmd, invoke, message, resolver};
|
||||
use commands::{cmd, invoke, message, renamed_command_in_mod, resolver};
|
||||
|
||||
use serde::Deserialize;
|
||||
use tauri::{
|
||||
@@ -188,6 +188,11 @@ fn command_arguments_wild(_: Window) {
|
||||
println!("we saw the wildcard!")
|
||||
}
|
||||
|
||||
#[command(rename = "renamed_command_new")]
|
||||
fn renamed_command() {
|
||||
println!("renamed command called")
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Person<'a> {
|
||||
name: &'a str,
|
||||
@@ -246,6 +251,8 @@ fn main() {
|
||||
future_simple_command,
|
||||
async_stateful_command,
|
||||
command_arguments_wild,
|
||||
renamed_command,
|
||||
renamed_command_in_mod,
|
||||
command_arguments_struct,
|
||||
simple_command_with_result,
|
||||
async_simple_command_snake,
|
||||
|
||||
Reference in new issue
Block a user