use std::{
fs::{File, OpenOptions},
io::{self, IoSlice, Write},
path::PathBuf,
sync::LazyLock,
};
pub const TTY_OVERRIDE: &str = "OMP_TTY";
pub fn override_path() -> Option<PathBuf> {
std::env::var_os(TTY_OVERRIDE)
.filter(|value| !value.is_empty())
.map(PathBuf::from)
}
pub fn overridden() -> bool {
static OVERRIDDEN: LazyLock<bool> = LazyLock::new(|| override_path().is_some());
*OVERRIDDEN
}
#[cfg(unix)]
pub fn open(options: &OpenOptions) -> io::Result<File> {
match override_path() {
Some(path) => options.open(path),
None => options.open("/dev/tty"),
}
}
pub struct TtyOut(Sink);
enum Sink {
Stdout(io::Stdout),
Device(File),
}
impl TtyOut {
pub fn new() -> io::Result<Self> {
match override_path() {
Some(path) => Ok(Self(Sink::Device(OpenOptions::new().write(true).open(path)?))),
None => Ok(Self(Sink::Stdout(io::stdout()))),
}
}
}
impl Write for TtyOut {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match &mut self.0 {
Sink::Stdout(out) => out.write(buf),
Sink::Device(out) => out.write(buf),
}
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
match &mut self.0 {
Sink::Stdout(out) => out.write_vectored(bufs),
Sink::Device(out) => out.write_vectored(bufs),
}
}
fn flush(&mut self) -> io::Result<()> {
match &mut self.0 {
Sink::Stdout(out) => out.flush(),
Sink::Device(out) => out.flush(),
}
}
}