Merge pull request #311 from matthiasbeyer/libimaginteraction/init

libimaginteraction/init
This commit is contained in:
Matthias Beyer 2016-04-12 21:57:58 +02:00
commit 9866c6354d
6 changed files with 422 additions and 0 deletions

View File

@ -0,0 +1,22 @@
[package]
name = "libimaginteraction"
version = "0.1.0"
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
[dependencies]
spinner = "0.4"
interactor = "0.1"
log = "0.3.5"
ansi_term = "0.7.2"
regex = "0.1.62"
lazy_static = "0.1.15"
[dependencies.libimagstore]
path = "../libimagstore"
[dependencies.libimagentryfilter]
path = "../libimagentryfilter"
[dependencies.libimagutil]
path = "../libimagutil"

View File

@ -0,0 +1,300 @@
// 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 error::InteractionError;
use error::InteractionErrorKind;
use result::Result;
use regex::Regex;
use ansi_term::Colour::*;
use interactor::*;
/// 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>) -> bool {
ask_bool_(s, default, &mut BufReader::new(stdin()))
}
fn ask_bool_<R: BufRead>(s: &str, default: Option<bool>, input: &mut R) -> bool {
lazy_static! {
static ref R_YES: Regex = Regex::new(r"^[Yy]$").unwrap();
static ref R_NO: Regex = Regex::new(r"^[Nn]$").unwrap();
}
loop {
ask_question(s, false);
if match default { Some(s) => s, _ => true } {
println!(" [Yn]: ");
} else {
println!(" [yN]: ");
}
let mut s = String::new();
let _ = input.read_line(&mut s);
if R_YES.is_match(&s[..]) {
return true
} else if R_NO.is_match(&s[..]) {
return false
} else {
if default.is_some() {
return default.unwrap();
}
// else again...
}
}
}
/// 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>) -> u64 {
ask_uint_(s, default, &mut BufReader::new(stdin()))
}
fn ask_uint_<R: BufRead>(s: &str, default: Option<u64>, input: &mut R) -> u64 {
use std::str::FromStr;
loop {
ask_question(s, false);
let mut s = String::new();
let _ = input.read_line(&mut s);
let u : RResult<u64, _> = FromStr::from_str(&s[..]);
match u {
Ok(u) => { return u; },
Err(_) => {
if default.is_some() {
return default.unwrap();
} // else keep looping
}
}
}
}
/// Ask the user for a String.
///
/// If `permit_empty` is set to false, the default value will be returned if the user inserts an
/// empty string.
///
/// If the `permit_empty` value is true, the `default` value is never returned.
///
/// If the `permit_multiline` is set to true, the `prompt` will be displayed before each input line.
///
/// If the `eof` parameter is `None`, the input ends as soon as there is an empty line input from
/// the user. If the parameter is `Some(text)`, the input ends if the input line is equal to `text`.
pub fn ask_string(s: &str,
default: Option<String>,
permit_empty: bool,
permit_multiline: bool,
eof: Option<&str>,
prompt: &str)
-> String
{
ask_string_(s,
default,
permit_empty,
permit_multiline,
eof,
prompt,
&mut BufReader::new(stdin()))
}
pub fn ask_string_<R: BufRead>(s: &str,
default: Option<String>,
permit_empty: bool,
permit_multiline: bool,
eof: Option<&str>,
prompt: &str,
input: &mut R)
-> String
{
let mut v = vec![];
loop {
ask_question(s, true);
print!("{}", prompt);
let mut s = String::new();
let _ = input.read_line(&mut s);
if permit_multiline {
if permit_multiline && eof.map(|e| e == s).unwrap_or(false) {
return v.join("\n");
}
if permit_empty || v.len() != 0 {
v.push(s);
}
print!("{}", prompt);
} else {
if s.len() == 0 && permit_empty {
return s;
} else if s.len() == 0 && !permit_empty {
if default.is_some() {
return default.unwrap();
} else {
continue;
}
} else {
return s;
}
}
}
}
pub fn ask_select_from_list(list: &[&str]) -> Result<String> {
pick_from_list(default_menu_cmd().as_mut(), list, "Selection: ")
.map_err(|e| InteractionError::new(InteractionErrorKind::Unknown, Some(Box::new(e))))
}
/// Helper function to print a imag question string. The `question` argument may not contain a
/// 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) {
if nl {
println!("[imag]: {}?", Yellow.paint(question));
} else {
print!("[imag]: {}?", Yellow.paint(question));
}
}
#[cfg(test)]
mod test {
use std::io::BufReader;
use super::ask_bool_;
use super::ask_uint_;
#[test]
fn test_ask_bool_nodefault_yes() {
let question = "Is this true";
let default = None;
let answers = "\n\n\n\n\ny";
assert!(ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_bool_nodefault_no() {
let question = "Is this true";
let default = None;
let answers = "n";
assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_bool_default_no() {
let question = "Is this true";
let default = Some(false);
let answers = "n";
assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_bool_default_yes() {
let question = "Is this true";
let default = Some(true);
let answers = "y";
assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_bool_default_yes_answer_no() {
let question = "Is this true";
let default = Some(true);
let answers = "n";
assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_bool_default_no_answer_yes() {
let question = "Is this true";
let default = Some(false);
let answers = "y";
assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_bool_default_no_without_answer() {
let question = "Is this true";
let default = Some(false);
let answers = "\n";
assert!(false == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_bool_default_yes_without_answer() {
let question = "Is this true";
let default = Some(true);
let answers = "\n";
assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_uint_nodefault() {
let question = "Is this 1";
let default = None;
let answers = "1";
assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_uint_default() {
let question = "Is this 1";
let default = Some(1);
let answers = "1";
assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_uint_default_2_input_1() {
let question = "Is this 1";
let default = Some(2);
let answers = "1";
assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_uint_default_2_noinput() {
let question = "Is this 1";
let default = Some(2);
let answers = "\n";
assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_uint_default_2_several_noinput() {
let question = "Is this 1";
let default = Some(2);
let answers = "\n\n\n\n";
assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes())));
}
#[test]
fn test_ask_uint_default_2_wrong_input() {
let question = "Is this 1";
let default = Some(2);
let answers = "\n\n\nasfb\nsakjf\naskjf\n-2";
assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes())));
}
}

View File

@ -0,0 +1,78 @@
use std::error::Error;
use std::fmt::Error as FmtError;
use std::clone::Clone;
use std::fmt::{Display, Formatter};
/**
* Kind of error
*/
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InteractionErrorKind {
Unknown
}
fn interaction_error_type_as_str(e: &InteractionErrorKind) -> &'static str {
match e {
&InteractionErrorKind::Unknown => "Unknown Error",
}
}
impl Display for InteractionErrorKind {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
try!(write!(fmt, "{}", interaction_error_type_as_str(self)));
Ok(())
}
}
#[derive(Debug)]
pub struct InteractionError {
err_type: InteractionErrorKind,
cause: Option<Box<Error>>,
}
impl InteractionError {
/**
* Build a new InteractionError from an InteractionErrorKind, optionally with cause
*/
pub fn new(errtype: InteractionErrorKind, cause: Option<Box<Error>>)
-> InteractionError
{
InteractionError {
err_type: errtype,
cause: cause,
}
}
/**
* Get the error type of this InteractionError
*/
pub fn err_type(&self) -> InteractionErrorKind {
self.err_type.clone()
}
}
impl Display for InteractionError {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
try!(write!(fmt, "[{}]", interaction_error_type_as_str(&self.err_type.clone())));
Ok(())
}
}
impl Error for InteractionError {
fn description(&self) -> &str {
interaction_error_type_as_str(&self.err_type.clone())
}
fn cause(&self) -> Option<&Error> {
self.cause.as_ref().map(|e| &**e)
}
}

View File

@ -0,0 +1 @@
// A filter which uses crate:interactor to filter entries

View File

@ -0,0 +1,16 @@
extern crate spinner;
extern crate interactor;
#[macro_use] extern crate log;
extern crate ansi_term;
#[macro_use] extern crate lazy_static;
extern crate regex;
extern crate libimagentryfilter;
extern crate libimagstore;
#[macro_use] extern crate libimagutil;
pub mod ask;
pub mod error;
pub mod filter;
pub mod result;

View File

@ -0,0 +1,5 @@
use std::result::Result as RResult;
use error::InteractionError;
pub type Result<T> = RResult<T, InteractionError>;