From 91f5a33f5a8a8da58ce400adaa156c2c6b841a2c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 1 Nov 2018 20:30:12 +0100 Subject: [PATCH 1/3] Make "ask" functions get input and output streams This way we can control where the input comes from and the output goes to (like we want to with libimagrt). Signed-off-by: Matthias Beyer --- lib/etc/libimaginteraction/src/ask.rs | 121 +++++++++++++++----------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/lib/etc/libimaginteraction/src/ask.rs b/lib/etc/libimaginteraction/src/ask.rs index 0c6c55cd..86fbea30 100644 --- a/lib/etc/libimaginteraction/src/ask.rs +++ b/lib/etc/libimaginteraction/src/ask.rs @@ -19,10 +19,11 @@ // functions to ask the user for data, with crate:spinner -use std::io::stdin; use std::io::BufRead; use std::io::BufReader; use std::result::Result as RResult; +use std::io::Read; +use std::io::Write; use regex::Regex; use ansi_term::Colour::*; @@ -34,33 +35,33 @@ use failure::err_msg; /// Ask the user for a Yes/No answer. Optionally provide a default value. If none is provided, this /// keeps loop{}ing -pub fn ask_bool(s: &str, default: Option) -> bool { - ask_bool_(s, default, &mut BufReader::new(stdin())) +pub fn ask_bool(s: &str, default: Option, input: &mut Read, output: &mut Write) -> Result { + ask_bool_(s, default, &mut BufReader::new(input), output) } -fn ask_bool_(s: &str, default: Option, input: &mut R) -> bool { +fn ask_bool_(s: &str, default: Option, input: &mut R, output: &mut Write) -> Result { lazy_static! { static ref R_YES: Regex = Regex::new(r"^[Yy](\n?)$").unwrap(); static ref R_NO: Regex = Regex::new(r"^[Nn](\n?)$").unwrap(); } loop { - ask_question(s, false); + ask_question(s, false, output)?; if match default { Some(s) => s, _ => true } { - println!(" [Yn]: "); + writeln!(output, " [Yn]: ")?; } else { - println!(" [yN]: "); + writeln!(output, " [yN]: ")?; } let mut s = String::new(); let _ = input.read_line(&mut s); if R_YES.is_match(&s[..]) { - return true + return Ok(true) } else if R_NO.is_match(&s[..]) { - return false + return Ok(false) } else if default.is_some() { - return default.unwrap(); + return Ok(default.unwrap()) } // else again... } @@ -68,25 +69,25 @@ fn ask_bool_(s: &str, default: Option, input: &mut R) -> bool /// Ask the user for an unsigned number. Optionally provide a default value. If none is provided, /// this keeps loop{}ing -pub fn ask_uint(s: &str, default: Option) -> u64 { - ask_uint_(s, default, &mut BufReader::new(stdin())) +pub fn ask_uint(s: &str, default: Option, input: &mut Read, output: &mut Write) -> Result { + ask_uint_(s, default, &mut BufReader::new(input), output) } -fn ask_uint_(s: &str, default: Option, input: &mut R) -> u64 { +fn ask_uint_(s: &str, default: Option, input: &mut R, output: &mut Write) -> Result { use std::str::FromStr; loop { - ask_question(s, false); + ask_question(s, false, output)?; let mut s = String::new(); let _ = input.read_line(&mut s); let u : RResult = FromStr::from_str(&s[..]); match u { - Ok(u) => { return u; }, + Ok(u) => { return Ok(u); }, Err(_) => { if default.is_some() { - return default.unwrap(); + return Ok(default.unwrap()); } // else keep looping } } @@ -109,8 +110,10 @@ pub fn ask_string(s: &str, permit_empty: bool, permit_multiline: bool, eof: Option<&str>, - prompt: &str) - -> String + prompt: &str, + input: &mut Read, + output: &mut Write) + -> Result { ask_string_(s, default, @@ -118,7 +121,8 @@ pub fn ask_string(s: &str, permit_multiline, eof, prompt, - &mut BufReader::new(stdin())) + &mut BufReader::new(input), + output) } fn ask_string_(s: &str, @@ -127,36 +131,37 @@ fn ask_string_(s: &str, permit_multiline: bool, eof: Option<&str>, prompt: &str, - input: &mut R) - -> String + input: &mut R, + output: &mut Write) + -> Result { let mut v = vec![]; loop { - ask_question(s, true); - print!("{}", prompt); + ask_question(s, true, output)?; + write!(output, "{}", prompt)?; let mut s = String::new(); let _ = input.read_line(&mut s); if permit_multiline { if permit_multiline && eof.map_or(false, |e| e == s) { - return v.join("\n"); + return Ok(v.join("\n")); } if permit_empty || !v.is_empty() { v.push(s); } - print!("{}", prompt); + write!(output, "{}", prompt)?; } else if s.is_empty() && permit_empty { - return s; + return Ok(s); } else if s.is_empty() && !permit_empty { if default.is_some() { - return default.unwrap(); + return Ok(default.unwrap()); } else { continue; } } else { - return s; + return Ok(s); } } } @@ -171,11 +176,11 @@ pub fn ask_select_from_list(list: &[&str]) -> Result { /// trailing questionmark. /// /// The `nl` parameter can be used to configure whether a newline character should be printed -pub fn ask_question(question: &str, nl: bool) { +pub fn ask_question(question: &str, nl: bool, output: &mut Write) -> Result<()> { if nl { - println!("[imag]: {}?", Yellow.paint(question)); + writeln!(output, "[imag]: {}?", Yellow.paint(question)).map_err(Error::from) } else { - print!("[imag]: {}?", Yellow.paint(question)); + writeln!(output, "[imag]: {}?", Yellow.paint(question)).map_err(Error::from) } } @@ -191,8 +196,9 @@ mod test { let question = "Is this true"; let default = None; let answers = "\n\n\n\n\ny"; + let mut sink: Vec = vec![]; - assert!(ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -200,8 +206,9 @@ mod test { let question = "Is this true"; let default = None; let answers = "\n\n\n\n\ny\n"; + let mut sink: Vec = vec![]; - assert!(ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -209,8 +216,9 @@ mod test { let question = "Is this true"; let default = None; let answers = "n"; + let mut sink: Vec = vec![]; - assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -218,8 +226,9 @@ mod test { let question = "Is this true"; let default = None; let answers = "n\n"; + let mut sink: Vec = vec![]; - assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -227,8 +236,9 @@ mod test { let question = "Is this true"; let default = Some(false); let answers = "n"; + let mut sink: Vec = vec![]; - assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -236,8 +246,9 @@ mod test { let question = "Is this true"; let default = Some(false); let answers = "n\n"; + let mut sink: Vec = vec![]; - assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -245,8 +256,9 @@ mod test { let question = "Is this true"; let default = Some(true); let answers = "y"; + let mut sink: Vec = vec![]; - assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -254,8 +266,9 @@ mod test { let question = "Is this true"; let default = Some(true); let answers = "y\n"; + let mut sink: Vec = vec![]; - assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -263,8 +276,9 @@ mod test { let question = "Is this true"; let default = Some(true); let answers = "n"; + let mut sink: Vec = vec![]; - assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -272,8 +286,9 @@ mod test { let question = "Is this true"; let default = Some(false); let answers = "y"; + let mut sink: Vec = vec![]; - assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -281,8 +296,9 @@ mod test { let question = "Is this true"; let default = Some(false); let answers = "\n"; + let mut sink: Vec = vec![]; - assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -290,8 +306,9 @@ mod test { let question = "Is this true"; let default = Some(true); let answers = "\n"; + let mut sink: Vec = vec![]; - assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -299,8 +316,9 @@ mod test { let question = "Is this 1"; let default = None; let answers = "1"; + let mut sink: Vec = vec![]; - assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -308,8 +326,9 @@ mod test { let question = "Is this 1"; let default = Some(1); let answers = "1"; + let mut sink: Vec = vec![]; - assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -317,8 +336,9 @@ mod test { let question = "Is this 1"; let default = Some(2); let answers = "1"; + let mut sink: Vec = vec![]; - assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -326,8 +346,9 @@ mod test { let question = "Is this 1"; let default = Some(2); let answers = "\n"; + let mut sink: Vec = vec![]; - assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -335,8 +356,9 @@ mod test { let question = "Is this 1"; let default = Some(2); let answers = "\n\n\n\n"; + let mut sink: Vec = vec![]; - assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } #[test] @@ -344,8 +366,9 @@ mod test { let question = "Is this 1"; let default = Some(2); let answers = "\n\n\nasfb\nsakjf\naskjf\n-2"; + let mut sink: Vec = vec![]; - assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()))); + assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); } } From 8223f846b48cd31235391d8876b73d1e7ea88c84 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 1 Nov 2018 20:33:24 +0100 Subject: [PATCH 2/3] Use new "ask" function interface with input/output stream params In the imag-contact crate we had to rewrite the ask_continue!{} macro as a function for less headache, but besides that this is a rather straight-forward patch for adapting to the new interface. Signed-off-by: Matthias Beyer --- bin/core/imag-category/src/main.rs | 8 +- bin/core/imag-ref/src/main.rs | 12 ++- bin/domain/imag-contact/src/create.rs | 120 +++++++++++++++++++------- bin/domain/imag-diary/src/delete.rs | 11 ++- bin/domain/imag-habit/src/main.rs | 15 +++- 5 files changed, 130 insertions(+), 36 deletions(-) diff --git a/bin/core/imag-category/src/main.rs b/bin/core/imag-category/src/main.rs index 5b68eeb5..c44d0a26 100644 --- a/bin/core/imag-category/src/main.rs +++ b/bin/core/imag-category/src/main.rs @@ -211,7 +211,13 @@ fn delete_category(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("delete-category").unwrap(); // safed by main() let name = scmd.value_of("delete-category-name").map(String::from).unwrap(); // safed by clap let ques = format!("Do you really want to delete category '{}' and remove links to all categorized enties?", name); - let answer = ask_bool(&ques, Some(false)); + + let mut input = rt.stdin().unwrap_or_else(|| { + error!("No input stream. Cannot ask for permission"); + ::std::process::exit(1) + }); + let mut output = rt.stdout(); + let answer = ask_bool(&ques, Some(false), &mut input, &mut output).map_err_trace_exit_unwrap(1); if answer { info!("Deleting category '{}'", name); diff --git a/bin/core/imag-ref/src/main.rs b/bin/core/imag-ref/src/main.rs index 96dedad9..d273ec90 100644 --- a/bin/core/imag-ref/src/main.rs +++ b/bin/core/imag-ref/src/main.rs @@ -117,9 +117,19 @@ fn remove(rt: &Runtime) { .into_storeid() .map_err_trace_exit_unwrap(1); + let mut input = rt.stdin().unwrap_or_else(|| { + error!("No input stream. Cannot ask for permission"); + exit(1); + }); + + let mut output = rt.stdout(); + match rt.store().get(id.clone()).map_err_trace_exit_unwrap(1) { Some(mut entry) => { - if yes || ask_bool(&format!("Delete ref from entry '{}'", id), None) { + if yes || + ask_bool(&format!("Delete ref from entry '{}'", id), None, &mut input, &mut output) + .map_err_trace_exit_unwrap(1) + { let _ = entry.remove_ref().map_err_trace_exit_unwrap(1); } else { info!("Aborted"); diff --git a/bin/domain/imag-contact/src/create.rs b/bin/domain/imag-contact/src/create.rs index a99ef869..edac2b2e 100644 --- a/bin/domain/imag-contact/src/create.rs +++ b/bin/domain/imag-contact/src/create.rs @@ -34,6 +34,7 @@ use std::collections::BTreeMap; use std::process::exit; +use std::io::Read; use std::io::Write; use std::path::PathBuf; use std::fs::OpenOptions; @@ -72,14 +73,9 @@ mod test { } } -macro_rules! ask_continue { - { yes => $yes:expr; no => $no:expr } => { - if ::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true)) { - $yes - } else { - $no - } - }; +fn ask_continue(inputstream: &mut Read, outputstream: &mut Write) -> bool { + ::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true), inputstream, outputstream) + .map_err_trace_exit_unwrap(1) } pub fn create(rt: &Runtime) { @@ -90,7 +86,7 @@ pub fn create(rt: &Runtime) { if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) { let uuid = if fl.is_file() { error!("File does exist, cannot create/override"); - exit(1); + exit(1) } else if fl.is_dir() { let uuid = Uuid::new_v4().to_hyphenated().to_string(); fl.push(uuid.clone()); @@ -147,6 +143,13 @@ pub fn create(rt: &Runtime) { } }; + let mut input = rt.stdin().unwrap_or_else(|| { + error!("No input stream. Cannot ask for permission"); + exit(1) + }); + + let mut output = rt.stdout(); + loop { ::libimagentryedit::edit::edit_in_tmpfile(&rt, &mut template) .map_warn_err_str("Editing failed.") @@ -158,20 +161,27 @@ pub fn create(rt: &Runtime) { } match ::toml::de::from_str(&template) - .map(|toml| parse_toml_into_vcard(toml, uuid.clone())) + .map(|toml| parse_toml_into_vcard(&mut output, &mut input, toml, uuid.clone())) .map_err(Error::from) { Err(e) => { error!("Error parsing template"); trace_error(&e); - ask_continue! { yes => continue; no => exit(1) }; + + if ask_continue(&mut input, &mut output) { + continue; + } else { + exit(1) + } }, Ok(None) => continue, Ok(Some(vcard)) => { if template == TEMPLATE || template.is_empty() { - if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false)) { - exit(1); + if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false), &mut input, &mut output) + .map_err_trace_exit_unwrap(1) + { + exit(1) } else { continue; } @@ -205,7 +215,7 @@ pub fn create(rt: &Runtime) { info!("Ready"); } -fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { +fn parse_toml_into_vcard(output: &mut Write, input: &mut Read, toml: Value, uuid: String) -> Option { let mut vcard = VcardBuilder::new().with_uid(uuid); { // parse name @@ -257,7 +267,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { Some(p) => p, None => { error!("Key 'nickname.[{}].name' missing", i); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, }; @@ -274,7 +288,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { Some(_) => { error!("Type Error: Expected Array or String at 'nickname'"); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, None => { // nothing @@ -310,7 +328,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { Some(p) => p, None => { error!("Key 'phones.[{}].type' missing", i); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } } }; @@ -318,7 +340,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { Some(p) => p, None => { error!("Key 'phones.[{}].number' missing", i); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } } }; @@ -331,7 +357,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { Some(_) => { error!("Expected Array at 'phones'."); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, None => { // nothing @@ -347,7 +377,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { let adrtype = match read_str_from_toml(element, "type", false) { None => { error!("Key 'adresses.[{}].type' missing", i); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, Some(p) => p, }; @@ -378,7 +412,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { Some(_) => { error!("Type Error: Expected Array at 'addresses'"); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, None => { // nothing @@ -394,7 +432,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { let mailtype = match read_str_from_toml(element, "type", false) { None => { error!("Error: 'email.[{}].type' missing", i); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, Some(p) => p, }; // TODO: Unused, because unsupported by vobject @@ -402,7 +444,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { let mail = match read_str_from_toml(element, "addr", false) { None => { error!("Error: 'email.[{}].addr' missing", i); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, Some(p) => p, }; @@ -416,7 +462,11 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option { Some(_) => { error!("Type Error: Expected Array at 'email'"); - ask_continue! { yes => return None; no => exit(1) }; + if ask_continue(input, output) { + return None + } else { + exit(1) + } }, None => { // nothing @@ -508,6 +558,7 @@ fn read_str_from_toml(toml: &Value, path: &'static str, must_be_there: bool) -> #[cfg(test)] mod test_parsing { use super::parse_toml_into_vcard; + use std::io::empty; // TODO const TEMPLATE : &'static str = include_str!("../static/new-contact-template-test.toml"); @@ -515,7 +566,8 @@ mod test_parsing { #[test] fn test_template_names() { let uid = String::from("uid"); - let vcard = parse_toml_into_vcard(::toml::de::from_str(TEMPLATE).unwrap(), uid); + let mut output = Vec::new(); + let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid); assert!(vcard.is_some(), "Failed to parse test template."); let vcard = vcard.unwrap(); @@ -532,7 +584,8 @@ mod test_parsing { #[test] fn test_template_person() { let uid = String::from("uid"); - let vcard = parse_toml_into_vcard(::toml::de::from_str(TEMPLATE).unwrap(), uid); + let mut output = Vec::new(); + let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid); assert!(vcard.is_some(), "Failed to parse test template."); let vcard = vcard.unwrap(); @@ -550,7 +603,8 @@ mod test_parsing { #[test] fn test_template_organization() { let uid = String::from("uid"); - let vcard = parse_toml_into_vcard(::toml::de::from_str(TEMPLATE).unwrap(), uid); + let mut output = Vec::new(); + let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid); assert!(vcard.is_some(), "Failed to parse test template."); let vcard = vcard.unwrap(); @@ -567,7 +621,8 @@ mod test_parsing { #[test] fn test_template_phone() { let uid = String::from("uid"); - let vcard = parse_toml_into_vcard(::toml::de::from_str(TEMPLATE).unwrap(), uid); + let mut output = Vec::new(); + let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid); assert!(vcard.is_some(), "Failed to parse test template."); let vcard = vcard.unwrap(); @@ -582,7 +637,8 @@ mod test_parsing { #[test] fn test_template_email() { let uid = String::from("uid"); - let vcard = parse_toml_into_vcard(::toml::de::from_str(TEMPLATE).unwrap(), uid); + let mut output = Vec::new(); + let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid); assert!(vcard.is_some(), "Failed to parse test template."); let vcard = vcard.unwrap(); @@ -597,7 +653,8 @@ mod test_parsing { #[test] fn test_template_addresses() { let uid = String::from("uid"); - let vcard = parse_toml_into_vcard(::toml::de::from_str(TEMPLATE).unwrap(), uid); + let mut output = Vec::new(); + let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid); assert!(vcard.is_some(), "Failed to parse test template."); let vcard = vcard.unwrap(); @@ -614,7 +671,8 @@ mod test_parsing { #[test] fn test_template_other() { let uid = String::from("uid"); - let vcard = parse_toml_into_vcard(::toml::de::from_str(TEMPLATE).unwrap(), uid); + let mut output = Vec::new(); + let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid); assert!(vcard.is_some(), "Failed to parse test template."); let vcard = vcard.unwrap(); diff --git a/bin/domain/imag-diary/src/delete.rs b/bin/domain/imag-diary/src/delete.rs index f054cd2a..085b79a9 100644 --- a/bin/domain/imag-diary/src/delete.rs +++ b/bin/domain/imag-diary/src/delete.rs @@ -59,7 +59,16 @@ pub fn delete(rt: &Runtime) { .get_location() .clone(); - if !ask_bool(&format!("Deleting {:?}", to_del_location), Some(true)) { + let mut input = rt.stdin().unwrap_or_else(|| { + error!("No input stream. Cannot ask for permission"); + exit(1); + }); + + let mut output = rt.stdout(); + + if !ask_bool(&format!("Deleting {:?}", to_del_location), Some(true), &mut input, &mut output) + .map_err_trace_exit_unwrap(1) + { info!("Aborting delete action"); return; } diff --git a/bin/domain/imag-habit/src/main.rs b/bin/domain/imag-habit/src/main.rs index 6f81e077..629afa10 100644 --- a/bin/domain/imag-habit/src/main.rs +++ b/bin/domain/imag-habit/src/main.rs @@ -166,6 +166,13 @@ fn delete(rt: &Runtime) { let yes = scmd.is_present("delete-yes"); let delete_instances = scmd.is_present("delete-instances"); + let mut input = rt.stdin().unwrap_or_else(|| { + error!("No input stream. Cannot ask for permission"); + exit(1); + }); + + let mut output = rt.stdout(); + let _ = rt .store() .all_habit_templates() @@ -191,7 +198,9 @@ fn delete(rt: &Runtime) { let do_delete = |id| rt.store().delete(id).map_err_trace_exit_unwrap(1); if !yes { let q = format!("Really delete {}", id); - if ask_bool(&q, Some(false)) { + if ask_bool(&q, Some(false), &mut input, &mut output) + .map_err_trace_exit_unwrap(1) + { let _ = do_delete(id); } } else { @@ -215,7 +224,9 @@ fn delete(rt: &Runtime) { let do_delete_template = |sid| rt.store().delete(sid).map_err_trace_exit_unwrap(1); if !yes { let q = format!("Really delete template {}", sid); - if ask_bool(&q, Some(false)) { + if ask_bool(&q, Some(false), &mut input, &mut output) + .map_err_trace_exit_unwrap(1) + { let _ = do_delete_template(sid); } } else { From afeb4031ac64a257469973cc194009435e54883d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 1 Nov 2018 20:34:53 +0100 Subject: [PATCH 3/3] Use runtime provided error stream Signed-off-by: Matthias Beyer --- bin/core/imag-git/src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/core/imag-git/src/main.rs b/bin/core/imag-git/src/main.rs index f17cf335..a5f370f9 100644 --- a/bin/core/imag-git/src/main.rs +++ b/bin/core/imag-git/src/main.rs @@ -136,7 +136,10 @@ fn main() { Ok(exit_status) => { if !exit_status.success() { debug!("git exited with non-zero exit code: {:?}", exit_status); - eprintln!("git exited with non-zero exit code"); + let mut err = rt.stderr(); + writeln!(err, "git exited with non-zero exit code") + .to_exit_code() + .unwrap_or_exit(); ::std::process::exit(exit_status.code().unwrap_or(1)); } debug!("Successful exit!");