2021-08-25 14:30:27 -05:00
|
|
|
use std::io::{self, BufRead, Write};
|
2022-05-07 10:10:38 -05:00
|
|
|
use std::collections::HashMap;
|
2021-08-11 20:24:42 -05:00
|
|
|
|
2021-08-25 14:30:27 -05:00
|
|
|
use crate::io::Streams;
|
|
|
|
use crate::database::Database;
|
2022-05-07 10:10:38 -05:00
|
|
|
use crate::error::Result;
|
|
|
|
use crate::commands::Facts;
|
2021-08-25 14:30:27 -05:00
|
|
|
|
|
|
|
fn read_line<I: BufRead>(mut r#in: I) -> io::Result<String> {
|
2021-08-11 20:24:42 -05:00
|
|
|
let mut pre_n = String::new();
|
2021-08-25 14:30:27 -05:00
|
|
|
r#in.read_line(&mut pre_n)?;
|
2021-08-11 20:24:42 -05:00
|
|
|
Ok(pre_n)
|
|
|
|
}
|
|
|
|
|
2022-05-07 10:10:38 -05:00
|
|
|
pub fn ask<D, I, O, E>(streams: &mut Streams<D, I, O, E>, question: &str) -> io::Result<bool>
|
|
|
|
where
|
|
|
|
D: Database,
|
|
|
|
I: BufRead,
|
|
|
|
O: Write,
|
|
|
|
E: Write,
|
|
|
|
{
|
2021-08-25 14:30:27 -05:00
|
|
|
write!(streams.out, "{} [y/N] ", question)?;
|
|
|
|
streams.out.flush()?;
|
2021-08-11 20:24:42 -05:00
|
|
|
|
2021-08-25 14:30:27 -05:00
|
|
|
Ok(read_line(&mut streams.r#in)?.to_lowercase().starts_with('y'))
|
2021-08-11 20:24:42 -05:00
|
|
|
}
|
2022-05-07 10:10:38 -05:00
|
|
|
|
|
|
|
enum Choice {
|
|
|
|
Number(usize),
|
|
|
|
Quit,
|
|
|
|
CtrlD,
|
|
|
|
Whatever,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_choice(s: String) -> Choice {
|
|
|
|
let s = s.trim();
|
|
|
|
|
|
|
|
if let Ok(n) = s.parse::<usize>() {
|
|
|
|
if n == 0 {
|
|
|
|
Choice::Whatever
|
|
|
|
} else {
|
|
|
|
Choice::Number(n)
|
|
|
|
}
|
|
|
|
} else if s == "" {
|
|
|
|
Choice::CtrlD
|
|
|
|
} else if s.to_lowercase() == "q" {
|
|
|
|
Choice::Quit
|
|
|
|
} else {
|
|
|
|
Choice::Whatever
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Offers the last N entries (configurable) to the user and waits for a choice.
|
|
|
|
pub fn note_from_last_entries<D, I, O, E>(streams: &mut Streams<D, I, O, E>, facts: &Facts, current_sheet: &str) -> Result<Option<String>>
|
|
|
|
where
|
|
|
|
D: Database,
|
|
|
|
I: BufRead,
|
|
|
|
O: Write,
|
|
|
|
E: Write,
|
|
|
|
{
|
|
|
|
let entries = streams.db.entries_by_sheet(current_sheet, None, None)?;
|
|
|
|
let mut uniques = HashMap::new();
|
|
|
|
|
|
|
|
entries
|
|
|
|
.into_iter().rev()
|
|
|
|
.filter_map(|e| e.note.map(|n| (n, e.start)))
|
|
|
|
.map(|(n, s)| if uniques.contains_key(&n) {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
uniques.insert(n, s);
|
|
|
|
true
|
|
|
|
})
|
|
|
|
.filter(|&i| i)
|
|
|
|
.take(facts.config.interactive_entries)
|
|
|
|
.count();
|
|
|
|
|
|
|
|
let mut uniques: Vec<_> = uniques.into_iter().collect();
|
|
|
|
uniques.sort_unstable_by_key(|(_n, s)| s.clone());
|
|
|
|
|
|
|
|
writeln!(streams.out, "Latest entries of sheet '{current_sheet}':\n")?;
|
|
|
|
|
|
|
|
let formatter = timeago::Formatter::new();
|
|
|
|
|
|
|
|
for (i, (note, time)) in uniques.iter().enumerate() {
|
|
|
|
let i = i + 1;
|
|
|
|
let ago = formatter.convert_chrono(*time, facts.now);
|
|
|
|
|
|
|
|
writeln!(streams.out, " {i}) {note} ({ago})")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
writeln!(streams.out, "\nenter number or q to cancel")?;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
write!(streams.out, ">> ")?;
|
|
|
|
streams.out.flush()?;
|
|
|
|
|
|
|
|
let choice = to_choice(read_line(&mut streams.r#in)?);
|
|
|
|
|
|
|
|
match choice {
|
|
|
|
Choice::Number(i) => if let Some((n, _s)) = uniques.get(i - 1) {
|
|
|
|
return Ok(Some(n.clone()));
|
|
|
|
} else {
|
|
|
|
writeln!(streams.out, "Not an option")?;
|
|
|
|
}
|
|
|
|
Choice::Quit => return Ok(None),
|
|
|
|
Choice::CtrlD => {
|
|
|
|
writeln!(streams.out)?;
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
Choice::Whatever => writeln!(streams.out, "Not an option")?,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|