@@ -8,7 +8,7 @@ use super::InvokeContext;
8
8
use crate :: { api:: ipc:: CallbackFn , Runtime } ;
9
9
#[ cfg( shell_scope) ]
10
10
use crate :: { Manager , Scopes } ;
11
- use serde:: Deserialize ;
11
+ use serde:: { Deserialize , Serialize } ;
12
12
use tauri_macros:: { command_enum, module_command_handler, CommandModule } ;
13
13
14
14
#[ cfg( shell_scope) ]
@@ -63,6 +63,15 @@ pub struct CommandOptions {
63
63
#[ derive( Deserialize , CommandModule ) ]
64
64
#[ serde( tag = "cmd" , rename_all = "camelCase" ) ]
65
65
pub enum Cmd {
66
+ /// The execute and return script API.
67
+ #[ cmd( shell_script, "shell > execute or shell > sidecar" ) ]
68
+ #[ serde( rename_all = "camelCase" ) ]
69
+ ExecuteAndReturn {
70
+ program : String ,
71
+ args : ExecuteArgs ,
72
+ #[ serde( default ) ]
73
+ options : CommandOptions ,
74
+ } ,
66
75
/// The execute script API.
67
76
#[ cmd( shell_script, "shell > execute or shell > sidecar" ) ]
68
77
#[ serde( rename_all = "camelCase" ) ]
@@ -81,101 +90,88 @@ pub enum Cmd {
81
90
Open { path : String , with : Option < String > } ,
82
91
}
83
92
93
+ #[ derive( Serialize ) ]
94
+ #[ cfg( any( shell_execute, shell_sidecar) ) ]
95
+ struct ChildProcessReturn {
96
+ code : Option < i32 > ,
97
+ signal : Option < i32 > ,
98
+ stdout : String ,
99
+ stderr : String ,
100
+ }
101
+
84
102
impl Cmd {
85
103
#[ module_command_handler( shell_script) ]
86
- #[ allow( unused_variables) ]
104
+ fn execute_and_return < R : Runtime > (
105
+ context : InvokeContext < R > ,
106
+ program : String ,
107
+ args : ExecuteArgs ,
108
+ options : CommandOptions ,
109
+ ) -> super :: Result < ChildProcessReturn > {
110
+ let encoding = options
111
+ . encoding
112
+ . as_ref ( )
113
+ . and_then ( |encoding| crate :: api:: process:: Encoding :: for_label ( encoding. as_bytes ( ) ) ) ;
114
+ let command = prepare_cmd ( & context, & program, args, options) ?;
115
+
116
+ let mut command: std:: process:: Command = command. into ( ) ;
117
+ let output = command. output ( ) ?;
118
+
119
+ let ( stdout, stderr) = match encoding {
120
+ Some ( encoding) => (
121
+ encoding. decode_with_bom_removal ( & output. stdout ) . 0 . into ( ) ,
122
+ encoding. decode_with_bom_removal ( & output. stderr ) . 0 . into ( ) ,
123
+ ) ,
124
+ None => (
125
+ String :: from_utf8 ( output. stdout ) ?,
126
+ String :: from_utf8 ( output. stderr ) ?,
127
+ ) ,
128
+ } ;
129
+
130
+ #[ cfg( unix) ]
131
+ use std:: os:: unix:: process:: ExitStatusExt ;
132
+
133
+ Ok ( ChildProcessReturn {
134
+ code : output. status . code ( ) ,
135
+ #[ cfg( windows) ]
136
+ signal : None ,
137
+ #[ cfg( unix) ]
138
+ signal : output. status . signal ( ) ,
139
+ stdout,
140
+ stderr,
141
+ } )
142
+ }
143
+
144
+ #[ module_command_handler( shell_script) ]
87
145
fn execute < R : Runtime > (
88
146
context : InvokeContext < R > ,
89
147
program : String ,
90
148
args : ExecuteArgs ,
91
149
on_event_fn : CallbackFn ,
92
150
options : CommandOptions ,
93
151
) -> super :: Result < ChildId > {
94
- let mut command = if options. sidecar {
95
- #[ cfg( not( shell_sidecar) ) ]
96
- return Err ( crate :: Error :: ApiNotAllowlisted ( "shell > sidecar" . to_string ( ) ) . into_anyhow ( ) ) ;
97
- #[ cfg( shell_sidecar) ]
98
- {
99
- let program = PathBuf :: from ( program) ;
100
- let program_as_string = program. display ( ) . to_string ( ) ;
101
- let program_no_ext_as_string = program. with_extension ( "" ) . display ( ) . to_string ( ) ;
102
- let configured_sidecar = context
103
- . config
104
- . tauri
105
- . bundle
106
- . external_bin
107
- . as_ref ( )
108
- . map ( |bins| {
109
- bins
110
- . iter ( )
111
- . find ( |b| b == & & program_as_string || b == & & program_no_ext_as_string)
112
- } )
113
- . unwrap_or_default ( ) ;
114
- if let Some ( sidecar) = configured_sidecar {
115
- context
116
- . window
117
- . state :: < Scopes > ( )
118
- . shell
119
- . prepare_sidecar ( & program. to_string_lossy ( ) , sidecar, args)
120
- . map_err ( crate :: error:: into_anyhow) ?
121
- } else {
122
- return Err ( crate :: Error :: SidecarNotAllowed ( program) . into_anyhow ( ) ) ;
123
- }
124
- }
125
- } else {
126
- #[ cfg( not( shell_execute) ) ]
127
- return Err ( crate :: Error :: ApiNotAllowlisted ( "shell > execute" . to_string ( ) ) . into_anyhow ( ) ) ;
128
- #[ cfg( shell_execute) ]
129
- match context
130
- . window
131
- . state :: < Scopes > ( )
132
- . shell
133
- . prepare ( & program, args)
134
- {
135
- Ok ( cmd) => cmd,
136
- Err ( e) => {
137
- #[ cfg( debug_assertions) ]
138
- eprintln ! ( "{e}" ) ;
139
- return Err ( crate :: Error :: ProgramNotAllowed ( PathBuf :: from ( program) ) . into_anyhow ( ) ) ;
140
- }
141
- }
142
- } ;
143
- #[ cfg( any( shell_execute, shell_sidecar) ) ]
144
- {
145
- if let Some ( cwd) = options. cwd {
146
- command = command. current_dir ( cwd) ;
147
- }
148
- if let Some ( env) = options. env {
149
- command = command. envs ( env) ;
150
- } else {
151
- command = command. env_clear ( ) ;
152
- }
153
- if let Some ( encoding) = options. encoding {
154
- if let Some ( encoding) = crate :: api:: process:: Encoding :: for_label ( encoding. as_bytes ( ) ) {
155
- command = command. encoding ( encoding) ;
156
- } else {
157
- return Err ( anyhow:: anyhow!( format!( "unknown encoding {encoding}" ) ) ) ;
158
- }
159
- }
160
- let ( mut rx, child) = command. spawn ( ) ?;
152
+ use std:: future:: Future ;
153
+ use std:: pin:: Pin ;
161
154
162
- let pid = child. pid ( ) ;
163
- command_child_store ( ) . lock ( ) . unwrap ( ) . insert ( pid, child) ;
155
+ let command = prepare_cmd ( & context, & program, args, options) ?;
164
156
165
- crate :: async_runtime:: spawn ( async move {
166
- while let Some ( event) = rx. recv ( ) . await {
167
- if matches ! ( event, crate :: api:: process:: CommandEvent :: Terminated ( _) ) {
168
- command_child_store ( ) . lock ( ) . unwrap ( ) . remove ( & pid) ;
169
- }
170
- let js = crate :: api:: ipc:: format_callback ( on_event_fn, & event)
171
- . expect ( "unable to serialize CommandEvent" ) ;
157
+ let ( mut rx, child) = command. spawn ( ) ?;
172
158
173
- let _ = context. window . eval ( js. as_str ( ) ) ;
159
+ let pid = child. pid ( ) ;
160
+ command_child_store ( ) . lock ( ) . unwrap ( ) . insert ( pid, child) ;
161
+
162
+ crate :: async_runtime:: spawn ( async move {
163
+ while let Some ( event) = rx. recv ( ) . await {
164
+ if matches ! ( event, crate :: api:: process:: CommandEvent :: Terminated ( _) ) {
165
+ command_child_store ( ) . lock ( ) . unwrap ( ) . remove ( & pid) ;
174
166
}
175
- } ) ;
167
+ let js = crate :: api:: ipc:: format_callback ( on_event_fn, & event)
168
+ . expect ( "unable to serialize CommandEvent" ) ;
176
169
177
- Ok ( pid)
178
- }
170
+ let _ = context. window . eval ( js. as_str ( ) ) ;
171
+ }
172
+ } ) ;
173
+
174
+ Ok ( pid)
179
175
}
180
176
181
177
#[ module_command_handler( shell_script) ]
@@ -226,6 +222,81 @@ impl Cmd {
226
222
}
227
223
}
228
224
225
+ fn prepare_cmd < R : Runtime > (
226
+ context : & InvokeContext < R > ,
227
+ program : & String ,
228
+ args : ExecuteArgs ,
229
+ options : CommandOptions ,
230
+ ) -> super :: Result < crate :: api:: process:: Command > {
231
+ let mut command = if options. sidecar {
232
+ #[ cfg( not( shell_sidecar) ) ]
233
+ return Err ( crate :: Error :: ApiNotAllowlisted ( "shell > sidecar" . to_string ( ) ) . into_anyhow ( ) ) ;
234
+ #[ cfg( shell_sidecar) ]
235
+ {
236
+ let program = PathBuf :: from ( program) ;
237
+ let program_as_string = program. display ( ) . to_string ( ) ;
238
+ let program_no_ext_as_string = program. with_extension ( "" ) . display ( ) . to_string ( ) ;
239
+ let configured_sidecar = context
240
+ . config
241
+ . tauri
242
+ . bundle
243
+ . external_bin
244
+ . as_ref ( )
245
+ . map ( |bins| {
246
+ bins
247
+ . iter ( )
248
+ . find ( |b| b == & & program_as_string || b == & & program_no_ext_as_string)
249
+ } )
250
+ . unwrap_or_default ( ) ;
251
+ if let Some ( sidecar) = configured_sidecar {
252
+ context
253
+ . window
254
+ . state :: < Scopes > ( )
255
+ . shell
256
+ . prepare_sidecar ( & program. to_string_lossy ( ) , sidecar, args)
257
+ . map_err ( crate :: error:: into_anyhow)
258
+ } else {
259
+ Err ( crate :: Error :: SidecarNotAllowed ( program) . into_anyhow ( ) )
260
+ }
261
+ }
262
+ } else {
263
+ #[ cfg( not( shell_execute) ) ]
264
+ return Err ( crate :: Error :: ApiNotAllowlisted ( "shell > execute" . to_string ( ) ) . into_anyhow ( ) ) ;
265
+ #[ cfg( shell_execute) ]
266
+ match context
267
+ . window
268
+ . state :: < Scopes > ( )
269
+ . shell
270
+ . prepare ( program, args)
271
+ {
272
+ Ok ( cmd) => Ok ( cmd) ,
273
+ Err ( e) => {
274
+ #[ cfg( debug_assertions) ]
275
+ eprintln ! ( "{e}" ) ;
276
+ Err ( crate :: Error :: ProgramNotAllowed ( PathBuf :: from ( program) ) . into_anyhow ( ) )
277
+ }
278
+ }
279
+ } ?;
280
+
281
+ if let Some ( cwd) = options. cwd {
282
+ command = command. current_dir ( cwd) ;
283
+ }
284
+ if let Some ( env) = options. env {
285
+ command = command. envs ( env) ;
286
+ } else {
287
+ command = command. env_clear ( ) ;
288
+ }
289
+ if let Some ( encoding) = & options. encoding {
290
+ if let Some ( encoding) = crate :: api:: process:: Encoding :: for_label ( encoding. as_bytes ( ) ) {
291
+ command = command. encoding ( encoding) ;
292
+ } else {
293
+ return Err ( anyhow:: anyhow!( format!( "unknown encoding {encoding}" ) ) ) ;
294
+ }
295
+ }
296
+
297
+ Ok ( command)
298
+ }
299
+
229
300
#[ cfg( test) ]
230
301
mod tests {
231
302
use super :: { Buffer , ChildId , CommandOptions , ExecuteArgs } ;
0 commit comments