mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-01 10:01:07 +02:00
refactor(macros): explicitly pass idents (#1812)
This commit is contained in:
5
.changes/command-macros-binding-refactor.md
Normal file
5
.changes/command-macros-binding-refactor.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-macros": patch
|
||||
---
|
||||
|
||||
internal: Refactor all macro code that expects specific bindings to be passed Idents
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use quote::format_ident;
|
||||
use syn::{
|
||||
parse::{Parse, ParseBuffer},
|
||||
Ident, Path, Token,
|
||||
@@ -51,12 +52,14 @@ impl From<Handler> for proc_macro::TokenStream {
|
||||
wrappers,
|
||||
}: Handler,
|
||||
) -> Self {
|
||||
quote::quote!(move |__tauri_invoke__| {
|
||||
let __tauri_cmd__ = __tauri_invoke__.message.command();
|
||||
match __tauri_cmd__ {
|
||||
#(stringify!(#commands) => #wrappers!(#paths, __tauri_invoke__),)*
|
||||
let cmd = format_ident!("__tauri_cmd__");
|
||||
let invoke = format_ident!("__tauri_invoke__");
|
||||
quote::quote!(move |#invoke| {
|
||||
let #cmd = #invoke.message.command();
|
||||
match #cmd {
|
||||
#(stringify!(#commands) => #wrappers!(#paths, #invoke),)*
|
||||
_ => {
|
||||
__tauri_invoke__.resolver.reject(format!("command {} not found", __tauri_cmd__))
|
||||
#invoke.resolver.reject(format!("command {} not found", #cmd))
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse::{Parse, ParseBuffer},
|
||||
parse_macro_input,
|
||||
@@ -36,6 +36,12 @@ impl Parse for ExecutionContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// The bindings we attach to `tauri::Invoke`.
|
||||
struct Invoke {
|
||||
message: Ident,
|
||||
resolver: Ident,
|
||||
}
|
||||
|
||||
/// Create a new [`Wrapper`] from the function and the generated code parsed from the function.
|
||||
pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let function = parse_macro_input!(item as ItemFn);
|
||||
@@ -48,6 +54,11 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let invoke = Invoke {
|
||||
message: format_ident!("__tauri_message__"),
|
||||
resolver: format_ident!("__tauri_resolver__"),
|
||||
};
|
||||
|
||||
// body to the command wrapper or a `compile_error!` of an error occurred while parsing it.
|
||||
let body = syn::parse::<ExecutionContext>(attributes)
|
||||
.map(|context| match function.sig.asyncness {
|
||||
@@ -55,11 +66,13 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
None => context,
|
||||
})
|
||||
.and_then(|context| match context {
|
||||
ExecutionContext::Async => body_async(&function),
|
||||
ExecutionContext::Blocking => body_blocking(&function),
|
||||
ExecutionContext::Async => body_async(&function, &invoke),
|
||||
ExecutionContext::Blocking => body_blocking(&function, &invoke),
|
||||
})
|
||||
.unwrap_or_else(syn::Error::into_compile_error);
|
||||
|
||||
let Invoke { message, resolver } = invoke;
|
||||
|
||||
// Rely on rust 2018 edition to allow importing a macro from a path.
|
||||
quote!(
|
||||
#function
|
||||
@@ -68,16 +81,9 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
macro_rules! #wrapper {
|
||||
// double braces because the item is expected to be a block expression
|
||||
($path:path, $invoke:ident) => {{
|
||||
// import all the autoref specialization items
|
||||
#[allow(unused_imports)]
|
||||
use ::tauri::command::private::*;
|
||||
|
||||
// prevent warnings when the body is a `compile_error!` or if the command has no arguments
|
||||
#[allow(unused_variables)]
|
||||
let ::tauri::Invoke {
|
||||
message: __tauri_message__,
|
||||
resolver: __tauri_resolver__
|
||||
} = $invoke;
|
||||
let ::tauri::Invoke { message: #message, resolver: #resolver } = $invoke;
|
||||
|
||||
#body
|
||||
}};
|
||||
@@ -94,14 +100,15 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
///
|
||||
/// See the [`tauri::command`] module for all the items and traits that make this possible.
|
||||
///
|
||||
/// * Requires binding `__tauri_message__` and `__tauri_resolver__`.
|
||||
/// * Requires all the traits from `tauri::command::private` to be in scope.
|
||||
///
|
||||
/// [`tauri::command`]: https://docs.rs/tauri/*/tauri/runtime/index.html
|
||||
fn body_async(function: &ItemFn) -> syn::Result<TokenStream2> {
|
||||
parse_args(function).map(|args| {
|
||||
fn body_async(function: &ItemFn, invoke: &Invoke) -> syn::Result<TokenStream2> {
|
||||
let Invoke { message, resolver } = invoke;
|
||||
parse_args(function, message).map(|args| {
|
||||
quote! {
|
||||
__tauri_resolver__.respond_async_serialized(async move {
|
||||
#[allow(unused_imports)]
|
||||
use ::tauri::command::private::*;
|
||||
|
||||
#resolver.respond_async_serialized(async move {
|
||||
let result = $path(#(#args?),*);
|
||||
let kind = (&result).async_kind();
|
||||
kind.future(result).await
|
||||
@@ -114,40 +121,39 @@ fn body_async(function: &ItemFn) -> syn::Result<TokenStream2> {
|
||||
///
|
||||
/// See the [`tauri::command`] module for all the items and traits that make this possible.
|
||||
///
|
||||
/// * Requires binding `__tauri_message__` and `__tauri_resolver__`.
|
||||
/// * Requires all the traits from `tauri::command::private` to be in scope.
|
||||
///
|
||||
/// [`tauri::command`]: https://docs.rs/tauri/*/tauri/runtime/index.html
|
||||
fn body_blocking(function: &ItemFn) -> syn::Result<TokenStream2> {
|
||||
let args = parse_args(function)?;
|
||||
fn body_blocking(function: &ItemFn, invoke: &Invoke) -> syn::Result<TokenStream2> {
|
||||
let Invoke { message, resolver } = invoke;
|
||||
let args = parse_args(function, message)?;
|
||||
|
||||
// the body of a `match` to early return any argument that wasn't successful in parsing.
|
||||
let match_body = quote!({
|
||||
Ok(arg) => arg,
|
||||
Err(err) => return __tauri_resolver__.invoke_error(err),
|
||||
Err(err) => return #resolver.invoke_error(err),
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
#[allow(unused_imports)]
|
||||
use ::tauri::command::private::*;
|
||||
|
||||
let result = $path(#(match #args #match_body),*);
|
||||
let kind = (&result).blocking_kind();
|
||||
kind.block(result, __tauri_resolver__);
|
||||
kind.block(result, #resolver);
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse all arguments for the command wrapper to use from the signature of the command function.
|
||||
fn parse_args(function: &ItemFn) -> syn::Result<Vec<TokenStream2>> {
|
||||
fn parse_args(function: &ItemFn, message: &Ident) -> syn::Result<Vec<TokenStream2>> {
|
||||
function
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| parse_arg(&function.sig.ident, arg))
|
||||
.map(|arg| parse_arg(&function.sig.ident, arg, message))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Transform a [`FnArg`] into a command argument.
|
||||
///
|
||||
/// * Requires binding `__tauri_message__`.
|
||||
fn parse_arg(command: &Ident, arg: &FnArg) -> syn::Result<TokenStream2> {
|
||||
fn parse_arg(command: &Ident, arg: &FnArg, message: &Ident) -> syn::Result<TokenStream2> {
|
||||
// we have no use for self arguments
|
||||
let mut arg = match arg {
|
||||
FnArg::Typed(arg) => arg.pat.as_ref().clone(),
|
||||
@@ -190,7 +196,7 @@ fn parse_arg(command: &Ident, arg: &FnArg) -> syn::Result<TokenStream2> {
|
||||
::tauri::command::CommandItem {
|
||||
name: stringify!(#command),
|
||||
key: #key,
|
||||
message: &__tauri_message__,
|
||||
message: &#message,
|
||||
}
|
||||
)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user