use std::io::{self, BufRead, Write}; use std::collections::HashMap; use crate::io::Streams; use crate::database::Database; use crate::error::Result; use crate::commands::Facts; fn read_line(mut r#in: I) -> io::Result { let mut pre_n = String::new(); r#in.read_line(&mut pre_n)?; Ok(pre_n) } pub fn ask(streams: &mut Streams, question: &str) -> io::Result where D: Database, I: BufRead, O: Write, E: Write, { write!(streams.out, "{} [y/N] ", question)?; streams.out.flush()?; Ok(read_line(&mut streams.r#in)?.to_lowercase().starts_with('y')) } enum Choice { Number(usize), Quit, CtrlD, Whatever, } fn to_choice(s: String) -> Choice { let s = s.trim(); if let Ok(n) = s.parse::() { 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(streams: &mut Streams, facts: &Facts, current_sheet: &str) -> Result> 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")?, } }; }