use anyhow::{Context, Error, Result}; use atty::Stream; use serde::Serialize; use std::{convert::TryFrom, fmt::Debug}; use termcolor::{ColorChoice, StandardStream}; use crate::output::{OutputFmt, OutputJson, Print, PrintTable, PrintTableOpts, WriteColor}; pub trait PrinterService { fn print(&mut self, data: T) -> Result<()>; fn print_table( &mut self, data: T, opts: PrintTableOpts, ) -> Result<()>; fn is_json(&self) -> bool; } pub struct StdoutPrinter { pub writter: Box, pub fmt: OutputFmt, } impl PrinterService for StdoutPrinter { fn print(&mut self, data: T) -> Result<()> { match self.fmt { OutputFmt::Plain => data.print(self.writter.as_mut()), OutputFmt::Json => serde_json::to_writer(self.writter.as_mut(), &OutputJson::new(data)) .context("cannot write JSON to writter"), } } fn print_table( &mut self, data: T, opts: PrintTableOpts, ) -> Result<()> { match self.fmt { OutputFmt::Plain => data.print_table(self.writter.as_mut(), opts), OutputFmt::Json => serde_json::to_writer(self.writter.as_mut(), &OutputJson::new(data)) .context("cannot write JSON to writter"), } } fn is_json(&self) -> bool { self.fmt == OutputFmt::Json } } impl From for StdoutPrinter { fn from(fmt: OutputFmt) -> Self { let writter = StandardStream::stdout(if atty::isnt(Stream::Stdin) { // Colors should be deactivated if the terminal is not a tty. ColorChoice::Never } else { // Otherwise let's `termcolor` decide by inspecting the environment. From the [doc]: // - If `NO_COLOR` is set to any value, then colors will be suppressed. // - If `TERM` is set to dumb, then colors will be suppressed. // - In non-Windows environments, if `TERM` is not set, then colors will be suppressed. // // [doc]: https://github.com/BurntSushi/termcolor#automatic-color-selection ColorChoice::Auto }); let writter = Box::new(writter); Self { writter, fmt } } } impl TryFrom> for StdoutPrinter { type Error = Error; fn try_from(fmt: Option<&str>) -> Result { Ok(Self { fmt: OutputFmt::try_from(fmt)?, ..Self::from(OutputFmt::Plain) }) } }