#[ allow( clippy ::std_instead_of_alloc, clippy ::std_instead_of_core ) ]
mod private
{
use std ::
{
fmt ::Formatter,
path :: { Path, PathBuf },
process :: { Command, Stdio },
};
use std ::collections ::HashMap;
use std ::ffi ::OsString;
use duct ::cmd;
use error_tools ::
{
untyped :: { Error, Context, format_err },
};
use former ::Former;
use iter_tools ::iter ::Itertools;
pub fn run( options: Run ) -> Result< Report, Report >
{
let bin_path: &Path = options.bin_path.as_ref();
let current_path: &Path = options.current_path.as_ref();
let mut report = Report
{
command: format!( "{} {}", bin_path.display(), options.args.iter().map( | a | a.to_string_lossy() ).join( " " ) ),
current_path: current_path.to_path_buf(),
.. Report ::default()
};
let mut env: HashMap< String, String > = std ::env ::vars().collect();
env.extend( options.env_variable );
let output = if options.joining_streams
{
let output = cmd( bin_path.as_os_str(), &options.args )
.dir( current_path )
.full_env( env )
.stderr_to_stdout()
.stdout_capture()
.unchecked()
.run()
.map_err( | e |
{
report.error = Err( e.into() );
Err :: < (), () >( () )
});
output
}
else
{
let child = Command ::new( bin_path )
.args( &options.args )
.envs( env )
.stdout( Stdio ::piped() )
.stderr( Stdio ::piped() )
.current_dir( current_path )
.spawn()
.context( "failed to spawn process" )
.map_err( | e |
{
report.error = Err( e );
Err :: < (), () >( () )
});
if report.error.is_err()
{
return Err( report );
}
let child = child.unwrap();
child
.wait_with_output()
.context( "failed to wait on child" )
.map_err( | e |
{
report.error = Err( e );
Err :: < (), () >( () )
})
};
if report.error.is_err()
{
return Err( report );
}
let output = output.unwrap();
let out = String ::from_utf8( output.stdout )
.context( "Found invalid UTF-8" )
.map_err( | e |
{
report.error = Err( e );
Err :: < (), () >( () )
});
if out.is_err()
{
return Err( report );
}
let out = out.unwrap();
report.out = out;
let err = String ::from_utf8( output.stderr )
.context( "Found invalid UTF-8" )
.map_err( | e |
{
report.error = Err( e );
Err :: < (), () >( () )
});
if err.is_err()
{
return Err( report );
}
let err = err.unwrap();
report.err = err;
if output.status.success()
{
Ok( report )
}
else
{
report.error = Err( format_err!( "Process was finished with error code: {}", output.status ) );
Err( report )
}
}
#[ derive( Debug, Former ) ]
pub struct Run
{
bin_path: PathBuf,
current_path: PathBuf,
args: Vec< OsString >,
#[ former( default = false ) ]
joining_streams: bool,
env_variable: HashMap< String, String >,
}
impl RunFormer
{
pub fn run( self ) -> Result< Report, Report >
{
run( self.form() )
}
pub fn run_with_shell( self, exec_path: &str, ) -> Result< Report, Report >
{
let ( program, args ) =
if cfg!( target_os = "windows" )
{
( "cmd", [ "/C", exec_path ] )
}
else
{
( "sh", [ "-c", exec_path ] )
};
self
.args( args.into_iter().map( OsString ::from ).collect :: < Vec< _ > >() )
.bin_path( program )
.run()
}
}
#[ derive( Debug, ) ]
pub struct Report
{
pub command: String,
pub current_path: PathBuf,
pub out: String,
pub err: String,
pub error: Result< (), Error >
}
impl Clone for Report
{
fn clone( &self ) -> Self
{
Self
{
command: self.command.clone(),
current_path: self.current_path.clone(),
out: self.out.clone(),
err: self.err.clone(),
error: self.error.as_ref().map_err( | e | Error ::msg( e.to_string() ) ).copied(),
}
}
}
impl Default for Report
{
fn default() -> Self
{
Report
{
command: String ::default(),
current_path: PathBuf ::new(),
out: String ::default(),
err: String ::default(),
error: Ok( () ),
}
}
}
impl core ::fmt ::Display for Report
{
fn fmt( &self, f: &mut Formatter< '_ > ) -> core ::fmt ::Result
{
f.write_fmt( format_args!( "> {}\n", self.command ) )?;
f.write_fmt( format_args!( " @ {}\n\n", self.current_path.display() ) )?;
if !self.out.trim().is_empty()
{
f.write_fmt( format_args!( " {}\n", self.out.replace( '\n', "\n " ) ) )?;
}
if !self.err.trim().is_empty()
{
f.write_fmt( format_args!( " {}\n", self.err.replace( '\n', "\n " ) ) )?;
}
Ok( () )
}
}
}
crate ::mod_interface!
{
own use run;
own use Run;
own use Report;
}