use anyhow::{Result, anyhow};
pub trait Console {
fn say(&mut self, line: &str) -> Result<()>;
fn ask(&mut self, question: &str, default: Option<&str>) -> Result<String>;
fn ask_secret(&mut self, question: &str) -> Result<String>;
}
pub struct Terminal<'a, W: std::io::Write> {
out: &'a mut W,
}
impl<'a, W: std::io::Write> Terminal<'a, W> {
pub fn new(out: &'a mut W) -> Self {
Self { out }
}
}
impl<W: std::io::Write> Console for Terminal<'_, W> {
fn say(&mut self, line: &str) -> Result<()> {
writeln!(self.out, "{line}")?;
Ok(())
}
fn ask(&mut self, question: &str, default: Option<&str>) -> Result<String> {
match default {
Some(value) => write!(self.out, "{question} [{value}]: ")?,
None => write!(self.out, "{question}: ")?,
}
self.out.flush()?;
let mut line = String::new();
let read = std::io::stdin().read_line(&mut line)?;
if read == 0 {
return Err(anyhow!("input ended while `drep init` was still asking"));
}
let answer = line.trim();
Ok(match (answer.is_empty(), default) {
(true, Some(value)) => value.to_string(),
_ => answer.to_string(),
})
}
fn ask_secret(&mut self, question: &str) -> Result<String> {
use std::io::IsTerminal;
write!(self.out, "{question}: ")?;
self.out.flush()?;
let secret = if std::io::stdin().is_terminal() {
rpassword::read_password()?
} else {
let mut line = String::new();
std::io::stdin().read_line(&mut line)?;
line.trim_end_matches(['\n', '\r']).to_string()
};
writeln!(self.out)?;
Ok(secret)
}
}