#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
html_root_url = "https://stebalien.github.io/doc/term/term/",
test(attr(deny(warnings)))
)]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
#![allow(clippy::redundant_field_names)]
use std::io::prelude::*;
pub use crate::terminfo::TerminfoTerminal;
#[cfg(windows)]
pub use win::{WinConsole, WinConsoleInfo};
use std::io::{self, Stderr, Stdout};
pub mod terminfo;
#[cfg(windows)]
mod win;
pub type StdoutTerminal = dyn Terminal<Output = Stdout> + Send;
pub type StderrTerminal = dyn Terminal<Output = Stderr> + Send;
#[cfg(not(windows))]
pub fn stdout() -> Option<Box<StdoutTerminal>> {
TerminfoTerminal::new(io::stdout()).map(|t| Box::new(t) as Box<StdoutTerminal>)
}
#[cfg(windows)]
pub fn stdout() -> Option<Box<StdoutTerminal>> {
TerminfoTerminal::new(io::stdout())
.map(|t| Box::new(t) as Box<StdoutTerminal>)
.or_else(|| {
WinConsole::new(io::stdout())
.ok()
.map(|t| Box::new(t) as Box<StdoutTerminal>)
})
}
#[cfg(not(windows))]
pub fn stderr() -> Option<Box<StderrTerminal>> {
TerminfoTerminal::new(io::stderr()).map(|t| Box::new(t) as Box<StderrTerminal>)
}
#[cfg(windows)]
pub fn stderr() -> Option<Box<StderrTerminal>> {
TerminfoTerminal::new(io::stderr())
.map(|t| Box::new(t) as Box<StderrTerminal>)
.or_else(|| {
WinConsole::new(io::stderr())
.ok()
.map(|t| Box::new(t) as Box<StderrTerminal>)
})
}
#[allow(missing_docs)]
pub mod color {
pub type Color = u32;
pub const BLACK: Color = 0;
pub const RED: Color = 1;
pub const GREEN: Color = 2;
pub const YELLOW: Color = 3;
pub const BLUE: Color = 4;
pub const MAGENTA: Color = 5;
pub const CYAN: Color = 6;
pub const WHITE: Color = 7;
pub const BRIGHT_BLACK: Color = 8;
pub const BRIGHT_RED: Color = 9;
pub const BRIGHT_GREEN: Color = 10;
pub const BRIGHT_YELLOW: Color = 11;
pub const BRIGHT_BLUE: Color = 12;
pub const BRIGHT_MAGENTA: Color = 13;
pub const BRIGHT_CYAN: Color = 14;
pub const BRIGHT_WHITE: Color = 15;
}
#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
pub enum Attr {
Bold,
Dim,
Italic(bool),
Underline(bool),
Blink,
Standout(bool),
Reverse,
Secure,
ForegroundColor(color::Color),
BackgroundColor(color::Color),
}
#[derive(Debug)]
pub enum Error {
Io(io::Error),
TerminfoParsing(terminfo::Error),
ParameterizedExpansion(terminfo::parm::Error),
NotSupported,
TermUnset,
TerminfoEntryNotFound,
CursorDestinationInvalid,
ColorOutOfRange,
#[doc(hidden)]
__Nonexhaustive,
}
impl std::cmp::PartialEq for Error {
fn eq(&self, other: &Error) -> bool {
use crate::Error::*;
match *self {
Io(_) => false,
TerminfoParsing(ref inner1) => match *other {
TerminfoParsing(ref inner2) => inner1 == inner2,
_ => false,
},
ParameterizedExpansion(ref inner1) => match *other {
ParameterizedExpansion(ref inner2) => inner1 == inner2,
_ => false,
},
NotSupported => match *other {
NotSupported => true,
_ => false,
},
TermUnset => match *other {
TermUnset => true,
_ => false,
},
TerminfoEntryNotFound => match *other {
TerminfoEntryNotFound => true,
_ => false,
},
CursorDestinationInvalid => match *other {
CursorDestinationInvalid => true,
_ => false,
},
ColorOutOfRange => match *other {
ColorOutOfRange => true,
_ => false,
},
__Nonexhaustive => match *other {
__Nonexhaustive => true,
_ => false,
},
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::Error::*;
match *self {
Io(ref io) => io.fmt(f),
TerminfoParsing(ref e) => e.fmt(f),
ParameterizedExpansion(ref e) => e.fmt(f),
NotSupported => f.write_str("operation not supported by the terminal"),
TermUnset => {
f.write_str("TERM environment variable unset, unable to detect a terminal")
}
TerminfoEntryNotFound => {
f.write_str("could not find a terminfo entry for this terminal")
}
CursorDestinationInvalid => f.write_str("could not move cursor to requested position"),
ColorOutOfRange => f.write_str("color not supported by the terminal"),
__Nonexhaustive => f.write_str("placeholder variant that shouldn't be used"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Error::Io(ref io) => Some(io),
Error::TerminfoParsing(ref e) => Some(e),
Error::ParameterizedExpansion(ref e) => Some(e),
_ => None,
}
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
let kind = match err {
Error::Io(ref e) => e.kind(),
_ => io::ErrorKind::Other,
};
io::Error::new(kind, err)
}
}
impl std::convert::From<io::Error> for Error {
fn from(val: io::Error) -> Self {
Error::Io(val)
}
}
impl std::convert::From<terminfo::Error> for Error {
fn from(val: terminfo::Error) -> Self {
Error::TerminfoParsing(val)
}
}
impl std::convert::From<terminfo::parm::Error> for Error {
fn from(val: terminfo::parm::Error) -> Self {
Error::ParameterizedExpansion(val)
}
}
pub trait Terminal: Write {
type Output: Write;
fn fg(&mut self, color: color::Color) -> Result<()>;
fn bg(&mut self, color: color::Color) -> Result<()>;
fn attr(&mut self, attr: Attr) -> Result<()>;
fn supports_attr(&self, attr: Attr) -> bool;
fn reset(&mut self) -> Result<()>;
fn supports_reset(&self) -> bool;
fn supports_color(&self) -> bool;
fn cursor_up(&mut self) -> Result<()>;
fn delete_line(&mut self) -> Result<()>;
fn carriage_return(&mut self) -> Result<()>;
fn get_ref(&self) -> &Self::Output;
fn get_mut(&mut self) -> &mut Self::Output;
fn into_inner(self) -> Self::Output
where
Self: Sized;
}