//! Utility functions for user interaction. use std::{ fmt::Display, io::{self, Write}, }; use anyhow::{bail, Result}; use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; /// Presents the user with a choice of items and awaits a selection. pub fn user_choice<'i, L: Display, I>( items: &'i [(L, I)], output: &mut StandardStream, ) -> Result<&'i I> { for (i, (label, _)) in items.iter().enumerate() { output.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?; write!(output, "[{}]", i)?; output.reset()?; writeln!(output, " {}", label)?; } let stdin = io::stdin(); loop { write!(output, "Enter a number: ")?; output.flush()?; let mut buffer = String::new(); stdin.read_line(&mut buffer)?; if let Ok(number) = buffer.trim().parse::() { if number < items.len() { return Ok(&items[number].1); } } } } /// Ask the user whether they want to continue. /// /// Returns `Ok(())` if the program should continue, and an error otherwise. pub fn ask_continue(output: &mut StandardStream) -> Result<()> { let stdin = io::stdin(); loop { output.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?; write!(output, "Continue? [Y/n] ")?; output.reset()?; output.flush()?; let mut buffer = String::new(); stdin.read_line(&mut buffer)?; if buffer == "\n" || buffer == "Y\n" || buffer == "y\n" { return Ok(()); } else if buffer == "N\n" || buffer == "n\n" { bail!("Cancelled by user"); } } }