Merge pull request #311 from matthiasbeyer/libimaginteraction/init
libimaginteraction/init
This commit is contained in:
commit
9866c6354d
6 changed files with 422 additions and 0 deletions
22
libimaginteraction/Cargo.toml
Normal file
22
libimaginteraction/Cargo.toml
Normal 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"
|
||||
|
300
libimaginteraction/src/ask.rs
Normal file
300
libimaginteraction/src/ask.rs
Normal 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())));
|
||||
}
|
||||
|
||||
}
|
78
libimaginteraction/src/error.rs
Normal file
78
libimaginteraction/src/error.rs
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
|
1
libimaginteraction/src/filter.rs
Normal file
1
libimaginteraction/src/filter.rs
Normal file
|
@ -0,0 +1 @@
|
|||
// A filter which uses crate:interactor to filter entries
|
16
libimaginteraction/src/lib.rs
Normal file
16
libimaginteraction/src/lib.rs
Normal 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;
|
||||
|
5
libimaginteraction/src/result.rs
Normal file
5
libimaginteraction/src/result.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use std::result::Result as RResult;
|
||||
|
||||
use error::InteractionError;
|
||||
|
||||
pub type Result<T> = RResult<T, InteractionError>;
|
Loading…
Reference in a new issue