@@ -19,8 +19,8 @@ use std::os::windows::process::CommandExt;
1919#[ cfg( windows) ]
2020const CREATE_NO_WINDOW : u32 = 0x0800_0000 ;
2121
22- use crate :: async_runtime:: { block_on as block_on_task, channel, Receiver } ;
23- use os_pipe:: { pipe, PipeWriter } ;
22+ use crate :: async_runtime:: { block_on as block_on_task, channel, Receiver , Sender } ;
23+ use os_pipe:: { pipe, PipeReader , PipeWriter } ;
2424use serde:: Serialize ;
2525use shared_child:: SharedChild ;
2626use tauri_utils:: platform;
@@ -55,11 +55,11 @@ pub struct TerminatedPayload {
5555#[ serde( tag = "event" , content = "payload" ) ]
5656#[ non_exhaustive]
5757pub enum CommandEvent {
58- /// Stderr line .
58+ /// Stderr bytes until a newline (\n) or carriage return (\r) is found .
5959 Stderr ( String ) ,
60- /// Stdout line .
60+ /// Stdout bytes until a newline (\n) or carriage return (\r) is found .
6161 Stdout ( String ) ,
62- /// An error happened.
62+ /// An error happened waiting for the command to finish or converting the stdout/stderr bytes to an UTF-8 string .
6363 Error ( String ) ,
6464 /// Command process terminated.
6565 Terminated ( TerminatedPayload ) ,
@@ -257,37 +257,18 @@ impl Command {
257257
258258 let ( tx, rx) = channel ( 1 ) ;
259259
260- let tx_ = tx. clone ( ) ;
261- let guard_ = guard. clone ( ) ;
262- spawn ( move || {
263- let _lock = guard_. read ( ) . unwrap ( ) ;
264- let reader = BufReader :: new ( stdout_reader) ;
265- for line in reader. lines ( ) {
266- let tx_ = tx_. clone ( ) ;
267- block_on_task ( async move {
268- let _ = match line {
269- Ok ( line) => tx_. send ( CommandEvent :: Stdout ( line) ) . await ,
270- Err ( e) => tx_. send ( CommandEvent :: Error ( e. to_string ( ) ) ) . await ,
271- } ;
272- } ) ;
273- }
274- } ) ;
275-
276- let tx_ = tx. clone ( ) ;
277- let guard_ = guard. clone ( ) ;
278- spawn ( move || {
279- let _lock = guard_. read ( ) . unwrap ( ) ;
280- let reader = BufReader :: new ( stderr_reader) ;
281- for line in reader. lines ( ) {
282- let tx_ = tx_. clone ( ) ;
283- block_on_task ( async move {
284- let _ = match line {
285- Ok ( line) => tx_. send ( CommandEvent :: Stderr ( line) ) . await ,
286- Err ( e) => tx_. send ( CommandEvent :: Error ( e. to_string ( ) ) ) . await ,
287- } ;
288- } ) ;
289- }
290- } ) ;
260+ spawn_pipe_reader (
261+ tx. clone ( ) ,
262+ guard. clone ( ) ,
263+ stdout_reader,
264+ CommandEvent :: Stdout ,
265+ ) ;
266+ spawn_pipe_reader (
267+ tx. clone ( ) ,
268+ guard. clone ( ) ,
269+ stderr_reader,
270+ CommandEvent :: Stderr ,
271+ ) ;
291272
292273 spawn ( move || {
293274 let _ = match child_. wait ( ) {
@@ -390,6 +371,88 @@ impl Command {
390371 }
391372}
392373
374+ fn spawn_pipe_reader < F : Fn ( String ) -> CommandEvent + Send + Copy + ' static > (
375+ tx : Sender < CommandEvent > ,
376+ guard : Arc < RwLock < ( ) > > ,
377+ pipe_reader : PipeReader ,
378+ wrapper : F ,
379+ ) {
380+ spawn ( move || {
381+ let _lock = guard. read ( ) . unwrap ( ) ;
382+ let mut reader = BufReader :: new ( pipe_reader) ;
383+
384+ let mut buf = Vec :: new ( ) ;
385+ loop {
386+ buf. clear ( ) ;
387+ match read_command_output ( & mut reader, & mut buf) {
388+ Ok ( n) => {
389+ if n == 0 {
390+ break ;
391+ }
392+ let tx_ = tx. clone ( ) ;
393+ let line = String :: from_utf8 ( buf. clone ( ) ) ;
394+ block_on_task ( async move {
395+ let _ = match line {
396+ Ok ( line) => tx_. send ( wrapper ( line) ) . await ,
397+ Err ( e) => tx_. send ( CommandEvent :: Error ( e. to_string ( ) ) ) . await ,
398+ } ;
399+ } ) ;
400+ }
401+ Err ( e) => {
402+ let tx_ = tx. clone ( ) ;
403+ let _ = block_on_task ( async move { tx_. send ( CommandEvent :: Error ( e. to_string ( ) ) ) . await } ) ;
404+ }
405+ }
406+ }
407+ } ) ;
408+ }
409+
410+ // adapted from https://doc.rust-lang.org/std/io/trait.BufRead.html#method.read_line
411+ fn read_command_output < R : BufRead + ?Sized > (
412+ r : & mut R ,
413+ buf : & mut Vec < u8 > ,
414+ ) -> std:: io:: Result < usize > {
415+ let mut read = 0 ;
416+ loop {
417+ let ( done, used) = {
418+ let available = match r. fill_buf ( ) {
419+ Ok ( n) => n,
420+ Err ( ref e) if e. kind ( ) == std:: io:: ErrorKind :: Interrupted => continue ,
421+ Err ( e) => return Err ( e) ,
422+ } ;
423+ match memchr:: memchr ( b'\n' , available) {
424+ Some ( i) => {
425+ let end = i + 1 ;
426+ buf. extend_from_slice ( & available[ ..end] ) ;
427+ ( true , end)
428+ }
429+ None => match memchr:: memchr ( b'\r' , available) {
430+ Some ( i) => {
431+ let end = i + 1 ;
432+ buf. extend_from_slice ( & available[ ..end] ) ;
433+ ( true , end)
434+ }
435+ None => {
436+ buf. extend_from_slice ( available) ;
437+ ( false , available. len ( ) )
438+ }
439+ } ,
440+ }
441+ } ;
442+ r. consume ( used) ;
443+ read += used;
444+ if done || used == 0 {
445+ if buf. ends_with ( & [ b'\n' ] ) {
446+ buf. pop ( ) ;
447+ }
448+ if buf. ends_with ( & [ b'\r' ] ) {
449+ buf. pop ( ) ;
450+ }
451+ return Ok ( read) ;
452+ }
453+ }
454+ }
455+
393456// tests for the commands functions.
394457#[ cfg( test) ]
395458mod test {
0 commit comments