From c18c0bbbe455119c935510bd6fb65292e97950b1 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 26 Feb 2018 22:43:26 +0100 Subject: [PATCH 1/4] Provide stdin/out/err resources via Runtime object This way we can control whether "out" output goes to stdout or stderr without the user of the functionality knowing. This is useful for later when we use libimagrt to automatically read and write the store from and to stdout/in depending on whether we are talking to a TTY or a pipe. --- lib/core/libimagrt/Cargo.toml | 1 + lib/core/libimagrt/src/lib.rs | 1 + lib/core/libimagrt/src/runtime.rs | 53 +++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/lib/core/libimagrt/Cargo.toml b/lib/core/libimagrt/Cargo.toml index e8a569ba..11e38d5f 100644 --- a/lib/core/libimagrt/Cargo.toml +++ b/lib/core/libimagrt/Cargo.toml @@ -29,6 +29,7 @@ is-match = "0.1" toml-query = "0.6" error-chain = "0.11" handlebars = "0.29.0" +atty = "0.2" libimagstore = { version = "0.7.0", path = "../../../lib/core/libimagstore" } libimagerror = { version = "0.7.0", path = "../../../lib/core/libimagerror" } diff --git a/lib/core/libimagrt/src/lib.rs b/lib/core/libimagrt/src/lib.rs index b6c6e735..8895f39d 100644 --- a/lib/core/libimagrt/src/lib.rs +++ b/lib/core/libimagrt/src/lib.rs @@ -47,6 +47,7 @@ extern crate clap; extern crate toml; extern crate toml_query; #[macro_use] extern crate is_match; +extern crate atty; extern crate libimagstore; extern crate libimagutil; diff --git a/lib/core/libimagrt/src/runtime.rs b/lib/core/libimagrt/src/runtime.rs index e1f8fa8a..dbbfbb51 100644 --- a/lib/core/libimagrt/src/runtime.rs +++ b/lib/core/libimagrt/src/runtime.rs @@ -21,6 +21,9 @@ use std::path::PathBuf; 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; @@ -47,6 +50,43 @@ pub struct Runtime<'a> { configuration: Option, 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, + stderr : Box, + stdin : Option, +} + +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 + }, + } } impl<'a> Runtime<'a> { @@ -150,6 +190,7 @@ impl<'a> Runtime<'a> { configuration: config, rtp: rtp, store: store, + resources: aquire_resources(), } }) .chain_err(|| RuntimeErrorKind::Instantiate) @@ -438,6 +479,18 @@ impl<'a> Runtime<'a> { .or(env::var("EDITOR").ok()) .map(Command::new) } + + pub fn stdout(&self) -> &Box { + &self.resources.stdout + } + + pub fn stderr(&self) -> &Box { + &self.resources.stderr + } + + pub fn stdin(&self) -> Option<&Stdin> { + self.resources.stdin.as_ref() + } } /// Exported for the `imag` command, you probably do not want to use that. From 50461b839acba753cae899c137ea364649cfd5b9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 26 Feb 2018 22:57:17 +0100 Subject: [PATCH 2/4] Use output stream from Runtime::stdout() --- bin/core/imag-annotate/src/main.rs | 11 +++++------ bin/core/imag-diagnostics/src/main.rs | 2 +- bin/core/imag-gps/src/main.rs | 7 ++----- bin/core/imag-grep/src/main.rs | 21 +++++++++------------ bin/core/imag-ids/src/main.rs | 4 +--- bin/core/imag-init/src/main.rs | 2 +- bin/core/imag-link/src/main.rs | 5 ++--- bin/core/imag-store/src/retrieve.rs | 8 +++----- bin/core/imag-tag/src/main.rs | 8 +++----- bin/domain/imag-bookmark/src/main.rs | 3 +-- bin/domain/imag-contact/src/create.rs | 3 +-- bin/domain/imag-contact/src/main.rs | 3 +-- bin/domain/imag-diary/src/list.rs | 4 +--- bin/domain/imag-diary/src/main.rs | 5 ++--- bin/domain/imag-habit/src/main.rs | 12 ++++-------- bin/domain/imag-log/src/main.rs | 2 +- bin/domain/imag-mail/src/main.rs | 6 +++--- bin/domain/imag-notes/src/main.rs | 4 +--- bin/domain/imag-timetrack/src/day.rs | 7 +++---- bin/domain/imag-timetrack/src/list.rs | 4 +--- bin/domain/imag-timetrack/src/month.rs | 8 +++----- bin/domain/imag-timetrack/src/week.rs | 7 +++---- bin/domain/imag-timetrack/src/year.rs | 2 +- bin/domain/imag-todo/src/main.rs | 4 ++-- 24 files changed, 55 insertions(+), 87 deletions(-) diff --git a/bin/core/imag-annotate/src/main.rs b/bin/core/imag-annotate/src/main.rs index cf758c4f..790ce8fb 100644 --- a/bin/core/imag-annotate/src/main.rs +++ b/bin/core/imag-annotate/src/main.rs @@ -135,7 +135,6 @@ fn remove(rt: &Runtime) { } fn list(rt: &Runtime) { - let mut out = ::std::io::stdout(); let scmd = rt.cli().subcommand_matches("list").unwrap(); // safed by clap let with_text = scmd.is_present("list-with-text"); match scmd.value_of("entry").map(PathBuf::from) { @@ -150,7 +149,7 @@ fn list(rt: &Runtime) { .map_err_trace_exit_unwrap(1) .enumerate() .map(|(i, a)| { - list_annotation(&mut out, i, a.map_err_trace_exit_unwrap(1), with_text) + list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text) }) .collect::>(); } @@ -163,22 +162,22 @@ fn list(rt: &Runtime) { .map_err_trace_exit_unwrap(1) .enumerate() .map(|(i, a)| { - list_annotation(&mut out, i, a.map_err_trace_exit_unwrap(1), with_text) + list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text) }) .collect::>(); } } } -fn list_annotation<'a>(out: &mut Write, i: usize, a: FileLockEntry<'a>, with_text: bool) { +fn list_annotation<'a>(rt: &Runtime, i: usize, a: FileLockEntry<'a>, with_text: bool) { let _ = if with_text { - writeln!(out, + writeln!(rt.stdout(), "--- {i: >5} | {id}\n{text}\n\n", i = i, id = a.get_location(), text = a.get_content()) } else { - writeln!(out, "{: >5} | {}", i, a.get_location()) + writeln!(rt.stdout(), "{: >5} | {}", i, a.get_location()) } .to_exit_code() .unwrap_or_exit(); diff --git a/bin/core/imag-diagnostics/src/main.rs b/bin/core/imag-diagnostics/src/main.rs index 43172192..50dd2be0 100644 --- a/bin/core/imag-diagnostics/src/main.rs +++ b/bin/core/imag-diagnostics/src/main.rs @@ -155,7 +155,7 @@ fn main() { let n = diags.len(); - let mut out = ::std::io::stdout(); + let mut out = rt.stdout(); let _ = writeln!(out, "imag version {}", env!("CARGO_PKG_VERSION")) .to_exit_code() diff --git a/bin/core/imag-gps/src/main.rs b/bin/core/imag-gps/src/main.rs index 56423286..3277547b 100644 --- a/bin/core/imag-gps/src/main.rs +++ b/bin/core/imag-gps/src/main.rs @@ -144,10 +144,8 @@ fn remove(rt: &Runtime) { }) .map_err_trace_exit_unwrap(1); // The parsing of the deleted values failed - let mut out = ::std::io::stdout(); - if scmd.is_present("print-removed") { - let _ = writeln!(out, "{}", removed_value).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "{}", removed_value).to_exit_code().unwrap_or_exit(); } } @@ -174,7 +172,6 @@ fn get(rt: &Runtime) { exit(1) }); - let mut out = ::std::io::stdout(); - let _ = writeln!(out, "{}", value).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "{}", value).to_exit_code().unwrap_or_exit(); } diff --git a/bin/core/imag-grep/src/main.rs b/bin/core/imag-grep/src/main.rs index 64220f90..1f97e4a2 100644 --- a/bin/core/imag-grep/src/main.rs +++ b/bin/core/imag-grep/src/main.rs @@ -44,6 +44,7 @@ use std::io::Write; use regex::Regex; +use libimagrt::runtime::Runtime; use libimagrt::setup::generate_runtime_setup; use libimagstore::store::Entry; use libimagerror::trace::MapErrTrace; @@ -88,15 +89,13 @@ fn main() { .into_get_iter() .filter_map(|res| res.map_err_trace_exit_unwrap(1)) .filter(|entry| pattern.is_match(entry.get_content())) - .map(|entry| show(&entry, &pattern, &opts, &mut count)) + .map(|entry| show(&rt, &entry, &pattern, &opts, &mut count)) .count(); - let mut out = ::std::io::stdout(); - if opts.count { - let _ = writeln!(out, "{}", count).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "{}", count).to_exit_code().unwrap_or_exit(); } else if !opts.files_with_matches { - let _ = writeln!(out, "Processed {} files, {} matches, {} nonmatches", + let _ = writeln!(rt.stdout(), "Processed {} files, {} matches, {} nonmatches", overall_count, count, overall_count - count) @@ -105,24 +104,22 @@ fn main() { } } -fn show(e: &Entry, re: &Regex, opts: &Options, count: &mut usize) { - let mut out = ::std::io::stdout(); - +fn show(rt: &Runtime, e: &Entry, re: &Regex, opts: &Options, count: &mut usize) { if opts.files_with_matches { - let _ = writeln!(out, "{}", e.get_location()).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "{}", e.get_location()).to_exit_code().unwrap_or_exit(); } else if opts.count { *count += 1; } else { - let _ = writeln!(out, "{}:", e.get_location()).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "{}:", e.get_location()).to_exit_code().unwrap_or_exit(); for capture in re.captures_iter(e.get_content()) { for mtch in capture.iter() { if let Some(m) = mtch { - let _ = writeln!(out, " '{}'", m.as_str()).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), " '{}'", m.as_str()).to_exit_code().unwrap_or_exit(); } } } - let _ = writeln!(out, "").to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "").to_exit_code().unwrap_or_exit(); } } diff --git a/bin/core/imag-ids/src/main.rs b/bin/core/imag-ids/src/main.rs index 3b3a4638..98dd779b 100644 --- a/bin/core/imag-ids/src/main.rs +++ b/bin/core/imag-ids/src/main.rs @@ -60,13 +60,11 @@ fn main() { "print all ids", build_ui); - let mut out = ::std::io::stdout(); - rt.store() .entries() .map_err_trace_exit_unwrap(1) .for_each(|id| { - let _ = writeln!(out, "{}", id.to_str().map_err_trace_exit_unwrap(1)) + let _ = writeln!(rt.stdout(), "{}", id.to_str().map_err_trace_exit_unwrap(1)) .to_exit_code() .unwrap_or_exit(); }) diff --git a/bin/core/imag-init/src/main.rs b/bin/core/imag-init/src/main.rs index 742ad20c..356b3bca 100644 --- a/bin/core/imag-init/src/main.rs +++ b/bin/core/imag-init/src/main.rs @@ -68,7 +68,7 @@ imagrc.toml fn main() { let version = make_imag_version!(); - let app = ui::build_ui(&version); + let app = ui::build_ui(&version); let matches = app.get_matches(); let mut out = ::std::io::stdout(); diff --git a/bin/core/imag-link/src/main.rs b/bin/core/imag-link/src/main.rs index 271f64ab..639df976 100644 --- a/bin/core/imag-link/src/main.rs +++ b/bin/core/imag-link/src/main.rs @@ -254,7 +254,6 @@ fn list_linkings(rt: &Runtime) { .unwrap(); // safed by clap let list_externals = cmd.is_present("list-externals-too"); - let mut out = ::std::io::stdout(); for entry in cmd.values_of("entries").unwrap() { // safed by clap match rt.store().get(PathBuf::from(entry)) { @@ -268,7 +267,7 @@ fn list_linkings(rt: &Runtime) { .ok(); if let Some(link) = link { - let _ = writeln!(out, "{: <3}: {}", i, link) + let _ = writeln!(rt.stdout(), "{: <3}: {}", i, link) .to_exit_code() .unwrap_or_exit(); i += 1; @@ -284,7 +283,7 @@ fn list_linkings(rt: &Runtime) { .map_err_trace_exit_unwrap(1) .into_string(); - let _ = writeln!(out, "{: <3}: {}", i, link) + let _ = writeln!(rt.stdout(), "{: <3}: {}", i, link) .to_exit_code() .unwrap_or_exit(); diff --git a/bin/core/imag-store/src/retrieve.rs b/bin/core/imag-store/src/retrieve.rs index 20dc100c..ea895138 100644 --- a/bin/core/imag-store/src/retrieve.rs +++ b/bin/core/imag-store/src/retrieve.rs @@ -51,11 +51,9 @@ pub fn retrieve(rt: &Runtime) { } pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) { - let mut out = ::std::io::stdout(); - if do_print_raw(scmd) { debug!("Printing raw content..."); - let _ = writeln!(out, "{}", e.to_str()) + let _ = writeln!(rt.stdout(), "{}", e.to_str()) .to_exit_code() .unwrap_or_exit(); } else if do_filter(scmd) { @@ -74,7 +72,7 @@ pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) { unimplemented!() } else { debug!("Printing header as TOML..."); - let _ = writeln!(out, "{}", e.get_header()) + let _ = writeln!(rt.stdout(), "{}", e.get_header()) .to_exit_code() .unwrap_or_exit(); } @@ -82,7 +80,7 @@ pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) { if do_print_content(scmd) { debug!("Printing content..."); - let _ = writeln!(out, "{}", e.get_content()) + let _ = writeln!(rt.stdout(), "{}", e.get_content()) .to_exit_code() .unwrap_or_exit(); } diff --git a/bin/core/imag-tag/src/main.rs b/bin/core/imag-tag/src/main.rs index 9fb21188..ecec7afa 100644 --- a/bin/core/imag-tag/src/main.rs +++ b/bin/core/imag-tag/src/main.rs @@ -164,11 +164,9 @@ fn list(id: PathBuf, rt: &Runtime) { unimplemented!() } - let mut out = ::std::io::stdout(); - if line_out { for tag in &tags { - let _ = writeln!(out, "{}", tag) + let _ = writeln!(rt.stdout(), "{}", tag) .to_exit_code() .unwrap_or_exit(); } @@ -176,13 +174,13 @@ fn list(id: PathBuf, rt: &Runtime) { if sepp_out { let sepp = scmd.value_of("sep").unwrap(); // we checked before - let _ = writeln!(out, "{}", tags.join(sepp)) + let _ = writeln!(rt.stdout(), "{}", tags.join(sepp)) .to_exit_code() .unwrap_or_exit(); } if comm_out { - let _ = writeln!(out, "{}", tags.join(", ")) + let _ = writeln!(rt.stdout(), "{}", tags.join(", ")) .to_exit_code() .unwrap_or_exit(); } diff --git a/bin/domain/imag-bookmark/src/main.rs b/bin/domain/imag-bookmark/src/main.rs index 9fbd75e0..48f349c4 100644 --- a/bin/domain/imag-bookmark/src/main.rs +++ b/bin/domain/imag-bookmark/src/main.rs @@ -135,11 +135,10 @@ fn list(rt: &Runtime) { .map_err_trace_exit_unwrap(1); let links = collection.links(rt.store()).map_err_trace_exit_unwrap(1); - let mut out = ::std::io::stdout(); debug!("Listing..."); for (i, link) in links.enumerate() { match link { - Ok(link) => writeln!(out, "{: >3}: {}", i, link).to_exit_code().unwrap_or_exit(), + Ok(link) => writeln!(rt.stdout(), "{: >3}: {}", i, link).to_exit_code().unwrap_or_exit(), Err(e) => trace_error(&e) } }; diff --git a/bin/domain/imag-contact/src/create.rs b/bin/domain/imag-contact/src/create.rs index 9b44b703..03dde897 100644 --- a/bin/domain/imag-contact/src/create.rs +++ b/bin/domain/imag-contact/src/create.rs @@ -20,7 +20,6 @@ use std::collections::BTreeMap; use std::process::exit; use std::io::Write; -use std::io::stdout; use std::path::PathBuf; use std::fs::OpenOptions; @@ -96,7 +95,7 @@ pub fn create(rt: &Runtime) { (Box::new(file), Some(fl)) } else { - (Box::new(stdout()), None) + (Box::new(rt.stdout()), None) } }; diff --git a/bin/domain/imag-contact/src/main.rs b/bin/domain/imag-contact/src/main.rs index 35ad1e0b..ba50d1b8 100644 --- a/bin/domain/imag-contact/src/main.rs +++ b/bin/domain/imag-contact/src/main.rs @@ -109,7 +109,6 @@ fn main() { fn list(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("list").unwrap(); let list_format = get_contact_print_format("contact.list_format", rt, &scmd); - let mut out = ::std::io::stdout(); let _ = rt .store() @@ -144,7 +143,7 @@ fn list(rt: &Runtime) { .map_err(CE::from) .map_err_trace_exit_unwrap(1); - writeln!(out, "{}", s).to_exit_code().unwrap_or_exit() + writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit() }) .collect::>(); } diff --git a/bin/domain/imag-diary/src/list.rs b/bin/domain/imag-diary/src/list.rs index 051fb028..158ef577 100644 --- a/bin/domain/imag-diary/src/list.rs +++ b/bin/domain/imag-diary/src/list.rs @@ -33,13 +33,11 @@ pub fn list(rt: &Runtime) { let diaryname = get_diary_name(rt) .unwrap_or_else(|| warn_exit("No diary selected. Use either the configuration file or the commandline option", 1)); - let mut out = ::std::io::stdout(); - Diary::entries(rt.store(), &diaryname) .map_dbg_str("Ok") .map_err_trace_exit_unwrap(1) .for_each(|id| { - writeln!(out, "{}", id + writeln!(rt.stdout(), "{}", id .without_base() .to_str() .map_err_trace() diff --git a/bin/domain/imag-diary/src/main.rs b/bin/domain/imag-diary/src/main.rs index bbd443d2..dd487936 100644 --- a/bin/domain/imag-diary/src/main.rs +++ b/bin/domain/imag-diary/src/main.rs @@ -79,9 +79,8 @@ fn main() { if rt.is_ok() { rt.unwrap() } else { - let mut out = ::std::io::stdout(); - let _ = writeln!(out, "Could not set up Runtime").to_exit_code().unwrap_or_exit(); - let _ = writeln!(out, "{:?}", rt.err().unwrap()).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "Could not set up Runtime").to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "{:?}", rt.err().unwrap()).to_exit_code().unwrap_or_exit(); exit(1); } }; diff --git a/bin/domain/imag-habit/src/main.rs b/bin/domain/imag-habit/src/main.rs index 8594dfbe..0a7288b6 100644 --- a/bin/domain/imag-habit/src/main.rs +++ b/bin/domain/imag-habit/src/main.rs @@ -332,8 +332,7 @@ fn today(rt: &Runtime, future: bool) { } if !empty { - let mut out = ::std::io::stdout(); - let _ = table.print(&mut out).to_exit_code().unwrap_or_exit(); + let _ = table.print(&mut rt.stdout()).to_exit_code().unwrap_or_exit(); } } } @@ -388,8 +387,7 @@ fn list(rt: &Runtime) { }); if !empty { - let mut out = ::std::io::stdout(); - let _ = table.print(&mut out).to_exit_code().unwrap_or_exit(); + let _ = table.print(&mut rt.stdout()).to_exit_code().unwrap_or_exit(); } } @@ -411,7 +409,6 @@ fn show(rt: &Runtime) { vec![date, comm] } - let mut out = ::std::io::stdout(); let header = ["#", "Date", "Comment"] .iter() .map(|s| Cell::new(s)) @@ -433,7 +430,7 @@ fn show(rt: &Runtime) { let recur = habit.habit_recur_spec().map_err_trace_exit_unwrap(1); let comm = habit.habit_comment().map_err_trace_exit_unwrap(1); - let _ = writeln!(out, + let _ = writeln!(rt.stdout(), "{i} - {name}\nBase : {b},\nRecurrence: {r}\nComment : {c}\n", i = i, name = name, @@ -461,8 +458,7 @@ fn show(rt: &Runtime) { }); if !empty { - let mut out = ::std::io::stdout(); - let _ = table.print(&mut out).to_exit_code().unwrap_or_exit(); + let _ = table.print(&mut rt.stdout()).to_exit_code().unwrap_or_exit(); } }) .collect::>(); diff --git a/bin/domain/imag-log/src/main.rs b/bin/domain/imag-log/src/main.rs index 9c0f199d..7c2804ec 100644 --- a/bin/domain/imag-log/src/main.rs +++ b/bin/domain/imag-log/src/main.rs @@ -137,7 +137,7 @@ fn show(rt: &Runtime) { } let id = e.diary_id().map_err_trace_exit_unwrap(1); - writeln!(::std::io::stdout(), + writeln!(rt.stdout(), "{dname: >10} - {y: >4}-{m:0>2}-{d:0>2}T{H:0>2}:{M:0>2} - {text}", dname = id.diary_name(), y = id.year(), diff --git a/bin/domain/imag-mail/src/main.rs b/bin/domain/imag-mail/src/main.rs index b822aeba..a108d692 100644 --- a/bin/domain/imag-mail/src/main.rs +++ b/bin/domain/imag-mail/src/main.rs @@ -73,7 +73,7 @@ fn list(rt: &Runtime) { use libimagmail::error::ResultExt; // TODO: Implement lister type in libimagmail for this - fn list_mail(m: Mail) { + fn list_mail(rt: &Runtime, m: Mail) { let id = match m.get_message_id() { Ok(Some(f)) => f, Ok(None) => "".to_owned(), @@ -110,7 +110,7 @@ fn list(rt: &Runtime) { }, }; - writeln!(::std::io::stdout(), + writeln!(rt.stdout(), "Mail: {id}\n\tFrom: {from}\n\tTo: {to}\n\t{subj}\n", from = from, id = id, @@ -130,7 +130,7 @@ fn list(rt: &Runtime) { .map(|fle| Mail::from_fle(fle).map_err_trace().ok()) }) .filter_map(|e| e) - .for_each(list_mail); + .for_each(|m| list_mail(&rt, m)); } fn mail_store(rt: &Runtime) { diff --git a/bin/domain/imag-notes/src/main.rs b/bin/domain/imag-notes/src/main.rs index df1870e2..de395c54 100644 --- a/bin/domain/imag-notes/src/main.rs +++ b/bin/domain/imag-notes/src/main.rs @@ -119,8 +119,6 @@ fn edit(rt: &Runtime) { fn list(rt: &Runtime) { use std::cmp::Ordering; - let mut out = ::std::io::stdout(); - let _ = rt .store() .all_notes() @@ -139,7 +137,7 @@ fn list(rt: &Runtime) { .iter() .for_each(|note| { let name = note.get_name().map_err_trace_exit_unwrap(1); - writeln!(out, "{}", name) + writeln!(rt.stdout(), "{}", name) .to_exit_code() .unwrap_or_exit() }); diff --git a/bin/domain/imag-timetrack/src/day.rs b/bin/domain/imag-timetrack/src/day.rs index 4bdbcda6..97451920 100644 --- a/bin/domain/imag-timetrack/src/day.rs +++ b/bin/domain/imag-timetrack/src/day.rs @@ -86,7 +86,6 @@ pub fn day(rt: &Runtime) -> i32 { tags_filter.and(start_time_filter).and(end_time_filter) }; - let mut out = ::std::io::stdout(); rt.store() .get_timetrackings() .map_err_trace_exit_unwrap(1) @@ -111,9 +110,9 @@ pub fn day(rt: &Runtime) -> i32 { .trace_unwrap_exit(1) .map(|(tag, start, end)| { match (start, end) { - (None, _) => writeln!(out, "{} has no start time.", tag), - (Some(s), None) => writeln!(out, "{} | {} - ...", tag, s), - (Some(s), Some(e)) => writeln!(out, "{} | {} - {}", tag, s, e), + (None, _) => writeln!(rt.stdout(), "{} has no start time.", tag), + (Some(s), None) => writeln!(rt.stdout(), "{} | {} - ...", tag, s), + (Some(s), Some(e)) => writeln!(rt.stdout(), "{} | {} - {}", tag, s, e), } .to_exit_code() }) diff --git a/bin/domain/imag-timetrack/src/list.rs b/bin/domain/imag-timetrack/src/list.rs index 2ecf7580..f79493d4 100644 --- a/bin/domain/imag-timetrack/src/list.rs +++ b/bin/domain/imag-timetrack/src/list.rs @@ -117,8 +117,6 @@ pub fn list_impl(rt: &Runtime, let mut table = Table::new(); table.set_titles(Row::new(["Tag", "Start", "End"].into_iter().map(|s| Cell::new(s)).collect())); - let mut stdout = ::std::io::stdout(); - rt.store() .get_timetrackings() .map_err_trace_exit_unwrap(1) @@ -167,7 +165,7 @@ pub fn list_impl(rt: &Runtime, }) }) .map_err_trace_exit_unwrap(1) - .print(&mut stdout) + .print(&mut rt.stdout()) .map_err(|_| TimeTrackError::from(String::from("Failed printing table"))) .map(|_| 0) .map_err_trace() diff --git a/bin/domain/imag-timetrack/src/month.rs b/bin/domain/imag-timetrack/src/month.rs index 90c38619..13414e7a 100644 --- a/bin/domain/imag-timetrack/src/month.rs +++ b/bin/domain/imag-timetrack/src/month.rs @@ -101,8 +101,6 @@ pub fn month(rt: &Runtime) -> i32 { tags_filter.and(start_time_filter).and(end_time_filter) }; - let mut out = ::std::io::stdout(); - rt.store() .get_timetrackings() .map_err_trace_exit_unwrap(1) @@ -127,9 +125,9 @@ pub fn month(rt: &Runtime) -> i32 { .trace_unwrap_exit(1) .map(|(tag, start, end)| { match (start, end) { - (None, _) => writeln!(out, "{} has no start time.", tag), - (Some(s), None) => writeln!(out, "{} | {} - ...", tag, s), - (Some(s), Some(e)) => writeln!(out, "{} | {} - {}", tag, s, e), + (None, _) => writeln!(rt.stdout(), "{} has no start time.", tag), + (Some(s), None) => writeln!(rt.stdout(), "{} | {} - ...", tag, s), + (Some(s), Some(e)) => writeln!(rt.stdout(), "{} | {} - {}", tag, s, e), } .to_exit_code() }) diff --git a/bin/domain/imag-timetrack/src/week.rs b/bin/domain/imag-timetrack/src/week.rs index a73657d8..db1cec90 100644 --- a/bin/domain/imag-timetrack/src/week.rs +++ b/bin/domain/imag-timetrack/src/week.rs @@ -99,7 +99,6 @@ pub fn week(rt: &Runtime) -> i32 { tags_filter.and(start_time_filter).and(end_time_filter) }; - let mut out = ::std::io::stdout(); rt.store() .get_timetrackings() .map_err_trace_exit_unwrap(1) @@ -124,9 +123,9 @@ pub fn week(rt: &Runtime) -> i32 { .trace_unwrap_exit(1) .map(|(tag, start, end)| { match (start, end) { - (None, _) => writeln!(out, "{} has no start time.", tag), - (Some(s), None) => writeln!(out, "{} | {} - ...", tag, s), - (Some(s), Some(e)) => writeln!(out, "{} | {} - {}", tag, s, e), + (None, _) => writeln!(rt.stdout(), "{} has no start time.", tag), + (Some(s), None) => writeln!(rt.stdout(), "{} | {} - ...", tag, s), + (Some(s), Some(e)) => writeln!(rt.stdout(), "{} | {} - {}", tag, s, e), } .to_exit_code() }) diff --git a/bin/domain/imag-timetrack/src/year.rs b/bin/domain/imag-timetrack/src/year.rs index cddd21a1..4db59ec2 100644 --- a/bin/domain/imag-timetrack/src/year.rs +++ b/bin/domain/imag-timetrack/src/year.rs @@ -98,7 +98,7 @@ pub fn year(rt: &Runtime) -> i32 { tags_filter.and(start_time_filter).and(end_time_filter) }; - let mut out = ::std::io::stdout(); + let mut out = rt.stdout(); rt.store() .get_timetrackings() .map_err_trace_exit_unwrap(1) diff --git a/bin/domain/imag-todo/src/main.rs b/bin/domain/imag-todo/src/main.rs index 8fab4e46..7eaf1d80 100644 --- a/bin/domain/imag-todo/src/main.rs +++ b/bin/domain/imag-todo/src/main.rs @@ -71,7 +71,7 @@ fn tw_hook(rt: &Runtime) { .import_task_from_reader(stdin) .map_err_trace_exit_unwrap(1); - let _ = writeln!(::std::io::stdout(), "{}\nTask {} stored in imag", line, uuid) + let _ = writeln!(rt.stdout(), "{}\nTask {} stored in imag", line, uuid) .to_exit_code() .unwrap_or_exit(); @@ -148,7 +148,7 @@ fn list(rt: &Runtime) { }; // and then print that - let _ = writeln!(::std::io::stdout(), "{}", outstring).to_exit_code().unwrap_or_exit(); + let _ = writeln!(rt.stdout(), "{}", outstring).to_exit_code().unwrap_or_exit(); }); res.map_err_trace().ok(); From 19e9dfe33cb85b4fb51de5bcf5bb4bdc590dc5ba Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 1 Mar 2018 18:18:23 +0100 Subject: [PATCH 3/4] 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. --- lib/core/libimagrt/src/io.rs | 66 +++++++++++++++++++++++++++++++ lib/core/libimagrt/src/lib.rs | 1 + lib/core/libimagrt/src/runtime.rs | 65 +++++++++--------------------- 3 files changed, 86 insertions(+), 46 deletions(-) create mode 100644 lib/core/libimagrt/src/io.rs diff --git a/lib/core/libimagrt/src/io.rs b/lib/core/libimagrt/src/io.rs new file mode 100644 index 00000000..5f6bc9d8 --- /dev/null +++ b/lib/core/libimagrt/src/io.rs @@ -0,0 +1,66 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2018 Matthias Beyer 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 { + 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)"), + } + } +} diff --git a/lib/core/libimagrt/src/lib.rs b/lib/core/libimagrt/src/lib.rs index 8895f39d..d56d638c 100644 --- a/lib/core/libimagrt/src/lib.rs +++ b/lib/core/libimagrt/src/lib.rs @@ -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; diff --git a/lib/core/libimagrt/src/runtime.rs b/lib/core/libimagrt/src/runtime.rs index dbbfbb51..e76618c7 100644 --- a/lib/core/libimagrt/src/runtime.rs +++ b/lib/core/libimagrt/src/runtime.rs @@ -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, 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, - stderr : Box, - stdin : Option, -} - -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 { - &self.resources.stdout + pub fn stdout(&self) -> OutputProxy { + if self.stdout_is_tty { + OutputProxy::Out + } else { + OutputProxy::Err + } } - pub fn stderr(&self) -> &Box { - &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 { + if self.stdin_is_tty { + Some(::std::io::stdin()) + } else { + None + } } } From ee6e766c8ddcf2899a2b0051ad470f8a6fb545ad Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 3 Mar 2018 16:00:49 +0100 Subject: [PATCH 4/4] Use generate_runtime_setup() to create Runtime object --- bin/domain/imag-diary/src/main.rs | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/bin/domain/imag-diary/src/main.rs b/bin/domain/imag-diary/src/main.rs index dd487936..ccaf3ab8 100644 --- a/bin/domain/imag-diary/src/main.rs +++ b/bin/domain/imag-diary/src/main.rs @@ -47,12 +47,7 @@ extern crate libimagstore; extern crate libimagtimeui; extern crate libimagutil; -use std::io::Write; -use std::process::exit; - -use libimagerror::exit::ExitUnwrap; -use libimagerror::io::ToExitCode; -use libimagrt::runtime::Runtime; +use libimagrt::setup::generate_runtime_setup; mod create; mod delete; @@ -66,24 +61,14 @@ use create::create; use delete::delete; use edit::edit; use list::list; -use ui::build_ui; use view::view; fn main() { let version = make_imag_version!(); - let name = "imag-diary"; - let about = "Personal Diary/Diaries"; - let ui = build_ui(Runtime::get_default_cli_builder(name, &version, about)); - let rt = { - let rt = Runtime::new(ui); - if rt.is_ok() { - rt.unwrap() - } else { - let _ = writeln!(rt.stdout(), "Could not set up Runtime").to_exit_code().unwrap_or_exit(); - let _ = writeln!(rt.stdout(), "{:?}", rt.err().unwrap()).to_exit_code().unwrap_or_exit(); - exit(1); - } - }; + let rt = generate_runtime_setup("imag-diary", + &version, + "Personal Diary/Diaries", + ui::build_ui); rt.cli() .subcommand_name()