use std::{
error::Error as StdError,
ffi::CStr,
fmt::{self, Display},
os::unix::io::AsRawFd,
};
use libc::ttyname;
use bstr::{BStr, BString, ByteSlice};
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum Error {
NotTTY,
LibcCall(String, i32),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::NotTTY => write!(f, "Not a TTY"),
Self::LibcCall(fn_name, err_code) => {
write!(f, "Failed calling {} with this error code: {}", fn_name, err_code)
},
}
}
}
impl StdError for Error {
#[inline]
fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
}
#[derive(Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub struct TTYName(BString);
impl TTYName {
pub fn new(file_descriptor: &impl AsRawFd) -> Result<Self, Error> {
let name = unsafe { ttyname(file_descriptor.as_raw_fd()) };
let name = if name.is_null() {
return Err(Error::NotTTY);
} else {
let name_cstr = unsafe { CStr::from_ptr(name) };
BString::from(name_cstr.to_bytes())
};
Ok(TTYName(name))
}
pub fn as_bstr(&self) -> &BStr { self.0.as_bstr() }
pub fn to_bstring(&self) -> BString { self.0.clone() }
}
impl Display for TTYName {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
}
pub trait IsTTY: AsRawFd {
fn is_tty(&self) -> bool;
}
impl<T: AsRawFd> IsTTY for T {
fn is_tty(&self) -> bool { is_tty(self) }
}
#[inline]
pub fn is_tty(file_descriptor: &impl AsRawFd) -> bool {
unsafe { libc::isatty(file_descriptor.as_raw_fd()) == 1 }
}