~matthiasbeyer/imag

7ba3b2f2b0a8503a528c4f37ce932314e6fb44d6 — Matthias Beyer 1 year, 1 month ago 4c9befd + b23b816
Merge branch 'runtime-more-io-control' into master

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
M bin/core/imag/src/main.rs => bin/core/imag/src/main.rs +2 -1
@@ 360,6 360,7 @@ fn forward_commandline_arguments(m: &ArgMatches, scmd: &mut Vec<String>) {
    push(Some("rtp"), "runtimepath", m , scmd);
    push(Some("store"), "storepath", m , scmd);
    push(Some("editor"), "editor", m , scmd);
    push(Some("ignore-ids"), "ignore-ids", m , scmd);
    push(Some("pipe-input"), "pipe-input", m , scmd);
    push(Some("pipe-output"), "pipe-output", m , scmd);
}


M bin/domain/imag-contact/src/lib.rs => bin/domain/imag-contact/src/lib.rs +1 -1
@@ 318,7 318,7 @@ fn find(rt: &Runtime) -> Result<()> {

    let mut i = 0;

    if !rt.output_is_pipe() || rt.ignore_ids() {
    if !rt.output_is_pipe() || rt.output_data_pipe() {
        if scmd.is_present("json") {
            iterator
                .filter_ok(|tpl| tpl.0)

M doc/user/src/conventions.md => doc/user/src/conventions.md +1 -1
@@ 22,7 22,7 @@ the following ways:
* If the standard output is not a pipe, the imag module does not print the
  StoreIDs it touched.

This behaviour can be overridden with the `--ignore-ids` flag.
This behaviour can be overridden with the `--pipe-input` and `--pipe-output` flag.

## Versioning


M lib/core/libimagrt/src/runtime.rs => lib/core/libimagrt/src/runtime.rs +43 -26
@@ 60,7 60,8 @@ pub struct Runtime<'a> {
    has_output_pipe: bool,
    has_input_pipe: bool,

    ignore_ids: bool
    input_data: bool,
    output_data: bool,
}

impl<'a> Runtime<'a> {


@@ 140,11 141,13 @@ impl<'a> Runtime<'a> {

        let has_output_pipe = !atty::is(atty::Stream::Stdout);
        let has_input_pipe  = !atty::is(atty::Stream::Stdin);
        let ignore_ids      = matches.is_present("ignore-ids");
        let input_data      = matches.is_present("input-pipe-data");
        let output_data     = matches.is_present("output-pipe-data");

        debug!("has output pipe = {}", has_output_pipe);
        debug!("has input pipe  = {}", has_input_pipe);
        debug!("ignore ids      = {}", ignore_ids);
        debug!("has output pipe  = {}", has_output_pipe);
        debug!("has input pipe   = {}", has_input_pipe);
        debug!("input pipe data  = {}", input_data);
        debug!("output pipe data = {}", output_data);

        store_result.map(|store| Runtime {
            cli_matches: matches,


@@ 154,7 157,8 @@ impl<'a> Runtime<'a> {

            has_output_pipe,
            has_input_pipe,
            ignore_ids,
            input_data,
            output_data,
        })
        .context(err_msg("Cannot instantiate runtime"))
        .map_err(Error::from)


@@ 238,12 242,23 @@ impl<'a> Runtime<'a> {
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("ignore-ids")
                .long("ignore-ids")
                .help("Do not assume that the output is a pipe to another imag command. This overrides the default behaviour where imag only prints the IDs of the touched entries to stdout if stdout is a pipe.")
                .long_help("Without this flag, imag assumes that if stdout is a pipe, the command imag pipes to is also an imag command. Thus, it prints the IDs of the processed entries to stdout and automatically redirects the command output to stderr. By providing this flag, this behaviour gets overridden: The IDs are not printed at all and the normal output is printed to stdout.")
                .required(false)
                .takes_value(false))
            .arg(Arg::with_name("input-pipe-data")
                 .long("pipe-input")
                 .short("I")
                 .required(false)
                 .takes_value(false)
                 .multiple(false)
                 .help("Do not expect imag ids on stdin if stdin is a pipe.")
                 )

            .arg(Arg::with_name("output-pipe-data")
                 .long("pipe-output")
                 .short("O")
                 .required(false)
                 .takes_value(false)
                 .multiple(false)
                 .help("Do not print imag ids to stdout if stdout is a pipe.")
                 )

    }



@@ 406,24 421,26 @@ impl<'a> Runtime<'a> {
        self.has_input_pipe
    }

    /// Alias for Runtime::input_is_pipe()
    /// Check whether the runtime expects imag ids from stdin
    pub fn ids_from_stdin(&self) -> bool {
        self.input_is_pipe()
        self.input_is_pipe() && !self.input_data
    }

    /// Check whether the runtime allows data to be piped into the program
    pub fn input_data_pipe(&self) -> bool {
        self.input_is_pipe() && self.input_data
    }

    /// Check whether the runtime ignores touched ids
    ///
    /// "Ignoring" in this context means whether the runtime prints them or not.
    pub fn ignore_ids(&self) -> bool {
        self.ignore_ids
    /// Check whether the runtime allows data to be piped into the program
    pub fn output_data_pipe(&self) -> bool {
        self.output_is_pipe() && self.output_data
    }

    pub fn stdout(&self) -> OutputProxy {
        if self.output_is_pipe() && !self.ignore_ids {
            OutputProxy::Err(::std::io::stderr())
        } else {
        if self.output_is_pipe() && self.output_data {
            OutputProxy::Out(::std::io::stdout())
        } else {
            OutputProxy::Err(::std::io::stderr())
        }
    }



@@ 432,10 449,10 @@ impl<'a> Runtime<'a> {
    }

    pub fn stdin(&self) -> Option<Stdin> {
        if self.has_input_pipe {
            None
        } else {
        if self.input_is_pipe() && self.input_data {
            Some(::std::io::stdin())
        } else {
            None
        }
    }



@@ 545,7 562,7 @@ impl<'a> Runtime<'a> {
    fn report_touched_id(&self, id: &StoreId, output: &mut StdoutLock) -> Result<bool> {
        use std::io::Write;

        if self.output_is_pipe() && !self.ignore_ids {
        if self.output_is_pipe() && !self.output_data {
            trace!("Reporting: {} to {:?}", id, output);
            if let Err(e) = writeln!(output, "{}", id) {
                return if e.kind() == std::io::ErrorKind::BrokenPipe {

M tests/ui/src/imag_category.rs => tests/ui/src/imag_category.rs +6 -2
@@ 28,7 28,8 @@ pub fn binary(tempdir: &TempDir) -> Command {
pub fn call(tmpdir: &TempDir, args: &[&str]) -> Vec<String> {
    let mut binary = binary(tmpdir);
    binary.stdin(std::process::Stdio::inherit());
    binary.arg("--ignore-ids");
    binary.arg("--pipe-input");
    binary.arg("--pipe-output");
    binary.args(args);
    debug!("Command = {:?}", binary);
    crate::imag::stdout_of_command(binary)


@@ 44,7 45,10 @@ fn test_new_entry_has_no_category() {
    let (assert, stderr_output) = {
        let mut binary = binary(&imag_home);
        binary.stdin(std::process::Stdio::inherit());
        binary.args(&["--ignore-ids", "get", "test"]);
        binary.arg("--pipe-input");
        binary.arg("--pipe-output");
        binary.arg("get");
        binary.arg("test");
        crate::imag::stderr_of_command(&mut binary)
    };


M tests/ui/src/imag_grep.rs => tests/ui/src/imag_grep.rs +2 -1
@@ 24,7 24,8 @@ use assert_fs::fixture::TempDir;

pub fn call(tempdir: &TempDir, pattern: &str) -> Vec<String> {
    let mut binary = binary(tempdir);
    binary.arg("--ignore-ids");
    binary.arg("--pipe-input");
    binary.arg("--pipe-output");
    binary.arg(pattern);

    // ensure that stdin is not used by the child process

M tests/ui/src/imag_header.rs => tests/ui/src/imag_header.rs +1 -1
@@ 56,7 56,7 @@ fn test_imag_version_as_semver_string() {
    crate::imag_init::call(&imag_home);
    crate::imag_create::call(&imag_home, &["test"]);

    let output              = call(&imag_home, &["--ignore-ids", "test", "read", "imag.version"]);
    let output              = call(&imag_home, &["--pipe-input", "--pipe-output", "test", "read", "imag.version"]);
    let version             = version::version!();
    debug!("output =  {:?}", output);
    assert_eq!(output.len(), 1);

M tests/ui/src/imag_mv.rs => tests/ui/src/imag_mv.rs +2 -1
@@ 30,7 30,8 @@ pub fn binary(tempdir: &TempDir) -> Command {
pub fn call(tmpdir: &TempDir, src: &str, dst: &str) {
    let mut binary = binary(tmpdir);
    binary.stdin(std::process::Stdio::inherit());
    binary.arg("--ignore-ids");
    binary.arg("--pipe-input");
    binary.arg("--pipe-output");
    binary.arg(src);
    binary.arg(dst);


M tests/ui/src/imag_tag.rs => tests/ui/src/imag_tag.rs +2 -1
@@ 28,7 28,8 @@ pub fn binary(tempdir: &TempDir) -> Command {
pub fn call(tmpdir: &TempDir, args: &[&str]) -> Vec<String> {
    let mut binary = binary(tmpdir);
    binary.stdin(std::process::Stdio::inherit());
    binary.arg("--ignore-ids");
    binary.arg("--pipe-input");
    binary.arg("--pipe-output");
    binary.args(args);
    debug!("Command = {:?}", binary);
    crate::imag::stdout_of_command(binary)

M tests/ui/src/imag_view.rs => tests/ui/src/imag_view.rs +2 -1
@@ 28,7 28,8 @@ pub fn call(tempdir: &TempDir, targets: &[&str]) -> Vec<String> {
    // ensure that stdin is not used by the child process
    binary.stdin(std::process::Stdio::inherit());

    binary.arg("--ignore-ids");
    binary.arg("--pipe-input");
    binary.arg("--pipe-output");

    for target in targets.iter() {
        binary.arg(target);