33// SPDX-License-Identifier: MIT
44
55use proc_macro2:: TokenStream ;
6- use quote:: { format_ident, quote} ;
6+ use quote:: { format_ident, quote, TokenStreamExt } ;
77use syn:: {
8- parse:: Parser , punctuated:: Punctuated , FnArg , Ident , ItemFn , Meta , NestedMeta , Pat , Path ,
9- ReturnType , Token , Type ,
8+ parse:: Parser , punctuated:: Punctuated , FnArg , Ident , ItemFn , Pat , Path , ReturnType , Token , Type ,
9+ Visibility ,
1010} ;
1111
12- pub fn generate_command ( attrs : Vec < NestedMeta > , function : ItemFn ) -> TokenStream {
13- // Check if "with_window" attr was passed to macro
14- let with_window = attrs. iter ( ) . any ( |a| {
15- if let NestedMeta :: Meta ( Meta :: Path ( path) ) = a {
16- path
17- . get_ident ( )
18- . map ( |i| * i == "with_window" )
19- . unwrap_or ( false )
20- } else {
21- false
12+ fn fn_wrapper ( function : & ItemFn ) -> ( & Visibility , Ident ) {
13+ (
14+ & function. vis ,
15+ format_ident ! ( "{}_wrapper" , function. sig. ident) ,
16+ )
17+ }
18+
19+ fn err ( function : ItemFn , error_message : & str ) -> TokenStream {
20+ let ( vis, wrap) = fn_wrapper ( & function) ;
21+ quote ! {
22+ #function
23+
24+ #vis fn #wrap<P : :: tauri:: Params >( _message: :: tauri:: InvokeMessage <P >) {
25+ compile_error!( #error_message) ;
26+ unimplemented!( )
2227 }
23- } ) ;
28+ }
29+ }
2430
31+ pub fn generate_command ( function : ItemFn ) -> TokenStream {
2532 let fn_name = function. sig . ident . clone ( ) ;
2633 let fn_name_str = fn_name. to_string ( ) ;
27- let fn_wrapper = format_ident ! ( "{}_wrapper" , fn_name ) ;
34+ let ( vis , fn_wrapper) = fn_wrapper ( & function ) ;
2835 let returns_result = match function. sig . output {
2936 ReturnType :: Type ( _, ref ty) => match & * * ty {
3037 Type :: Path ( type_path) => {
@@ -40,40 +47,45 @@ pub fn generate_command(attrs: Vec<NestedMeta>, function: ItemFn) -> TokenStream
4047 ReturnType :: Default => false ,
4148 } ;
4249
43- // Split function args into names and types
44- let ( mut names, mut types) : ( Vec < Ident > , Vec < Path > ) = function
45- . sig
46- . inputs
47- . iter ( )
48- . map ( |param| {
49- let mut arg_name = None ;
50- let mut arg_type = None ;
51- if let FnArg :: Typed ( arg) = param {
52- if let Pat :: Ident ( ident) = arg. pat . as_ref ( ) {
53- arg_name = Some ( ident. ident . clone ( ) ) ;
54- }
55- if let Type :: Path ( path) = arg. ty . as_ref ( ) {
56- arg_type = Some ( path. path . clone ( ) ) ;
57- }
50+ let mut invoke_arg_names: Vec < Ident > = Default :: default ( ) ;
51+ let mut invoke_arg_types: Vec < Path > = Default :: default ( ) ;
52+ let mut invoke_args: TokenStream = Default :: default ( ) ;
53+
54+ for param in & function. sig . inputs {
55+ let mut arg_name = None ;
56+ let mut arg_type = None ;
57+ if let FnArg :: Typed ( arg) = param {
58+ if let Pat :: Ident ( ident) = arg. pat . as_ref ( ) {
59+ arg_name = Some ( ident. ident . clone ( ) ) ;
60+ }
61+ if let Type :: Path ( path) = arg. ty . as_ref ( ) {
62+ arg_type = Some ( path. path . clone ( ) ) ;
5863 }
59- (
60- arg_name. clone ( ) . unwrap ( ) ,
61- arg_type. unwrap_or_else ( || panic ! ( "Invalid type for arg \" {}\" " , arg_name. unwrap( ) ) ) ,
62- )
63- } )
64- . unzip ( ) ;
65-
66- let window_arg_maybe = match types. first ( ) {
67- Some ( _) if with_window => {
68- // Remove window arg from list so it isn't expected as arg from JS
69- types. drain ( 0 ..1 ) ;
70- names. drain ( 0 ..1 ) ;
71- // Tell wrapper to pass `window` to original function
72- quote ! ( _window, )
7364 }
74- // Tell wrapper not to pass `window` to original function
75- _ => quote ! ( ) ,
76- } ;
65+
66+ let arg_name_ = arg_name. clone ( ) . unwrap ( ) ;
67+ let arg_name_s = arg_name_. to_string ( ) ;
68+
69+ let arg_type = match arg_type {
70+ Some ( arg_type) => arg_type,
71+ None => {
72+ return err (
73+ function. clone ( ) ,
74+ & format ! ( "invalid type for arg: {}" , arg_name_) ,
75+ )
76+ }
77+ } ;
78+
79+ invoke_args. append_all ( quote ! {
80+ let #arg_name_ = match <#arg_type>:: from_command( #fn_name_str, #arg_name_s, & message) {
81+ Ok ( value) => value,
82+ Err ( e) => return tauri:: InvokeResponse :: error( :: tauri:: Error :: InvalidArgs ( #fn_name_str, e) . to_string( ) )
83+ } ;
84+ } ) ;
85+ invoke_arg_names. push ( arg_name_. clone ( ) ) ;
86+ invoke_arg_types. push ( arg_type) ;
87+ }
88+
7789 let await_maybe = if function. sig . asyncness . is_some ( ) {
7890 quote ! ( . await )
7991 } else {
@@ -86,30 +98,23 @@ pub fn generate_command(attrs: Vec<NestedMeta>, function: ItemFn) -> TokenStream
8698 // note that all types must implement `serde::Serialize`.
8799 let return_value = if returns_result {
88100 quote ! {
89- match #fn_name( #window_arg_maybe # ( parsed_args . #names ) , * ) #await_maybe {
90- Ok ( value) => :: core:: result:: Result :: Ok ( value) ,
91- Err ( e) => :: core:: result:: Result :: Err ( e) ,
101+ match #fn_name( #( #invoke_arg_names ) , * ) #await_maybe {
102+ Ok ( value) => :: core:: result:: Result :: <_ , ( ) > :: Ok ( value) . into ( ) ,
103+ Err ( e) => :: core:: result:: Result :: < ( ) , _> :: Err ( e) . into ( ) ,
92104 }
93105 }
94106 } else {
95- quote ! { :: core:: result:: Result :: <_, ( ) >:: Ok ( #fn_name( #window_arg_maybe # ( parsed_args . #names ) , * ) #await_maybe) }
107+ quote ! { :: core:: result:: Result :: <_, ( ) >:: Ok ( #fn_name( #( #invoke_arg_names ) , * ) #await_maybe) . into ( ) }
96108 } ;
97109
98110 quote ! {
99111 #function
100- pub fn #fn_wrapper<P : :: tauri:: Params >( message: :: tauri:: InvokeMessage <P >) {
101- #[ derive( :: serde:: Deserialize ) ]
102- #[ serde( rename_all = "camelCase" ) ]
103- struct ParsedArgs {
104- #( #names: #types) , *
105- }
106- let _window = message. window( ) ;
107- match :: serde_json:: from_value:: <ParsedArgs >( message. payload( ) ) {
108- Ok ( parsed_args) => message. respond_async( async move {
109- #return_value
110- } ) ,
111- Err ( e) => message. reject( :: tauri:: Error :: InvalidArgs ( #fn_name_str, e) . to_string( ) ) ,
112- }
112+ #vis fn #fn_wrapper<P : :: tauri:: Params >( message: :: tauri:: InvokeMessage <P >, resolver: :: tauri:: InvokeResolver <P >) {
113+ use :: tauri:: command:: FromCommand ;
114+ resolver. respond_async( async move {
115+ #invoke_args
116+ #return_value
117+ } )
113118 }
114119 }
115120}
@@ -134,12 +139,12 @@ pub fn generate_handler(item: proc_macro::TokenStream) -> TokenStream {
134139 } ) ;
135140
136141 quote ! {
137- move |message| {
142+ move |message, resolver | {
138143 let cmd = message. command( ) . to_string( ) ;
139144 match cmd. as_str( ) {
140- #( stringify!( #fn_names) => #fn_wrappers( message) , ) *
145+ #( stringify!( #fn_names) => #fn_wrappers( message, resolver ) , ) *
141146 _ => {
142- message . reject( format!( "command {} not found" , cmd) )
147+ resolver . reject( format!( "command {} not found" , cmd) )
143148 } ,
144149 }
145150 }
0 commit comments