mod ancestry;
use std::io::{self, BufRead as _, IsTerminal as _};
use anyhow::{Result, bail};
#[cfg(debug_assertions)]
const ASSUME_TTY: &str = "MOAT_ASSUME_TTY";
fn assumed() -> bool {
#[cfg(debug_assertions)]
if std::env::var_os(ASSUME_TTY).is_some_and(|v| v == "1") {
static NOTED: std::sync::Once = std::sync::Once::new();
NOTED.call_once(|| eprintln!("moat: {ASSUME_TTY}=1 honoured (debug build only)"));
return true;
}
false
}
pub fn interactive() -> bool {
assumed() || std::io::stdin().is_terminal()
}
pub fn person() -> bool {
interactive() && refuse_agent("moat").is_ok()
}
pub fn refuse_agent(command: &str) -> Result<()> {
if assumed() {
return Ok(());
}
match ancestry::agent_ancestor() {
Ok(None) => Ok(()),
Ok(Some(agent)) => bail!(
"`{command}` must be run by a person, not by an agent: it was started from \
`{agent}`. Run it in a terminal of your own"
),
Err(error) => bail!(
"`{command}` must be run by a person, and the programs that started it cannot \
be checked ({error}). Run it in a terminal of your own"
),
}
}
pub fn read_answer() -> io::Result<String> {
let mut line = String::new();
if assumed() || cfg!(windows) {
io::stdin().lock().read_line(&mut line)?;
return Ok(line);
}
match std::fs::File::open("/dev/tty") {
Ok(tty) => {
io::BufReader::new(tty).read_line(&mut line)?;
}
Err(error) => eprintln!("moat: no controlling terminal to read the answer from: {error}"),
}
Ok(line)
}