2016-10-01 15:35:06 +00:00
//
// imag - the personal information management suite for the commandline
2018-02-07 01:48:53 +00:00
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
2016-10-01 15:35:06 +00:00
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
2016-01-20 19:46:42 +00:00
use std ::path ::PathBuf ;
2016-03-21 09:19:03 +00:00
use std ::process ::Command ;
use std ::env ;
2017-08-26 13:27:13 +00:00
use std ::process ::exit ;
2018-02-26 21:43:26 +00:00
use std ::io ::Stdin ;
2018-05-01 18:53:07 +00:00
use std ::sync ::Arc ;
2016-01-20 19:46:42 +00:00
pub use clap ::App ;
2018-04-03 16:23:52 +00:00
use clap ::AppSettings ;
2017-10-28 10:50:22 +00:00
use toml ::Value ;
2018-03-12 22:20:13 +00:00
use toml_query ::read ::TomlValueReadExt ;
2016-01-20 19:46:42 +00:00
use clap ::{ Arg , ArgMatches } ;
2018-10-30 17:40:50 +00:00
use failure ::ResultExt ;
use failure ::Fallible as Result ;
use failure ::Error ;
use failure ::err_msg ;
2016-01-20 19:46:42 +00:00
2017-10-28 10:50:22 +00:00
use configuration ::{ fetch_config , override_config , InternalConfiguration } ;
2016-01-20 19:46:42 +00:00
use logger ::ImagLogger ;
2018-03-01 17:18:23 +00:00
use io ::OutputProxy ;
2016-01-20 19:46:42 +00:00
2018-10-30 17:40:50 +00:00
use libimagerror ::errors ::ErrorMsg as EM ;
2017-08-26 13:27:13 +00:00
use libimagerror ::trace ::* ;
2017-06-25 15:08:18 +00:00
use libimagstore ::store ::Store ;
use libimagstore ::file_abstraction ::InMemoryFileAbstraction ;
2018-03-23 21:24:49 +00:00
use libimagutil ::debug_result ::DebugResult ;
2017-05-30 21:09:03 +00:00
use spec ::CliSpec ;
2016-01-20 19:46:42 +00:00
2017-02-21 14:31:24 +00:00
/// The Runtime object
///
/// This object contains the complete runtime environment of the imag application running.
2016-04-18 12:02:57 +00:00
#[ derive(Debug) ]
2016-01-20 19:46:42 +00:00
pub struct Runtime < ' a > {
rtp : PathBuf ,
2017-10-28 10:50:22 +00:00
configuration : Option < Value > ,
2016-02-20 20:15:09 +00:00
cli_matches : ArgMatches < ' a > ,
2016-01-20 19:46:42 +00:00
store : Store ,
}
impl < ' a > Runtime < ' a > {
2017-02-21 14:31:24 +00:00
/// Gets the CLI spec for the program and retreives the config file path (or uses the default on
/// in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
/// and builds the Runtime object with it.
///
2017-06-03 23:34:21 +00:00
/// The cli_app object should be initially build with the ::get_default_cli_builder() function.
2018-10-30 17:40:50 +00:00
pub fn new < C > ( cli_app : C ) -> Result < Runtime < ' a > >
2017-06-08 21:35:15 +00:00
where C : Clone + CliSpec < ' a > + InternalConfiguration
2017-06-03 23:34:21 +00:00
{
2016-05-16 16:59:02 +00:00
use libimagerror ::trace ::trace_error ;
2016-03-24 11:09:45 +00:00
2017-06-03 23:34:21 +00:00
let matches = cli_app . clone ( ) . matches ( ) ;
2016-04-05 15:46:51 +00:00
2017-06-08 21:35:15 +00:00
let rtp = get_rtp_match ( & matches ) ;
let configpath = matches . value_of ( Runtime ::arg_config_name ( ) )
. map_or_else ( | | rtp . clone ( ) , PathBuf ::from ) ;
2017-06-25 14:58:25 +00:00
debug! ( " Config path = {:?} " , configpath ) ;
2018-10-30 17:40:50 +00:00
let config = match fetch_config ( & configpath ) ? {
None = > {
return Err ( err_msg ( " No configuration file found " ) )
. context ( err_msg ( " Maybe try to use 'imag-init' to initialize imag? " ) )
. context ( err_msg ( " Continuing without configuration file " ) )
. context ( err_msg ( " Cannot instantiate runtime " ) )
. map_err ( Error ::from ) ;
2017-06-08 21:35:15 +00:00
} ,
2018-10-30 17:40:50 +00:00
Some ( mut config ) = > {
2017-10-28 10:50:22 +00:00
if let Err ( e ) = override_config ( & mut config , get_override_specs ( & matches ) ) {
2017-06-08 21:35:15 +00:00
error! ( " Could not apply config overrides " ) ;
trace_error ( & e ) ;
// TODO: continue question (interactive)
}
Some ( config )
}
} ;
2017-06-12 17:10:53 +00:00
Runtime ::_new ( cli_app , matches , config )
2017-06-08 21:35:15 +00:00
}
/// Builds the Runtime object using the given `config`.
2018-10-30 17:40:50 +00:00
pub fn with_configuration < C > ( cli_app : C , config : Option < Value > ) -> Result < Runtime < ' a > >
2017-06-08 21:35:15 +00:00
where C : Clone + CliSpec < ' a > + InternalConfiguration
2017-06-12 17:10:53 +00:00
{
let matches = cli_app . clone ( ) . matches ( ) ;
Runtime ::_new ( cli_app , matches , config )
}
2018-10-30 17:40:50 +00:00
fn _new < C > ( cli_app : C , matches : ArgMatches < ' a > , config : Option < Value > ) -> Result < Runtime < ' a > >
2017-06-12 17:10:53 +00:00
where C : Clone + CliSpec < ' a > + InternalConfiguration
2017-06-08 21:35:15 +00:00
{
2017-06-06 20:36:20 +00:00
if cli_app . enable_logging ( ) {
2017-08-26 13:27:13 +00:00
Runtime ::init_logger ( & matches , config . as_ref ( ) )
2017-06-03 23:34:21 +00:00
}
2016-03-25 18:42:10 +00:00
2017-06-08 21:35:15 +00:00
let rtp = get_rtp_match ( & matches ) ;
let storepath = matches . value_of ( Runtime ::arg_storepath_name ( ) )
2016-05-03 21:10:32 +00:00
. map_or_else ( | | {
2016-01-20 19:46:42 +00:00
let mut spath = rtp . clone ( ) ;
2016-01-28 19:05:43 +00:00
spath . push ( " store " ) ;
2016-01-20 19:46:42 +00:00
spath
2016-05-03 21:10:32 +00:00
} , PathBuf ::from ) ;
2016-03-05 11:36:11 +00:00
2017-06-25 14:58:25 +00:00
debug! ( " RTP path = {:?} " , rtp ) ;
debug! ( " Store path = {:?} " , storepath ) ;
2018-10-31 22:05:20 +00:00
debug! ( " CLI = {:?} " , matches ) ;
2017-06-25 14:58:25 +00:00
2017-06-25 14:57:48 +00:00
let store_result = if cli_app . use_inmemory_fs ( ) {
Store ::new_with_backend ( storepath ,
2017-12-22 10:24:04 +00:00
& config ,
2018-05-01 18:53:07 +00:00
Arc ::new ( InMemoryFileAbstraction ::default ( ) ) )
2017-06-25 14:57:48 +00:00
} else {
2017-12-22 10:24:04 +00:00
Store ::new ( storepath , & config )
2017-06-25 14:57:48 +00:00
} ;
store_result . map ( | store | {
2016-01-21 17:43:03 +00:00
Runtime {
cli_matches : matches ,
2017-06-08 21:35:15 +00:00
configuration : config ,
2016-01-21 17:43:03 +00:00
rtp : rtp ,
store : store ,
}
} )
2018-10-30 17:40:50 +00:00
. context ( err_msg ( " Cannot instantiate runtime " ) )
. map_err ( Error ::from )
2016-01-20 19:46:42 +00:00
}
2017-02-21 14:31:24 +00:00
///
/// Get a commandline-interface builder object from `clap`
///
/// This commandline interface builder object already contains some predefined interface flags:
/// * -v | --verbose for verbosity
/// * --debug for debugging
/// * -c <file> | --config <file> for alternative configuration file
/// * -r <path> | --rtp <path> for alternative runtimepath
/// * --store <path> for alternative store path
/// Each has the appropriate help text included.
///
/// The `appname` shall be "imag-<command>".
///
2016-01-20 19:46:42 +00:00
pub fn get_default_cli_builder ( appname : & ' a str ,
version : & ' a str ,
about : & ' a str )
2016-02-20 20:15:09 +00:00
-> App < ' a , ' a >
2016-01-20 19:46:42 +00:00
{
App ::new ( appname )
. version ( version )
. author ( " Matthias Beyer <mail@beyermatthias.de> " )
. about ( about )
2018-04-03 16:23:52 +00:00
. settings ( & [ AppSettings ::AllowExternalSubcommands ] )
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_verbosity_name ( ) )
2016-01-20 19:46:42 +00:00
. short ( " v " )
. long ( " verbose " )
2018-05-04 09:19:47 +00:00
. help ( " Set log level " )
2016-01-20 19:46:42 +00:00
. required ( false )
2017-08-26 13:27:13 +00:00
. takes_value ( true )
. possible_values ( & [ " trace " , " debug " , " info " , " warn " , " error " ] )
. value_name ( " LOGLEVEL " ) )
2016-01-20 19:46:42 +00:00
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_debugging_name ( ) )
2016-01-20 19:46:42 +00:00
. long ( " debug " )
2018-05-04 09:20:09 +00:00
. help ( " Enables debugging output. Shortcut for '--verbose debug' " )
2016-01-20 19:46:42 +00:00
. required ( false )
. takes_value ( false ) )
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_no_color_output_name ( ) )
2016-08-02 09:39:01 +00:00
. long ( " no-color " )
. help ( " Disable color output " )
. required ( false )
. takes_value ( false ) )
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_config_name ( ) )
2016-01-20 19:46:42 +00:00
. long ( " config " )
. help ( " Path to alternative config file " )
. required ( false )
2018-05-04 09:21:49 +00:00
. validator ( ::libimagutil ::cli_validators ::is_existing_path )
2016-01-20 19:46:42 +00:00
. takes_value ( true ) )
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_config_override_name ( ) )
2016-07-24 16:05:29 +00:00
. long ( " override-config " )
. help ( " Override a configuration settings. Use 'key=value' pairs, where the key is a path in the TOML configuration. The value must be present in the configuration and be convertible to the type of the configuration setting. If the argument does not contain a '=', it gets ignored. Setting Arrays and Tables is not yet supported. " )
. required ( false )
. takes_value ( true ) )
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_runtimepath_name ( ) )
2016-01-20 19:46:42 +00:00
. long ( " rtp " )
. help ( " Alternative runtimepath " )
. required ( false )
2018-05-04 09:21:49 +00:00
. validator ( ::libimagutil ::cli_validators ::is_directory )
2016-01-20 19:46:42 +00:00
. takes_value ( true ) )
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_storepath_name ( ) )
2016-01-20 19:46:42 +00:00
. long ( " store " )
. help ( " Alternative storepath. Must be specified as full path, can be outside of the RTP " )
. required ( false )
2018-05-04 09:21:49 +00:00
. validator ( ::libimagutil ::cli_validators ::is_directory )
2016-01-20 19:46:42 +00:00
. takes_value ( true ) )
2016-03-21 09:19:03 +00:00
2016-08-06 15:43:14 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_editor_name ( ) )
2016-03-21 09:19:03 +00:00
. long ( " editor " )
. help ( " Set editor " )
. required ( false )
. takes_value ( true ) )
2016-10-28 07:42:44 +00:00
2017-08-26 13:27:13 +00:00
. arg ( Arg ::with_name ( Runtime ::arg_logdest_name ( ) )
. long ( Runtime ::arg_logdest_name ( ) )
. help ( " Override the logging destinations from the configuration: values can be seperated by ',', a value of '-' marks the stderr output, everything else is expected to be a path " )
. required ( false )
. takes_value ( true )
. value_name ( " LOGDESTS " ) )
2016-01-20 19:46:42 +00:00
}
2017-02-21 14:31:24 +00:00
/// Get the argument names of the Runtime which are available
2016-08-06 15:43:14 +00:00
pub fn arg_names ( ) -> Vec < & 'static str > {
vec! [
Runtime ::arg_verbosity_name ( ) ,
Runtime ::arg_debugging_name ( ) ,
Runtime ::arg_no_color_output_name ( ) ,
Runtime ::arg_config_name ( ) ,
Runtime ::arg_config_override_name ( ) ,
Runtime ::arg_runtimepath_name ( ) ,
Runtime ::arg_storepath_name ( ) ,
Runtime ::arg_editor_name ( ) ,
]
}
2017-02-21 14:31:24 +00:00
/// Get the verbosity argument name for the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_verbosity_name ( ) -> & 'static str {
" verbosity "
}
2017-02-21 14:31:24 +00:00
/// Get the debugging argument name for the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_debugging_name ( ) -> & 'static str {
" debugging "
}
2017-02-21 14:31:24 +00:00
/// Get the argument name for no color output of the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_no_color_output_name ( ) -> & 'static str {
" no-color-output "
}
2017-02-21 14:31:24 +00:00
/// Get the config argument name for the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_config_name ( ) -> & 'static str {
" config "
}
2017-02-21 14:31:24 +00:00
/// Get the config-override argument name for the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_config_override_name ( ) -> & 'static str {
" config-override "
}
2017-02-21 14:31:24 +00:00
/// Get the runtime argument name for the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_runtimepath_name ( ) -> & 'static str {
" runtimepath "
}
2017-02-21 14:31:24 +00:00
/// Get the storepath argument name for the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_storepath_name ( ) -> & 'static str {
" storepath "
}
2017-02-21 14:31:24 +00:00
/// Get the editor argument name for the Runtime
2016-08-06 15:43:14 +00:00
pub fn arg_editor_name ( ) -> & 'static str {
" editor "
}
2017-09-02 08:29:10 +00:00
/// Extract the Store object from the Runtime object, destroying the Runtime object
///
/// # Warning
///
/// This function is for testing _only_! It can be used to re-build a Runtime object with an
/// alternative Store.
#[ cfg(feature = " testing " ) ]
pub fn extract_store ( self ) -> Store {
self . store
}
/// Re-set the Store object within
///
/// # Warning
///
/// This function is for testing _only_! It can be used to re-build a Runtime object with an
/// alternative Store.
#[ cfg(feature = " testing " ) ]
pub fn with_store ( mut self , s : Store ) -> Self {
self . store = s ;
self
}
2017-08-26 13:27:13 +00:00
/// Get the argument name for the logging destination
pub fn arg_logdest_name ( ) -> & 'static str {
" logging-destinations "
}
2018-10-31 22:01:52 +00:00
#[ cfg(feature = " pub_logging_initialization " ) ]
pub fn init_logger ( matches : & ArgMatches , config : Option < & Value > ) {
Self ::_init_logger ( matches , config )
}
#[ cfg(not(feature = " pub_logging_initialization " )) ]
fn init_logger ( matches : & ArgMatches , config : Option < & Value > ) {
Self ::_init_logger ( matches , config )
}
2017-02-21 14:31:24 +00:00
/// Initialize the internal logger
2018-06-20 00:07:17 +00:00
///
/// If the environment variable "IMAG_LOG_ENV" is set, this simply
/// initializes a env-logger instance. Errors are ignored in this case.
/// If the environment variable is not set, this initializes the internal imag logger. On
/// error, this exits (as there is nothing we can do about that)
2018-10-31 22:01:52 +00:00
fn _init_logger ( matches : & ArgMatches , config : Option < & Value > ) {
2017-12-06 17:08:25 +00:00
use log ::set_max_level ;
use log ::set_boxed_logger ;
2016-05-24 14:43:04 +00:00
use std ::env ::var as env_var ;
use env_logger ;
2016-01-20 19:46:42 +00:00
2016-05-24 14:43:04 +00:00
if env_var ( " IMAG_LOG_ENV " ) . is_ok ( ) {
2017-12-07 21:07:01 +00:00
let _ = env_logger ::try_init ( ) ;
2016-05-24 14:43:04 +00:00
} else {
2017-12-06 17:08:25 +00:00
let logger = ImagLogger ::new ( matches , config )
. map_err_trace ( )
. unwrap_or_else ( | _ | exit ( 1 ) ) ;
set_max_level ( logger . global_loglevel ( ) . to_level_filter ( ) ) ;
debug! ( " Init logger with {} " , logger . global_loglevel ( ) ) ;
set_boxed_logger ( Box ::new ( logger ) )
. map_err ( | e | panic! ( " Could not setup logger: {:?} " , e ) )
. ok ( ) ;
2016-05-24 14:43:04 +00:00
}
2016-01-20 19:46:42 +00:00
}
2017-02-21 14:31:24 +00:00
/// Get the verbosity flag value
2016-01-20 19:46:42 +00:00
pub fn is_verbose ( & self ) -> bool {
self . cli_matches . is_present ( " verbosity " )
}
2017-02-21 14:31:24 +00:00
/// Get the debugging flag value
2016-01-20 19:46:42 +00:00
pub fn is_debugging ( & self ) -> bool {
self . cli_matches . is_present ( " debugging " )
}
2017-02-21 14:31:24 +00:00
/// Get the runtimepath
2016-01-20 19:46:42 +00:00
pub fn rtp ( & self ) -> & PathBuf {
& self . rtp
}
2017-02-21 14:31:24 +00:00
/// Get the commandline interface matches
2016-01-20 19:46:42 +00:00
pub fn cli ( & self ) -> & ArgMatches {
& self . cli_matches
}
2017-02-21 14:31:24 +00:00
/// Get the configuration object
2017-10-28 10:50:22 +00:00
pub fn config ( & self ) -> Option < & Value > {
2016-03-26 18:50:13 +00:00
self . configuration . as_ref ( )
}
2017-02-21 14:31:24 +00:00
/// Get the store object
2016-01-20 19:46:42 +00:00
pub fn store ( & self ) -> & Store {
& self . store
}
2017-02-21 14:31:24 +00:00
/// Get a editor command object which can be called to open the $EDITOR
2018-10-30 17:40:50 +00:00
pub fn editor ( & self ) -> Result < Option < Command > > {
2016-03-21 09:19:03 +00:00
self . cli ( )
. value_of ( " editor " )
. map ( String ::from )
2018-03-12 22:20:13 +00:00
. or_else ( | | {
self . config ( )
. and_then ( | v | match v . read ( " rt.editor " ) {
Ok ( Some ( & Value ::String ( ref s ) ) ) = > Some ( s . clone ( ) ) ,
_ = > None , // FIXME silently ignore errors in config is bad
} )
} )
2016-03-21 09:19:03 +00:00
. or ( env ::var ( " EDITOR " ) . ok ( ) )
2018-10-30 17:40:50 +00:00
. ok_or_else ( | | Error ::from ( EM ::IO ) )
2018-03-23 21:24:49 +00:00
. map_dbg ( | s | format! ( " Editing with ' {} ' " , s ) )
2018-03-12 22:20:32 +00:00
. and_then ( | s | {
2018-04-08 18:28:11 +00:00
let mut split = s . split_whitespace ( ) ;
2018-03-12 22:20:32 +00:00
let command = split . next ( ) ;
if command . is_none ( ) {
2018-03-23 21:24:49 +00:00
return Ok ( None )
2018-03-12 22:20:32 +00:00
}
let mut c = Command ::new ( command . unwrap ( ) ) ; // secured above
c . args ( split ) ;
2018-10-30 17:40:50 +00:00
c . stdin ( ::std ::fs ::File ::open ( " /dev/tty " ) . context ( EM ::IO ) ? ) ;
2018-03-12 19:07:19 +00:00
c . stderr ( ::std ::process ::Stdio ::inherit ( ) ) ;
2018-03-23 21:24:49 +00:00
Ok ( Some ( c ) )
2018-03-12 19:07:19 +00:00
} )
2016-03-21 09:19:03 +00:00
}
2018-02-26 21:43:26 +00:00
2018-03-01 17:18:23 +00:00
pub fn stdout ( & self ) -> OutputProxy {
2018-04-18 15:56:09 +00:00
OutputProxy ::Out ( ::std ::io ::stdout ( ) )
2018-02-26 21:43:26 +00:00
}
2018-03-01 17:18:23 +00:00
pub fn stderr ( & self ) -> OutputProxy {
2018-03-04 14:41:32 +00:00
OutputProxy ::Err ( ::std ::io ::stderr ( ) )
2018-02-26 21:43:26 +00:00
}
2018-03-01 17:18:23 +00:00
pub fn stdin ( & self ) -> Option < Stdin > {
2018-04-18 15:42:04 +00:00
Some ( ::std ::io ::stdin ( ) )
2018-02-26 21:43:26 +00:00
}
2018-04-03 16:23:01 +00:00
/// Helper for handling subcommands which are not available.
///
/// # Example
///
/// For example someone calls `imag foo bar`. If `imag-foo` is in the $PATH, but it has no
/// subcommand `bar`, the `imag-foo` binary is able to automatically forward the invokation to a
/// `imag-foo-bar` binary which might be in $PATH.
///
/// It needs to call `Runtime::handle_unknown_subcommand` with the following parameters:
///
/// 1. The "command" which was issued. In the example this would be `"imag-foo"`
/// 2. The "subcommand" which is missing: `"bar"` in the example
/// 3. The `ArgMatches` object from the call, so that this routine can forward all flags passed
/// to the `bar` subcommand.
///
2018-04-08 18:38:46 +00:00
/// # Warning
///
/// If, and only if, the subcommand does not exist (as in `::std::io::ErrorKind::NotFound`),
/// this function exits with 1 as exit status.
///
2018-04-03 16:23:01 +00:00
/// # Return value
///
/// On success, the exit status object of the `Command` invocation is returned.
///
/// # Details
///
/// The `IMAG_RTP` variable is set for the child process. It is set to the current runtime path.
///
/// Stdin, stdout and stderr are inherited to the child process.
///
/// This function **blocks** until the child returns.
///
pub fn handle_unknown_subcommand < S : AsRef < str > > ( & self ,
command : S ,
subcommand : S ,
args : & ArgMatches )
2018-10-30 17:40:50 +00:00
-> Result < ::std ::process ::ExitStatus >
2018-04-03 16:23:01 +00:00
{
use std ::io ::Write ;
use std ::io ::ErrorKind ;
let rtp_str = self . rtp ( )
. to_str ( )
. map ( String ::from )
2018-10-30 17:40:50 +00:00
. ok_or_else ( | | Error ::from ( EM ::IO ) ) ? ;
2018-04-03 16:23:01 +00:00
let command = format! ( " {} - {} " , command . as_ref ( ) , subcommand . as_ref ( ) ) ;
let subcommand_args = args . values_of ( " " )
. map ( | sx | sx . map ( String ::from ) . collect ( ) )
. unwrap_or_else ( | | vec! [ ] ) ;
Command ::new ( & command )
. stdin ( ::std ::process ::Stdio ::inherit ( ) )
. stdout ( ::std ::process ::Stdio ::inherit ( ) )
. stderr ( ::std ::process ::Stdio ::inherit ( ) )
. args ( & subcommand_args [ .. ] )
. env ( " IMAG_RTP " , rtp_str )
. spawn ( )
. and_then ( | mut c | c . wait ( ) )
. map_err ( | e | match e . kind ( ) {
ErrorKind ::NotFound = > {
let mut out = self . stdout ( ) ;
if let Err ( e ) = writeln! ( out , " No such command: '{}' " , command ) {
return e ;
}
if let Err ( e ) = writeln! ( out , " See 'imag --help' for available subcommands " ) {
return e ;
}
2018-04-08 18:38:46 +00:00
::std ::process ::exit ( 1 )
2018-04-03 16:23:01 +00:00
} ,
_ = > e ,
} )
2018-10-30 17:40:50 +00:00
. context ( EM ::IO )
. map_err ( Error ::from )
2018-04-03 16:23:01 +00:00
}
2016-01-20 19:46:42 +00:00
}
2018-02-26 21:10:20 +00:00
/// Exported for the `imag` command, you probably do not want to use that.
pub fn get_rtp_match < ' a > ( matches : & ArgMatches < ' a > ) -> PathBuf {
2017-06-08 21:35:15 +00:00
use std ::env ;
matches . value_of ( Runtime ::arg_runtimepath_name ( ) )
. map_or_else ( | | {
2017-10-31 14:29:45 +00:00
if let Ok ( home ) = env ::var ( " IMAG_RTP " ) {
return PathBuf ::from ( home ) ;
}
match env ::var ( " HOME " ) {
Ok ( home ) = > {
let mut p = PathBuf ::from ( home ) ;
p . push ( " .imag " ) ;
return p ;
} ,
Err ( _ ) = > panic! ( " You seem to be $HOME-less. Please get a $HOME before using this \
software . We are sorry for you and hope you have some \
accommodation anyways . " ),
}
2017-06-08 21:35:15 +00:00
} , PathBuf ::from )
}
2016-07-24 16:06:01 +00:00
fn get_override_specs ( matches : & ArgMatches ) -> Vec < String > {
matches
. values_of ( " config-override " )
. map ( | values | {
values
. filter ( | s | {
let b = s . contains ( " = " ) ;
if ! b { warn! ( " override '{}' does not contain '=' - will be ignored! " , s ) ; }
b
} )
. map ( String ::from )
. collect ( )
} )
. unwrap_or ( vec! [ ] )
}