use std::io::{self, Write};
use anyhow::{Context, Result};
pub fn resolve_codediff_path() -> String {
std::env::current_exe()
.ok()
.and_then(|path| path.to_str().map(str::to_string))
.unwrap_or_else(|| "codediff".to_string())
}
pub fn parse_yes_no(input: &str, default: bool) -> Option<bool> {
match input.trim().to_lowercase().as_str() {
"" => Some(default),
"y" | "yes" => Some(true),
"n" | "no" => Some(false),
_ => None,
}
}
pub fn ask_yes_no(prompt: &str, default: bool) -> Result<bool> {
loop {
let input = read_line(prompt)?;
match parse_yes_no(&input, default) {
Some(answer) => return Ok(answer),
None => println!("'{input}' - please answer 'y' or 'n'."),
}
}
}
pub fn read_line(prompt: &str) -> Result<String> {
print!("{prompt}");
io::stdout().flush().context("failed to write prompt")?;
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.context("failed to read from stdin")?;
Ok(input.trim().to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolve_codediff_path_never_returns_an_empty_string() {
assert!(!resolve_codediff_path().is_empty());
}
#[test]
fn parse_yes_no_accepts_y_n_and_their_full_spellings_case_insensitively() {
assert_eq!(parse_yes_no("y", false), Some(true));
assert_eq!(parse_yes_no("Y", false), Some(true));
assert_eq!(parse_yes_no("yes", false), Some(true));
assert_eq!(parse_yes_no("n", true), Some(false));
assert_eq!(parse_yes_no("no", true), Some(false));
}
#[test]
fn parse_yes_no_falls_back_to_the_default_on_an_empty_line() {
assert_eq!(parse_yes_no("", true), Some(true));
assert_eq!(parse_yes_no(" ", false), Some(false));
}
#[test]
fn parse_yes_no_rejects_anything_else() {
assert_eq!(parse_yes_no("maybe", true), None);
assert_eq!(parse_yes_no("ye", true), None);
}
}