use alloc::borrow::Cow;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt::Write as _;
use crate::rustc_span::source_map::SourceMap;
use alloc::sync::Arc;
use crate::rustc_errors::emitter::Emitter;
use crate::rustc_errors::formatting::{diag_message_source, try_format_diag_message};
use crate::rustc_errors::{DiagArgValue, DiagInner, Level};
pub struct PlainEmitter {
sm: Option<Arc<SourceMap>>,
short: bool,
dst: Option<alloc::boxed::Box<dyn core::fmt::Write + Send>>,
}
impl PlainEmitter {
pub fn new() -> PlainEmitter {
PlainEmitter { sm: None, short: false, dst: None }
}
pub fn sm(mut self, sm: Option<Arc<SourceMap>>) -> PlainEmitter {
self.sm = sm;
self
}
pub fn short_message(mut self, short: bool) -> PlainEmitter {
self.short = short;
self
}
pub fn dst(mut self, dst: alloc::boxed::Box<dyn core::fmt::Write + Send>) -> PlainEmitter {
self.dst = Some(dst);
self
}
}
impl Default for PlainEmitter {
fn default() -> PlainEmitter {
PlainEmitter::new()
}
}
fn level_name(level: Level) -> &'static str {
match level {
Level::Bug | Level::DelayedBug => "internal compiler error",
Level::Fatal | Level::Error => "error",
Level::ForceWarning | Level::Warning => "warning",
Level::Note | Level::OnceNote => "note",
Level::Help | Level::OnceHelp => "help",
Level::FailureNote => "note",
Level::Allow => "allow",
Level::Expect => "expect",
}
}
impl Emitter for PlainEmitter {
fn emit_diagnostic(&mut self, diag: DiagInner) {
let mut out = String::new();
let _ = write!(out, "{}", level_name(diag.level()));
if let Some(code) = diag.code {
let _ = write!(out, "[{code}]");
}
let mut unresolved = false;
let message: Vec<String> = diag
.messages
.iter()
.map(|(m, _)| {
let text = match try_format_diag_message(m, &diag.args) {
Some(text) => text,
None => {
unresolved = true;
Cow::Borrowed(diag_message_source(m))
}
};
text.split_whitespace().collect::<Vec<_>>().join(" ")
})
.collect();
let _ = write!(out, ": {}", message.join(" "));
if let Some(sm) = &self.sm
&& let Some(sp) = diag.span.primary_span()
&& !sp.is_dummy()
{
let _ = write!(out, "\n --> {}", sm.span_to_diagnostic_string(sp));
}
if !self.short && unresolved {
for (name, value) in diag.args.iter() {
let rendered = match value {
DiagArgValue::Str(s) => s.to_string(),
DiagArgValue::Number(n) => n.to_string(),
DiagArgValue::StrListSepByAnd(xs) => {
xs.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ")
}
};
let _ = write!(out, "\n {name} = {rendered}");
}
}
out.push('\n');
match self.dst.as_mut() {
Some(w) => {
let _ = w.write_str(&out);
}
None => eko::print::write_fd(2, out.as_bytes()),
}
}
fn source_map(&self) -> Option<&SourceMap> {
self.sm.as_deref()
}
}