Merge branch 'minor' into master

This commit is contained in:
Matthias Beyer 2019-05-25 10:44:13 +02:00
commit b4e25df98a
7 changed files with 27 additions and 301 deletions

View file

@ -14,6 +14,27 @@ the changelog (though updating of dependencies is).
Please note that we do not have a "Breaking changes" section as we are in Please note that we do not have a "Breaking changes" section as we are in
Version 0.y.z and thus we can break the API like we want and need to. Version 0.y.z and thus we can break the API like we want and need to.
## 0.9.3
Bugfix release for fixing:
* Removed an import which was already there and fails with the current beta
compiler
* Fix a negation error in the config aggregation in imag-log
* Dependency specification fail in 0.9.2 where everything did not depend on
0.9.2 but 0.9.1
## 0.9.2
Bugfix release for fixing:
* Fix a function that checks a flag. If the flag is not there, it should be "not
set".
* Fix to not ignore errors when collecting links in libimagentrylink
* Remove buildscripts because imag was not installable from crates.io with
buildscripts.
## 0.9.1 ## 0.9.1
Bugfix release for fixing: Bugfix release for fixing:

View file

@ -22,7 +22,6 @@ maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
chrono = "0.4" chrono = "0.4"
toml-query = "0.8" toml-query = "0.8"
lazy_static = "1.2"
toml = "0.4" toml = "0.4"
failure = "0.1" failure = "0.1"

View file

@ -44,12 +44,10 @@ pub trait EntryDate {
} }
lazy_static! { const DATE_HEADER_LOCATION : &'static str = "datetime.value";
static ref DATE_HEADER_LOCATION : &'static str = "datetime.value"; const DATE_RANGE_START_HEADER_LOCATION : &'static str = "datetime.range.start";
static ref DATE_RANGE_START_HEADER_LOCATION : &'static str = "datetime.range.start"; const DATE_RANGE_END_HEADER_LOCATION : &'static str = "datetime.range.end";
static ref DATE_RANGE_END_HEADER_LOCATION : &'static str = "datetime.range.end"; const DATE_FMT : &'static str = "%Y-%m-%dT%H:%M:%S";
static ref DATE_FMT : &'static str = "%Y-%m-%dT%H:%M:%S";
}
impl EntryDate for Entry { impl EntryDate for Entry {
@ -93,7 +91,7 @@ impl EntryDate for Entry {
self.get_header_mut() self.get_header_mut()
.insert(&DATE_HEADER_LOCATION, Value::String(date)) .insert(&DATE_HEADER_LOCATION, Value::String(date))
.context(format_err!("Failed to insert header '{}' in '{}'", .context(format_err!("Failed to insert header '{}' in '{}'",
*DATE_HEADER_LOCATION, DATE_HEADER_LOCATION,
self.get_location())) self.get_location()))
.map_err(Error::from) .map_err(Error::from)
.map(|opt| opt.map(|stri| { .map(|opt| opt.map(|stri| {

View file

@ -37,7 +37,6 @@
while_true, while_true,
)] )]
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate failure; #[macro_use] extern crate failure;
extern crate chrono; extern crate chrono;
extern crate toml_query; extern crate toml_query;

View file

@ -21,17 +21,14 @@
use std::io::BufRead; use std::io::BufRead;
use std::io::BufReader; use std::io::BufReader;
use std::result::Result as RResult;
use std::io::Read; use std::io::Read;
use std::io::Write; use std::io::Write;
use regex::Regex; use regex::Regex;
use ansi_term::Colour::*; use ansi_term::Colour::*;
use interactor::*;
use failure::Error; use failure::Error;
use failure::ResultExt; use failure::ResultExt;
use failure::Fallible as Result; use failure::Fallible as Result;
use failure::err_msg;
/// Ask the user for a Yes/No answer. Optionally provide a default value. If none is provided, this /// Ask the user for a Yes/No answer. Optionally provide a default value. If none is provided, this
/// keeps loop{}ing /// keeps loop{}ing
@ -67,111 +64,6 @@ fn ask_bool_<R: BufRead>(s: &str, default: Option<bool>, input: &mut R, output:
} }
} }
/// 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>, input: &mut Read, output: &mut Write) -> Result<u64> {
ask_uint_(s, default, &mut BufReader::new(input), output)
}
fn ask_uint_<R: BufRead>(s: &str, default: Option<u64>, input: &mut R, output: &mut Write) -> Result<u64> {
use std::str::FromStr;
loop {
ask_question(s, false, output)?;
let mut s = String::new();
let _ = input.read_line(&mut s);
let u : RResult<u64, _> = FromStr::from_str(&s[..]);
match u {
Ok(u) => { return Ok(u); },
Err(_) => {
if default.is_some() {
return Ok(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,
input: &mut Read,
output: &mut Write)
-> Result<String>
{
ask_string_(s,
default,
permit_empty,
permit_multiline,
eof,
prompt,
&mut BufReader::new(input),
output)
}
fn ask_string_<R: BufRead>(s: &str,
default: Option<String>,
permit_empty: bool,
permit_multiline: bool,
eof: Option<&str>,
prompt: &str,
input: &mut R,
output: &mut Write)
-> Result<String>
{
let mut v = vec![];
loop {
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 Ok(v.join("\n"));
}
if permit_empty || !v.is_empty() {
v.push(s);
}
write!(output, "{}", prompt)?;
} else if s.is_empty() && permit_empty {
return Ok(s);
} else if s.is_empty() && !permit_empty {
if default.is_some() {
return Ok(default.unwrap());
} else {
continue;
}
} else {
return Ok(s);
}
}
}
pub fn ask_select_from_list(list: &[&str]) -> Result<String> {
pick_from_list(default_menu_cmd().as_mut(), list, "Selection: ")
.context(err_msg("Unknown interaction error"))
.map_err(Error::from)
}
/// Helper function to print a imag question string. The `question` argument may not contain a /// Helper function to print a imag question string. The `question` argument may not contain a
/// trailing questionmark. /// trailing questionmark.
/// ///
@ -191,7 +83,6 @@ mod test {
use std::io::BufReader; use std::io::BufReader;
use super::ask_bool_; use super::ask_bool_;
use super::ask_uint_;
#[test] #[test]
fn test_ask_bool_nodefault_yes() { fn test_ask_bool_nodefault_yes() {
@ -313,64 +204,4 @@ mod test {
assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap()); assert!(true == ask_bool_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap());
} }
#[test]
fn test_ask_uint_nodefault() {
let question = "Is this 1";
let default = None;
let answers = "1";
let mut sink: Vec<u8> = vec![];
assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap());
}
#[test]
fn test_ask_uint_default() {
let question = "Is this 1";
let default = Some(1);
let answers = "1";
let mut sink: Vec<u8> = vec![];
assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap());
}
#[test]
fn test_ask_uint_default_2_input_1() {
let question = "Is this 1";
let default = Some(2);
let answers = "1";
let mut sink: Vec<u8> = vec![];
assert!(1 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap());
}
#[test]
fn test_ask_uint_default_2_noinput() {
let question = "Is this 1";
let default = Some(2);
let answers = "\n";
let mut sink: Vec<u8> = vec![];
assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap());
}
#[test]
fn test_ask_uint_default_2_several_noinput() {
let question = "Is this 1";
let default = Some(2);
let answers = "\n\n\n\n";
let mut sink: Vec<u8> = vec![];
assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap());
}
#[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";
let mut sink: Vec<u8> = vec![];
assert!(2 == ask_uint_(question, default, &mut BufReader::new(answers.as_bytes()), &mut sink).unwrap());
}
} }

View file

@ -1,123 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> 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
//
use failure::ResultExt;
use toml::Value;
use rustyline::{Config, Editor};
pub struct Readline {
editor: Editor,
history_file: PathBuf,
prompt: String,
}
impl Readline {
pub fn new(rt: &Runtime) -> Result<Readline> {
let c = rt.config().ok_or(IEK::NoConfigError)?;
let histfile = c.lookup("ui.cli.readline_history_file").ok_or(IEK::ConfigError)?;
let histsize = c.lookup("ui.cli.readline_history_size").ok_or(IEK::ConfigError)?;
let histigndups = c.lookup("ui.cli.readline_history_ignore_dups").ok_or(IEK::ConfigError)?;
let histignspace = c.lookup("ui.cli.readline_history_ignore_space").ok_or(IEK::ConfigError)?;
let prompt = c.lookup("ui.cli.readline_prompt").ok_or(IEK::ConfigError)?;
let histfile = histfile
.as_str()
.map(PathBuf::from)
.ok_or(IE::from_kind(IEK::ConfigTypeError))
.context(IEK::ConfigError)
.context(IEK::ReadlineError)?;
let histsize = histsize
.as_int()
.ok_or(IE::from_kind(IEK::ConfigTypeError))
.context(IEK::ConfigError)
.context(IEK::ReadlineError)?;
let histigndups = histigndups
.as_bool()
.ok_or(IE::from_kind(IEK::ConfigTypeError))
.context(IEK::ConfigError)
.context(IEK::ReadlineError)?;
let histignspace = histignspace
.as_bool()
.ok_or(IE::from_kind(IEK::ConfigTypeError))
.context(IEK::ConfigError)
.context(IEK::ReadlineError)?;
let prompt = prompt
.as_str()
.ok_or(IE::from_kind(IEK::ConfigTypeError))
.context(IEK::ConfigError)
.context(IEK::ReadlineError)?;
let config = Config::builder().
.max_history_size(histsize)
.history_ignore_dups(histigndups)
.history_ignore_space(histignspace)
.build();
let mut editor = Editor::new(config);
if !histfile.exists() {
let _ = File::create(histfile.clone())
.context(IEK::ReadlineHistoryFileCreationError)?;
}
let _ = editor.load_history(&histfile).context(ReadlineError)?;
Ok(Readline {
editor: editor,
history_file: histfile,
prompt: prompt,
})
}
pub fn read_line(&mut self) -> Result<Option<String>> {
use rustyline::ReadlineError;
use libimagutil::warn_result::*;
match self.editor.readline(&self.prompt) {
Ok(line) => {
self.editor.add_history_line(&line);
self.editor
.save_history(&self.history_file)
.map_warn_err_str(|e| format!("Could not save history file {} -> {:?}",
self.history_file.display(), e));
return line;
},
Err(ReadlineError::Interrupted) => {
info!("CTRL-C");
Ok(None)
},
Err(ReadlineError::Eof) => {
info!("CTRL-D");
Ok(None)
},
Err(err) => Err(err).map_err_into(ReadlineError),
}
}
}

View file

@ -47,6 +47,7 @@ CRATES=(
./bin/domain/imag-todo ./bin/domain/imag-todo
./bin/domain/imag-log ./bin/domain/imag-log
./bin/domain/imag-wiki ./bin/domain/imag-wiki
./bin/core/imag-markdown
./bin/core/imag-ref ./bin/core/imag-ref
./bin/core/imag-gps ./bin/core/imag-gps
./bin/core/imag-diagnostics ./bin/core/imag-diagnostics