use std::error::Error as StdError;
use std::fmt;
use std::io::{self, Write};
use ctl_text::{Context, Renderable};
#[cfg(feature = "detect")]
use crate::color::ColorMode;
use crate::encode::Encoder;
use crate::stream::Stream;
#[cfg(feature = "detect")]
use crate::terminal::DetectOptions;
use crate::terminal::Terminal;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
BrokenPipe(io::Error),
Io(io::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BrokenPipe(_) => f.write_str("the reader closed the output stream"),
Self::Io(_) => f.write_str("could not write the output"),
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::BrokenPipe(source) | Self::Io(source) => Some(source),
}
}
}
impl From<io::Error> for Error {
fn from(error: io::Error) -> Self {
if error.kind() == io::ErrorKind::BrokenPipe {
Self::BrokenPipe(error)
} else {
Self::Io(error)
}
}
}
impl From<Error> for io::Error {
fn from(error: Error) -> Self {
match error {
Error::BrokenPipe(error) if error.kind() == io::ErrorKind::BrokenPipe => error,
Error::BrokenPipe(_) => io::ErrorKind::BrokenPipe.into(),
Error::Io(error) => error,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Console {
stream: Stream,
terminal: Terminal,
}
impl Console {
#[must_use]
pub fn new(stream: Stream, terminal: Terminal) -> Self {
Self { stream, terminal }
}
#[cfg(feature = "detect")]
#[must_use]
pub fn from_process(stream: Stream, mode: ColorMode, options: DetectOptions) -> Self {
Self::new(stream, Terminal::from_process(stream, mode, options))
}
#[must_use]
pub fn stream(self) -> Stream {
self.stream
}
#[must_use]
pub fn terminal(self) -> Terminal {
self.terminal
}
#[must_use]
pub fn render(self, renderable: &dyn Renderable) -> String {
let context = self
.terminal
.width()
.map_or_else(Context::unbounded, Context::new);
let lines = renderable.render(&context);
Encoder::new(self.terminal.color()).encode(&lines)
}
pub fn write_to(
self,
writer: &mut dyn Write,
renderable: &dyn Renderable,
) -> Result<(), Error> {
writer.write_all(self.render(renderable).as_bytes())?;
writer.flush()?;
Ok(())
}
#[cfg(feature = "detect")]
pub fn print(self, renderable: &dyn Renderable) -> Result<(), Error> {
match self.stream {
Stream::Stdout => self.write_to(&mut io::stdout().lock(), renderable),
Stream::Stderr => self.write_to(&mut io::stderr().lock(), renderable),
}
}
}