@@ -10,7 +10,7 @@ use std::{
10
10
collections:: { BTreeMap , HashMap } ,
11
11
fmt:: { self , Debug } ,
12
12
fs:: File ,
13
- io:: { self , BufRead , BufReader , Write } ,
13
+ io:: { self , BufRead , BufReader , Read , Write } ,
14
14
path:: { Path , PathBuf } ,
15
15
process:: { self , Child , Stdio } ,
16
16
sync:: { Arc , Mutex } ,
@@ -59,12 +59,17 @@ impl SnippetExecutor {
59
59
let config = self . language_config ( snippet) ?;
60
60
let script_dir = Self :: write_snippet ( snippet, config) ?;
61
61
let state: Arc < Mutex < ExecutionState > > = Default :: default ( ) ;
62
+ let output_type = match snippet. attributes . image {
63
+ true => OutputType :: Binary ,
64
+ false => OutputType :: Lines ,
65
+ } ;
62
66
let reader_handle = CommandsRunner :: spawn (
63
67
state. clone ( ) ,
64
68
script_dir,
65
69
config. commands . clone ( ) ,
66
70
config. environment . clone ( ) ,
67
71
self . cwd . to_path_buf ( ) ,
72
+ output_type,
68
73
) ;
69
74
let handle = ExecutionHandle { state, reader_handle } ;
70
75
Ok ( handle)
@@ -183,15 +188,16 @@ impl CommandsRunner {
183
188
commands : Vec < Vec < String > > ,
184
189
env : HashMap < String , String > ,
185
190
cwd : PathBuf ,
191
+ output_type : OutputType ,
186
192
) -> thread:: JoinHandle < ( ) > {
187
193
let reader = Self { state, script_directory } ;
188
- thread:: spawn ( || reader. run ( commands, env, cwd) )
194
+ thread:: spawn ( move || reader. run ( commands, env, cwd, output_type ) )
189
195
}
190
196
191
- fn run ( self , commands : Vec < Vec < String > > , env : HashMap < String , String > , cwd : PathBuf ) {
197
+ fn run ( self , commands : Vec < Vec < String > > , env : HashMap < String , String > , cwd : PathBuf , output_type : OutputType ) {
192
198
let mut last_result = true ;
193
199
for command in commands {
194
- last_result = self . run_command ( command, & env, & cwd) ;
200
+ last_result = self . run_command ( command, & env, & cwd, output_type ) ;
195
201
if !last_result {
196
202
break ;
197
203
}
@@ -203,17 +209,23 @@ impl CommandsRunner {
203
209
self . state . lock ( ) . unwrap ( ) . status = status;
204
210
}
205
211
206
- fn run_command ( & self , command : Vec < String > , env : & HashMap < String , String > , cwd : & Path ) -> bool {
212
+ fn run_command (
213
+ & self ,
214
+ command : Vec < String > ,
215
+ env : & HashMap < String , String > ,
216
+ cwd : & Path ,
217
+ output_type : OutputType ,
218
+ ) -> bool {
207
219
let ( mut child, reader) = match self . launch_process ( command, env, cwd) {
208
220
Ok ( inner) => inner,
209
221
Err ( e) => {
210
222
let mut state = self . state . lock ( ) . unwrap ( ) ;
211
223
state. status = ProcessStatus :: Failure ;
212
- state. output . push ( e. to_string ( ) ) ;
224
+ state. output . extend ( e. to_string ( ) . into_bytes ( ) ) ;
213
225
return false ;
214
226
}
215
227
} ;
216
- let _ = Self :: process_output ( self . state . clone ( ) , reader) ;
228
+ let _ = Self :: process_output ( self . state . clone ( ) , reader, output_type ) ;
217
229
218
230
match child. wait ( ) {
219
231
Ok ( code) => code. success ( ) ,
@@ -246,24 +258,41 @@ impl CommandsRunner {
246
258
Ok ( ( child, reader) )
247
259
}
248
260
249
- fn process_output ( state : Arc < Mutex < ExecutionState > > , reader : os_pipe:: PipeReader ) -> io:: Result < ( ) > {
250
- let reader = BufReader :: new ( reader) ;
251
- for line in reader. lines ( ) {
252
- let mut line = line?;
253
- if line. contains ( '\t' ) {
254
- line = line. replace ( '\t' , " " ) ;
261
+ fn process_output (
262
+ state : Arc < Mutex < ExecutionState > > ,
263
+ mut reader : os_pipe:: PipeReader ,
264
+ output_type : OutputType ,
265
+ ) -> io:: Result < ( ) > {
266
+ match output_type {
267
+ OutputType :: Lines => {
268
+ let reader = BufReader :: new ( reader) ;
269
+ for line in reader. lines ( ) {
270
+ let mut state = state. lock ( ) . unwrap ( ) ;
271
+ state. output . extend ( line?. into_bytes ( ) ) ;
272
+ state. output . push ( b'\n' ) ;
273
+ }
274
+ Ok ( ( ) )
275
+ }
276
+ OutputType :: Binary => {
277
+ let mut buffer = Vec :: new ( ) ;
278
+ reader. read_to_end ( & mut buffer) ?;
279
+ state. lock ( ) . unwrap ( ) . output . extend ( buffer) ;
280
+ Ok ( ( ) )
255
281
}
256
- // TODO: consider not locking per line...
257
- state. lock ( ) . unwrap ( ) . output . push ( line) ;
258
282
}
259
- Ok ( ( ) )
260
283
}
261
284
}
262
285
286
+ #[ derive( Clone , Copy ) ]
287
+ enum OutputType {
288
+ Lines ,
289
+ Binary ,
290
+ }
291
+
263
292
/// The state of the execution of a process.
264
293
#[ derive( Clone , Default , Debug ) ]
265
294
pub ( crate ) struct ExecutionState {
266
- pub ( crate ) output : Vec < String > ,
295
+ pub ( crate ) output : Vec < u8 > ,
267
296
pub ( crate ) status : ProcessStatus ,
268
297
}
269
298
@@ -307,8 +336,8 @@ echo 'bye'"
307
336
}
308
337
} ;
309
338
310
- let expected_lines = vec ! [ "hello world" , "bye" ] ;
311
- assert_eq ! ( state. output, expected_lines ) ;
339
+ let expected = b "hello world\n bye \n " ;
340
+ assert_eq ! ( state. output, expected ) ;
312
341
}
313
342
314
343
#[ test]
@@ -343,8 +372,8 @@ echo 'hello world'
343
372
}
344
373
} ;
345
374
346
- let expected_lines = vec ! [ "This message redirects to stderr" , "hello world" ] ;
347
- assert_eq ! ( state. output, expected_lines ) ;
375
+ let expected = b "This message redirects to stderr\n hello world\n " ;
376
+ assert_eq ! ( state. output, expected ) ;
348
377
}
349
378
350
379
#[ test]
@@ -368,9 +397,8 @@ echo 'hello world'
368
397
}
369
398
} ;
370
399
371
- let expected_lines =
372
- vec ! [ "this line was hidden" , "this line was hidden and contains another prefix /// " , "hello world" ] ;
373
- assert_eq ! ( state. output, expected_lines) ;
400
+ let expected = b"this line was hidden\n this line was hidden and contains another prefix /// \n hello world\n " ;
401
+ assert_eq ! ( state. output, expected) ;
374
402
}
375
403
376
404
#[ test]
0 commit comments