use std::io;
use std::io::{IsTerminal, Read, Write};
use std::panic::RefUnwindSafe;
use termcolor::{ColorChoice, StandardStream};
use write::Termcolor;
pub mod fmt;
mod markup;
mod utils;
mod write;
pub use self::markup::{Markup, MarkupBuf, MarkupElement, MarkupNode};
pub use biome_markup::markup;
pub use utils::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LogLevel {
Error,
Log,
}
pub trait Console: Send + Sync + RefUnwindSafe {
fn println(&mut self, level: LogLevel, args: Markup);
fn print(&mut self, level: LogLevel, args: Markup);
fn read(&mut self) -> Option<String>;
}
pub trait ConsoleExt: Console {
fn error(&mut self, args: Markup);
fn log(&mut self, args: Markup);
fn append(&mut self, args: Markup);
}
impl<T: Console + ?Sized> ConsoleExt for T {
fn error(&mut self, args: Markup) {
self.println(LogLevel::Error, args);
}
fn log(&mut self, args: Markup) {
self.println(LogLevel::Log, args);
}
fn append(&mut self, args: Markup) {
self.print(LogLevel::Log, args);
}
}
pub struct EnvConsole {
out: StandardStream,
err: StandardStream,
r#in: io::Stdin,
}
#[derive(Debug, Clone)]
pub enum ColorMode {
Enabled,
Disabled,
Auto,
}
impl EnvConsole {
fn compute_color(colors: ColorMode) -> (ColorChoice, ColorChoice) {
match colors {
ColorMode::Enabled => (ColorChoice::Always, ColorChoice::Always),
ColorMode::Disabled => (ColorChoice::Never, ColorChoice::Never),
ColorMode::Auto => {
let stdout = if io::stdout().is_terminal() {
ColorChoice::Auto
} else {
ColorChoice::Never
};
let stderr = if io::stderr().is_terminal() {
ColorChoice::Auto
} else {
ColorChoice::Never
};
(stdout, stderr)
}
}
}
pub fn new(colors: ColorMode) -> Self {
let (out_mode, err_mode) = Self::compute_color(colors);
Self {
out: StandardStream::stdout(out_mode),
err: StandardStream::stderr(err_mode),
r#in: io::stdin(),
}
}
pub fn set_color(&mut self, colors: ColorMode) {
let (out_mode, err_mode) = Self::compute_color(colors);
self.out = StandardStream::stdout(out_mode);
self.err = StandardStream::stderr(err_mode);
}
}
impl Default for EnvConsole {
fn default() -> Self {
Self::new(ColorMode::Auto)
}
}
impl Console for EnvConsole {
fn println(&mut self, level: LogLevel, args: Markup) {
let mut out = match level {
LogLevel::Error => self.err.lock(),
LogLevel::Log => self.out.lock(),
};
fmt::Formatter::new(&mut Termcolor(&mut out))
.write_markup(args)
.unwrap();
writeln!(out).unwrap();
}
fn print(&mut self, level: LogLevel, args: Markup) {
let mut out = match level {
LogLevel::Error => self.err.lock(),
LogLevel::Log => self.out.lock(),
};
fmt::Formatter::new(&mut Termcolor(&mut out))
.write_markup(args)
.unwrap();
write!(out, "").unwrap();
}
fn read(&mut self) -> Option<String> {
if io::stdin().is_terminal() {
return None;
}
let mut handle = self.r#in.lock();
let mut buffer = String::new();
let result = handle.read_to_string(&mut buffer);
if result.is_ok() {
Some(buffer)
} else {
None
}
}
}
#[derive(Default, Debug)]
pub struct BufferConsole {
pub out_buffer: Vec<Message>,
pub in_buffer: Vec<String>,
pub print_json: bool,
}
impl BufferConsole {
pub fn with_json(mut self) -> Self {
self.print_json = true;
self
}
}
#[derive(Debug)]
pub struct Message {
pub level: LogLevel,
pub content: MarkupBuf,
}
impl Console for BufferConsole {
fn println(&mut self, level: LogLevel, args: Markup) {
self.out_buffer.push(Message {
level,
content: args.to_owned(),
});
}
fn print(&mut self, level: LogLevel, args: Markup) {
self.out_buffer.push(Message {
level,
content: args.to_owned(),
});
}
fn read(&mut self) -> Option<String> {
if self.in_buffer.is_empty() {
None
} else {
Some(self.in_buffer[0].clone())
}
}
}