use crate::{
diagnostic::{Component, Diagnostic},
errors::ComponentError,
DiagnosticStyle,
};
use anyhow::Result;
use rustc_errors::{
styled_buffer::{StyledBuffer, StyledString},
Style,
};
use std::fmt::Debug;
use std::io::{self, Write};
use termcolor::{Ansi, Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream, WriteColor};
pub trait Emitter<T>
where
T: Clone + PartialEq + Eq + Style + Debug,
{
fn format_diagnostic(
&mut self,
diag: &Diagnostic<T>,
) -> Result<StyledBuffer<T>, ComponentError>;
fn emit_diagnostic(&mut self, diag: &Diagnostic<T>) -> Result<()>;
fn supports_color(&self) -> bool {
false
}
}
pub struct EmitterWriter<'a> {
dst: Destination<'a>,
}
impl<'a> EmitterWriter<'a> {
pub fn new_with_writer(dst: Destination<'a>) -> Self {
Self { dst }
}
}
impl<'a> Default for EmitterWriter<'a> {
fn default() -> Self {
Self {
dst: Destination::from_stderr(ColorChoice::Auto),
}
}
}
pub enum Destination<'a> {
Terminal(StandardStream),
Buffered(BufferWriter, Buffer),
UnColoredRaw(&'a mut (dyn Write + Send)),
ColoredRaw(Ansi<&'a mut (dyn Write + Send)>),
}
impl<'a> Destination<'a> {
pub fn from_stderr(choice: ColorChoice) -> Self {
if cfg!(windows) {
Self::Terminal(StandardStream::stderr(choice))
} else {
let buffer_writer = BufferWriter::stderr(choice);
let buffer = buffer_writer.buffer();
Destination::Buffered(buffer_writer, buffer)
}
}
pub fn from_stdout(choice: ColorChoice) -> Self {
if cfg!(windows) {
Self::Terminal(StandardStream::stdout(choice))
} else {
let buffer_writer = BufferWriter::stdout(ColorChoice::Auto);
let buffer = buffer_writer.buffer();
Destination::Buffered(buffer_writer, buffer)
}
}
pub fn supports_color(&self) -> bool {
match *self {
Self::Terminal(ref stream) => stream.supports_color(),
Self::Buffered(ref buffer, _) => buffer.buffer().supports_color(),
Self::UnColoredRaw(_) => false,
Self::ColoredRaw(_) => true,
}
}
pub fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> {
match *self {
Self::Terminal(ref mut t) => t.set_color(color),
Self::Buffered(_, ref mut t) => t.set_color(color),
Self::ColoredRaw(ref mut t) => t.set_color(color),
Self::UnColoredRaw(_) => Ok(()),
}
}
pub fn reset(&mut self) -> io::Result<()> {
match *self {
Self::Terminal(ref mut t) => t.reset(),
Self::Buffered(_, ref mut t) => t.reset(),
Self::ColoredRaw(ref mut t) => t.reset(),
Self::UnColoredRaw(_) => Ok(()),
}
}
}
impl<'a> Write for Destination<'a> {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
match *self {
Self::Terminal(ref mut t) => t.write(bytes),
Self::Buffered(_, ref mut buf) => buf.write(bytes),
Self::UnColoredRaw(ref mut w) => w.write(bytes),
Self::ColoredRaw(ref mut t) => t.write(bytes),
}
}
fn flush(&mut self) -> io::Result<()> {
match *self {
Self::Terminal(ref mut t) => t.flush(),
Self::Buffered(_, ref mut buf) => buf.flush(),
Self::UnColoredRaw(ref mut w) => w.flush(),
Self::ColoredRaw(ref mut w) => w.flush(),
}
}
}
impl<'a> Drop for Destination<'a> {
fn drop(&mut self) {
if let Destination::Buffered(ref mut dst, ref mut buf) = self {
drop(dst.print(buf));
}
}
}
impl<'a, T> Emitter<T> for EmitterWriter<'a>
where
T: Clone + PartialEq + Eq + Style + Debug,
{
fn supports_color(&self) -> bool {
self.dst.supports_color()
}
fn emit_diagnostic(&mut self, diag: &Diagnostic<T>) -> Result<()> {
let buffer = self.format_diagnostic(diag)?;
emit_to_destination(&buffer.render(), &mut self.dst)?;
Ok(())
}
fn format_diagnostic(
&mut self,
diag: &Diagnostic<T>,
) -> Result<StyledBuffer<T>, ComponentError> {
let mut sb = StyledBuffer::<T>::new();
let mut errs = vec![];
diag.format(&mut sb, &mut errs);
if !errs.is_empty() {
return Err(ComponentError::ComponentFormatErrors(errs));
}
Ok(sb)
}
}
fn emit_to_destination<T>(
rendered_buffer: &[Vec<StyledString<T>>],
dst: &mut Destination,
) -> io::Result<()>
where
T: Clone + PartialEq + Eq + Style + Debug,
{
use rustc_errors::lock;
let _buffer_lock = lock::acquire_global_lock("compiler_base_errors");
for (pos, line) in rendered_buffer.iter().enumerate() {
for part in line {
let color_spec = match &part.style {
Some(style) => style.render_style_to_color_spec(),
None => ColorSpec::new(),
};
dst.set_color(&color_spec)?;
write!(dst, "{}", part.text)?;
dst.reset()?;
}
if pos != rendered_buffer.len() - 1 {
writeln!(dst)?;
}
}
dst.flush()?;
Ok(())
}
pub fn emit_diagnostic_to_uncolored_text(diag: &Diagnostic<DiagnosticStyle>) -> Result<String> {
let mut emit_tes = EmitResultText::new();
{
let mut emit_writter =
EmitterWriter::new_with_writer(Destination::UnColoredRaw(&mut emit_tes));
emit_writter.emit_diagnostic(diag)?;
}
Ok(emit_tes.test_res)
}
pub(crate) struct EmitResultText {
test_res: String,
}
impl Write for EmitResultText {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if let Ok(s) = std::str::from_utf8(buf) {
self.test_res.push_str(s)
} else {
self.test_res = String::new();
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
unsafe impl Send for EmitResultText {}
impl EmitResultText {
pub(crate) fn new() -> Self {
Self {
test_res: String::new(),
}
}
}