#[cfg(feature = "detect")]
use std::io::{self, IsTerminal};
#[cfg(all(feature = "detect", unix))]
use terminal_size::{Width, terminal_size_of};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Stream {
Stdout,
Stderr,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Probe {
Redirected,
#[non_exhaustive]
Terminal {
columns: Option<u16>,
},
}
impl Probe {
#[must_use]
pub const fn terminal(columns: Option<u16>) -> Self {
match columns {
Some(0) => Self::Terminal { columns: None },
Some(_) | None => Self::Terminal { columns },
}
}
#[cfg(feature = "detect")]
#[must_use]
pub fn of(stream: Stream) -> Self {
match stream {
Stream::Stdout => {
let handle = io::stdout();
if handle.is_terminal() {
Self::terminal(columns_of(handle))
} else {
Self::Redirected
}
}
Stream::Stderr => {
let handle = io::stderr();
if handle.is_terminal() {
Self::terminal(columns_of(handle))
} else {
Self::Redirected
}
}
}
}
#[must_use]
pub fn is_terminal(self) -> bool {
matches!(self, Self::Terminal { .. })
}
#[must_use]
pub fn columns(self) -> Option<u16> {
match self {
Self::Redirected => None,
Self::Terminal { columns } => columns,
}
}
}
#[cfg(all(feature = "detect", unix))]
fn columns_of(stream: impl std::os::fd::AsFd) -> Option<u16> {
terminal_size_of(stream).map(|(Width(columns), _)| columns)
}
#[cfg(all(feature = "detect", not(unix)))]
fn columns_of<T>(_stream: T) -> Option<u16> {
None
}