Implement pipe magic in libimagrt

When we merged the changes in libimagrt so that it automatically detects
whether stdin/stdout is a TTY and provides the user with stderr in case
stdout is not a TTY, we forgot that things like

    imag foo | grep bar

becomes impossible with that, because imag detects that stdout is not a
tty and automatically uses stderr for output.

But in this case, we don't want that. The output has to be stdout in
this case.

With this change, we have a flag in the runtime ("--pipe-magic" or "-P",
globally available) which turns on "pipe magic".

The expected behaviour is the following, if "-P" is passed:

* If stdout is a TTY, we print to stdout
* If stdout is not a TTY, we print to stderr
* If stdin is not a TTY, we do not provide it

If "-P" is not passed, we allow the user of libimagrt to use stdin for
interactive stuff (the interactive stuff is not yet implemented).

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2018-03-10 15:35:05 +01:00
parent a0b989efdf
commit ce0bd9298a

View file

@ -49,6 +49,7 @@ pub struct Runtime<'a> {
configuration: Option<Value>, configuration: Option<Value>,
cli_matches: ArgMatches<'a>, cli_matches: ArgMatches<'a>,
store: Store, store: Store,
use_pipe_magic: bool,
stdin_is_tty: bool, stdin_is_tty: bool,
stdout_is_tty: bool, stdout_is_tty: bool,
} }
@ -148,12 +149,15 @@ impl<'a> Runtime<'a> {
Store::new(storepath, &config) Store::new(storepath, &config)
}; };
let pipe_magic = matches.is_present(Runtime::pipe_magic_name());
store_result.map(|store| { store_result.map(|store| {
Runtime { Runtime {
cli_matches: matches, cli_matches: matches,
configuration: config, configuration: config,
rtp: rtp, rtp: rtp,
store: store, store: store,
use_pipe_magic: pipe_magic,
stdout_is_tty: ::atty::is(::atty::Stream::Stdout), stdout_is_tty: ::atty::is(::atty::Stream::Stdout),
stdin_is_tty: ::atty::is(::atty::Stream::Stdin), stdin_is_tty: ::atty::is(::atty::Stream::Stdin),
} }
@ -249,6 +253,13 @@ impl<'a> Runtime<'a> {
.takes_value(true) .takes_value(true)
.value_name("LOGDESTS")) .value_name("LOGDESTS"))
.arg(Arg::with_name(Runtime::pipe_magic_name())
.long(Runtime::pipe_magic_name())
.short("P")
.help("Use pipe-detection. With this flag, imag expects a JSON store on STDIN if stdin is not a TTY and prints the store to STDOUT if it is not a TTY.")
.required(false)
.takes_value(false))
} }
/// Get the argument names of the Runtime which are available /// Get the argument names of the Runtime which are available
@ -338,6 +349,11 @@ impl<'a> Runtime<'a> {
"logging-destinations" "logging-destinations"
} }
/// Get the argument name for pipe magic
pub fn pipe_magic_name() -> &'static str {
"pipe-magic"
}
/// Initialize the internal logger /// Initialize the internal logger
fn init_logger(matches: &ArgMatches, config: Option<&Value>) { fn init_logger(matches: &ArgMatches, config: Option<&Value>) {
use log::set_max_level; use log::set_max_level;
@ -446,10 +462,10 @@ impl<'a> Runtime<'a> {
} }
pub fn stdout(&self) -> OutputProxy { pub fn stdout(&self) -> OutputProxy {
if self.stdout_is_tty { if self.use_pipe_magic && !self.stdout_is_tty {
OutputProxy::Out(::std::io::stdout())
} else {
OutputProxy::Err(::std::io::stderr()) OutputProxy::Err(::std::io::stderr())
} else {
OutputProxy::Out(::std::io::stdout())
} }
} }
@ -458,10 +474,10 @@ impl<'a> Runtime<'a> {
} }
pub fn stdin(&self) -> Option<Stdin> { pub fn stdin(&self) -> Option<Stdin> {
if self.stdin_is_tty { if self.use_pipe_magic && !self.stdin_is_tty {
Some(::std::io::stdin())
} else {
None None
} else {
Some(::std::io::stdin())
} }
} }
} }