use alloc::{string::String, vec::Vec};
use core::ops::Deref;
use crate::{
diagnostics::{IntoDiagnostic, Report},
ColorChoice,
};
pub trait Emitter: Send + Sync {
fn buffer(&self) -> Buffer;
fn print(&self, buffer: Buffer) -> Result<(), Report>;
}
pub struct DefaultEmitter(DefaultEmitterImpl);
impl DefaultEmitter {
pub fn new(color: ColorChoice) -> Self {
Self(DefaultEmitterImpl::new(color))
}
}
impl Emitter for DefaultEmitter {
#[inline(always)]
fn buffer(&self) -> Buffer {
self.0.buffer()
}
#[inline(always)]
fn print(&self, buffer: Buffer) -> Result<(), Report> {
self.0.print(buffer)
}
}
impl Deref for DefaultEmitter {
type Target = DefaultEmitterImpl;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "std")]
#[doc(hidden)]
pub struct DefaultEmitterImpl {
writer: termcolor::BufferWriter,
}
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub struct DefaultEmitterImpl {
writer: Vec<u8>,
ansi: bool,
}
#[cfg(feature = "std")]
impl DefaultEmitterImpl {
fn new(color: ColorChoice) -> Self {
Self {
writer: termcolor::BufferWriter::stderr(color.into()),
}
}
}
#[cfg(feature = "std")]
impl Emitter for DefaultEmitterImpl {
#[inline(always)]
fn buffer(&self) -> Buffer {
Buffer(self.writer.buffer())
}
#[inline(always)]
fn print(&self, buffer: Buffer) -> Result<(), Report> {
self.writer.print(&buffer.0).into_diagnostic()
}
}
#[cfg(not(feature = "std"))]
impl DefaultEmitterImpl {
fn new(color: ColorChoice) -> Self {
Self {
ansi: color.should_ansi(),
writer: vec![],
}
}
}
#[cfg(not(feature = "std"))]
impl Emitter for DefaultEmitterImpl {
#[inline(always)]
fn buffer(&self) -> Buffer {
if self.ansi {
Buffer::ansi()
} else {
Buffer::no_color()
}
}
#[inline(always)]
fn print(&self, buffer: Buffer) -> Result<(), Report> {
self.0.push(b'\n');
self.0.extend(buffer.into_inner());
Ok(())
}
}
#[derive(Default)]
#[cfg(feature = "std")]
pub struct CaptureEmitter {
buffer: parking_lot::Mutex<Vec<u8>>,
}
#[cfg(feature = "std")]
impl CaptureEmitter {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub fn captured(&self) -> String {
let buf = self.buffer.lock();
String::from_utf8_lossy(buf.as_slice()).into_owned()
}
}
#[cfg(feature = "std")]
impl Emitter for CaptureEmitter {
#[inline]
fn buffer(&self) -> Buffer {
Buffer::no_color()
}
#[inline]
fn print(&self, buffer: Buffer) -> Result<(), Report> {
let mut bytes = buffer.into_inner();
let mut buf = self.buffer.lock();
buf.append(&mut bytes);
Ok(())
}
}
#[derive(Clone, Copy, Default)]
pub struct NullEmitter {
ansi: bool,
}
impl NullEmitter {
#[cfg(feature = "std")]
pub fn new(color: ColorChoice) -> Self {
use std::io::IsTerminal;
let ansi = match color {
ColorChoice::Never => false,
ColorChoice::Always | ColorChoice::AlwaysAnsi => true,
ColorChoice::Auto => std::io::stdout().is_terminal(),
};
Self { ansi }
}
#[cfg(not(feature = "std"))]
pub fn new(color: ColorChoice) -> Self {
let ansi = match color {
ColorChoice::Never => false,
ColorChoice::Always | ColorChoice::AlwaysAnsi => true,
ColorChoice::Auto => false,
};
Self { ansi }
}
}
impl Emitter for NullEmitter {
#[inline(always)]
fn buffer(&self) -> Buffer {
if self.ansi {
Buffer::ansi()
} else {
Buffer::no_color()
}
}
#[inline(always)]
fn print(&self, _buffer: Buffer) -> Result<(), Report> {
Ok(())
}
}
#[doc(hidden)]
#[cfg(not(feature = "std"))]
#[derive(Clone, Debug)]
pub struct Buffer(Vec<u8>);
#[doc(hidden)]
#[cfg(feature = "std")]
#[derive(Clone, Debug)]
pub struct Buffer(termcolor::Buffer);
impl Buffer {
#[cfg(not(feature = "std"))]
pub fn new(_choice: ColorChoice) -> Buffer {
Self::no_color()
}
#[cfg(feature = "std")]
pub fn new(choice: ColorChoice) -> Buffer {
match choice {
ColorChoice::Never => Self::no_color(),
ColorChoice::Auto => {
if choice.should_attempt_color() {
Self::ansi()
} else {
Self::no_color()
}
}
ColorChoice::Always | ColorChoice::AlwaysAnsi => Self::ansi(),
}
}
#[cfg(not(feature = "std"))]
pub fn no_color() -> Buffer {
Self(vec![])
}
#[cfg(feature = "std")]
pub fn no_color() -> Buffer {
Self(termcolor::Buffer::no_color())
}
#[cfg(not(feature = "std"))]
pub fn ansi() -> Buffer {
Buffer(vec![])
}
#[cfg(feature = "std")]
pub fn ansi() -> Buffer {
Self(termcolor::Buffer::ansi())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn clear(&mut self) {
self.0.clear()
}
#[inline(always)]
#[cfg(not(feature = "std"))]
pub fn into_inner(self) -> Vec<u8> {
self.0
}
#[cfg(feature = "std")]
pub fn into_inner(self) -> Vec<u8> {
self.0.into_inner()
}
#[inline(always)]
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
#[inline(always)]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
self.0.as_mut_slice()
}
}
#[cfg(not(feature = "std"))]
impl core::fmt::Write for Buffer {
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Result> {
use core::fmt::Write;
self.0.write_str(s);
}
}
#[cfg(feature = "std")]
impl std::io::Write for Buffer {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
self.0.flush()
}
}