Implement proxy object where runtime configures output
This is another approach for providing access to stdin/out/err via libimagrt::runtime::Runtime. The Runtime object does configure which output gets returned (stdout if stdout is a tty, else stderr). With this we can change libimagrt to read/write the store from/to stdin/stdout without the user noticing that she does not write to stdout but stderr. Reading from stdin is not possible then, though.
This commit is contained in:
parent
50461b839a
commit
19e9dfe33c
3 changed files with 86 additions and 46 deletions
66
lib/core/libimagrt/src/io.rs
Normal file
66
lib/core/libimagrt/src/io.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
//! Proxy objects for std::io::Stdin, std::io::Stdout, std::io::Stderr
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::io::Write;
|
||||
|
||||
/// Proxy object for output
|
||||
///
|
||||
/// This is returned by `Runtime::stdout()` does implement `Write`. So you can
|
||||
/// `write!(rt.stdout(), "some things")` and it just works.
|
||||
///
|
||||
/// The `Runtime` has to decide whether the OutputProxy should write to stdout, stderr or simply be
|
||||
/// a "sink" which does not write to either.
|
||||
///
|
||||
pub enum OutputProxy {
|
||||
Out,
|
||||
Err,
|
||||
Sink,
|
||||
}
|
||||
|
||||
impl Write for OutputProxy {
|
||||
fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
|
||||
match *self {
|
||||
OutputProxy::Out => ::std::io::stdout().write(buf),
|
||||
OutputProxy::Err => ::std::io::stderr().write(buf),
|
||||
OutputProxy::Sink => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> ::std::io::Result<()> {
|
||||
match *self {
|
||||
OutputProxy::Out => ::std::io::stdout().flush(),
|
||||
OutputProxy::Err => ::std::io::stderr().flush(),
|
||||
OutputProxy::Sink => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Debug for OutputProxy {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||
match *self {
|
||||
OutputProxy::Out => write!(f, "OutputProxy(Stdout)"),
|
||||
OutputProxy::Err => write!(f, "OutputProxy(Stderr)"),
|
||||
OutputProxy::Sink => write!(f, "OutputProxy(Sink)"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,6 +57,7 @@ extern crate libimaginteraction;
|
|||
pub mod error;
|
||||
pub mod configuration;
|
||||
pub mod logger;
|
||||
pub mod io;
|
||||
pub mod runtime;
|
||||
pub mod setup;
|
||||
pub mod spec;
|
||||
|
|
|
@ -22,8 +22,6 @@ use std::process::Command;
|
|||
use std::env;
|
||||
use std::process::exit;
|
||||
use std::io::Stdin;
|
||||
use std::io::Write;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use clap::App;
|
||||
use toml::Value;
|
||||
|
@ -35,6 +33,7 @@ use error::RuntimeError;
|
|||
use error::RuntimeErrorKind;
|
||||
use error::ResultExt;
|
||||
use logger::ImagLogger;
|
||||
use io::OutputProxy;
|
||||
|
||||
use libimagerror::trace::*;
|
||||
use libimagstore::store::Store;
|
||||
|
@ -50,43 +49,8 @@ pub struct Runtime<'a> {
|
|||
configuration: Option<Value>,
|
||||
cli_matches: ArgMatches<'a>,
|
||||
store: Store,
|
||||
resources: Resources,
|
||||
}
|
||||
|
||||
/// Resources for standard output, error output and input stream
|
||||
///
|
||||
/// These resources are set depending whether stdin/stdout are TTYs or not, but they are always
|
||||
/// provided.
|
||||
struct Resources {
|
||||
stdout : Box<Write>,
|
||||
stderr : Box<Write>,
|
||||
stdin : Option<Stdin>,
|
||||
}
|
||||
|
||||
impl Debug for Resources {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||
write!(f, "Resources(...)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a Resource object
|
||||
///
|
||||
/// If stdout is not a TTY, the Resources::stdout will actually point to stderr
|
||||
/// If stdin is not a TTY, it will be None
|
||||
fn aquire_resources() -> Resources {
|
||||
Resources {
|
||||
stdout: if ::atty::is(::atty::Stream::Stdout) {
|
||||
Box::new(::std::io::stdout())
|
||||
} else {
|
||||
Box::new(::std::io::stderr())
|
||||
},
|
||||
stderr: Box::new(::std::io::stderr()),
|
||||
stdin: if ::atty::is(::atty::Stream::Stdin) {
|
||||
Some(::std::io::stdin())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
stdin_is_tty: bool,
|
||||
stdout_is_tty: bool,
|
||||
}
|
||||
|
||||
impl<'a> Runtime<'a> {
|
||||
|
@ -190,7 +154,8 @@ impl<'a> Runtime<'a> {
|
|||
configuration: config,
|
||||
rtp: rtp,
|
||||
store: store,
|
||||
resources: aquire_resources(),
|
||||
stdout_is_tty: ::atty::is(::atty::Stream::Stdout),
|
||||
stdin_is_tty: ::atty::is(::atty::Stream::Stdin),
|
||||
}
|
||||
})
|
||||
.chain_err(|| RuntimeErrorKind::Instantiate)
|
||||
|
@ -480,16 +445,24 @@ impl<'a> Runtime<'a> {
|
|||
.map(Command::new)
|
||||
}
|
||||
|
||||
pub fn stdout(&self) -> &Box<Write> {
|
||||
&self.resources.stdout
|
||||
pub fn stdout(&self) -> OutputProxy {
|
||||
if self.stdout_is_tty {
|
||||
OutputProxy::Out
|
||||
} else {
|
||||
OutputProxy::Err
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stderr(&self) -> &Box<Write> {
|
||||
&self.resources.stderr
|
||||
pub fn stderr(&self) -> OutputProxy {
|
||||
OutputProxy::Err
|
||||
}
|
||||
|
||||
pub fn stdin(&self) -> Option<&Stdin> {
|
||||
self.resources.stdin.as_ref()
|
||||
pub fn stdin(&self) -> Option<Stdin> {
|
||||
if self.stdin_is_tty {
|
||||
Some(::std::io::stdin())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue