use crate::{
diagnostic::diagnostic_message::TemplateLoader, emit_diagnostic_to_uncolored_text, Diagnostic,
DiagnosticStyle, Emitter, EmitterWriter,
};
use anyhow::{bail, Context, Result};
use compiler_base_span::fatal_error::FatalError;
use fluent::FluentArgs;
use std::{
fmt::Debug,
path::PathBuf,
sync::{Arc, Mutex},
};
const DEFAULT_TEMPLATE_RESOURCE: &str = "src/diagnostic/locales/en-US/";
const DIAGNOSTIC_MESSAGES_ROOT: &str = env!("CARGO_MANIFEST_DIR");
pub struct DiagnosticHandler {
handler_inner: Mutex<DiagnosticHandlerInner>,
}
impl Debug for DiagnosticHandler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.handler_inner.lock() {
Ok(inner) => {
write!(f, "{:?}", inner)
}
Err(_) => {
write!(f, "")
}
}
}
}
impl DiagnosticHandler {
pub fn default() -> Self {
Self {
handler_inner: Mutex::new(DiagnosticHandlerInner::default()),
}
}
pub fn new_with_default_template_dir() -> Result<Self> {
let mut cargo_file_path = PathBuf::from(DIAGNOSTIC_MESSAGES_ROOT);
cargo_file_path.push(DEFAULT_TEMPLATE_RESOURCE);
let abs_path = cargo_file_path.to_str().with_context(|| {
format!("No such file or directory '{}'", DEFAULT_TEMPLATE_RESOURCE)
})?;
DiagnosticHandler::new_with_template_dir(abs_path).with_context(|| {
format!(
"Failed to init `TemplateLoader` from '{}'",
DEFAULT_TEMPLATE_RESOURCE
)
})
}
pub fn new_with_template_dir(template_dir: &str) -> Result<Self> {
let handler_inner = DiagnosticHandlerInner::new_with_template_dir(template_dir)
.with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?;
Ok(Self {
handler_inner: Mutex::new(handler_inner),
})
}
pub fn add_err_diagnostic(&self, diag: Diagnostic<DiagnosticStyle>) -> Result<&Self> {
match self.handler_inner.lock() {
Ok(mut inner) => {
inner.add_err_diagnostic(diag);
Ok(self)
}
Err(_) => bail!("Add Error Diagnostic Failed."),
}
}
pub fn add_warn_diagnostic(&self, diag: Diagnostic<DiagnosticStyle>) -> Result<&Self> {
match self.handler_inner.lock() {
Ok(mut inner) => {
inner.add_warn_diagnostic(diag);
Ok(self)
}
Err(_) => bail!("Add Warn Diagnostic Failed."),
}
}
pub fn diagnostics_count(&self) -> Result<usize> {
match self.handler_inner.lock() {
Ok(inner) => Ok(inner.diagnostics_count()),
Err(_) => bail!("Diagnostics Counts Failed."),
}
}
pub fn emit_all_diags_into_string(&self) -> Result<Vec<Result<String>>> {
match self.handler_inner.lock() {
Ok(inner) => Ok(inner.emit_all_diags_into_string()),
Err(_) => bail!("Emit Diagnostics Failed."),
}
}
pub fn emit_nth_diag_into_string(&self, index: usize) -> Result<Option<Result<String>>> {
match self.handler_inner.lock() {
Ok(inner) => Ok(inner.emit_nth_diag_into_string(index)),
Err(_) => bail!("Emit Diagnostics Failed."),
}
}
pub fn emit_error_diagnostic(&self, diag: Diagnostic<DiagnosticStyle>) -> Result<&Self> {
match self.handler_inner.lock() {
Ok(mut inner) => {
inner
.emit_error_diagnostic(diag)
.with_context(|| ("Emit Error Diagnostics Failed."))?;
Ok(self)
}
Err(_) => bail!("Emit Error Diagnostics Failed."),
}
}
pub fn emit_warn_diagnostic(&self, diag: Diagnostic<DiagnosticStyle>) -> Result<&Self> {
match self.handler_inner.lock() {
Ok(mut inner) => {
inner
.emit_warn_diagnostic(diag)
.with_context(|| ("Emit Warn Diagnostics Failed."))?;
Ok(self)
}
Err(_) => bail!("Emit Warn Diagnostics Failed."),
}
}
pub fn emit_stashed_diagnostics(&self) -> Result<&Self> {
match self.handler_inner.lock() {
Ok(mut inner) => {
inner
.emit_stashed_diagnostics()
.with_context(|| ("Emit Stashed Diagnostics Failed."))?;
Ok(self)
}
Err(_) => bail!("Emit Stashed Diagnostics Failed."),
}
}
pub fn has_errors(&self) -> Result<bool> {
match self.handler_inner.lock() {
Ok(inner) => Ok(inner.has_errors()),
Err(_) => bail!("Check Has Errors Failed."),
}
}
pub fn has_warns(&self) -> Result<bool> {
match self.handler_inner.lock() {
Ok(inner) => Ok(inner.has_warns()),
Err(_) => bail!("Check Has Warns Failed."),
}
}
pub fn abort_if_errors(&self) -> Result<&Self> {
match self.handler_inner.lock() {
Ok(mut inner) => {
inner
.abort_if_errors()
.with_context(|| ("Abort If Errors Failed."))?;
Ok(self)
}
Err(_) => bail!("Abort If Errors Failed."),
}
}
pub fn get_diagnostic_msg(
&self,
index: &str,
sub_index: Option<&str>,
args: &MessageArgs,
) -> Result<String> {
match self.handler_inner.lock() {
Ok(inner) => inner.get_diagnostic_msg(index, sub_index, args),
Err(_) => bail!("Find Diagnostic Message Failed."),
}
}
}
#[derive(Default)]
pub struct MessageArgs<'a>(pub(crate) FluentArgs<'a>);
impl<'a> MessageArgs<'a> {
pub fn new() -> Self {
Self(FluentArgs::new())
}
pub fn set(&mut self, k: &'a str, v: &'a str) {
self.0.set(k, v);
}
}
pub(crate) struct DiagnosticHandlerInner {
emitter: Box<dyn Emitter<DiagnosticStyle>>,
diagnostics: Vec<Diagnostic<DiagnosticStyle>>,
err_count: usize,
warn_count: usize,
template_loader: Arc<TemplateLoader>,
}
impl Debug for DiagnosticHandlerInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut diag_fmt = String::new();
for diag in &self.diagnostics {
diag_fmt.push_str(&format!("{:?}", diag));
}
write!(f, "{}", diag_fmt)
}
}
impl DiagnosticHandlerInner {
pub(crate) fn default() -> Self {
Self {
err_count: 0,
warn_count: 0,
emitter: Box::new(EmitterWriter::default()),
diagnostics: vec![],
template_loader: Arc::new(TemplateLoader::default()),
}
}
pub(crate) fn new_with_template_dir(template_dir: &str) -> Result<Self> {
let template_loader = TemplateLoader::new_with_template_dir(template_dir)
.with_context(|| format!("Failed to init `TemplateLoader` from '{}'", template_dir))?;
Ok(Self {
err_count: 0,
warn_count: 0,
emitter: Box::new(EmitterWriter::default()),
diagnostics: vec![],
template_loader: Arc::new(template_loader),
})
}
pub(crate) fn add_err_diagnostic(&mut self, diag: Diagnostic<DiagnosticStyle>) {
self.diagnostics.push(diag);
self.err_count += 1;
}
pub(crate) fn add_warn_diagnostic(&mut self, diag: Diagnostic<DiagnosticStyle>) {
self.diagnostics.push(diag);
self.warn_count += 1;
}
#[inline]
pub(crate) fn diagnostics_count(&self) -> usize {
self.diagnostics.len()
}
pub(crate) fn emit_all_diags_into_string(&self) -> Vec<Result<String>> {
self.diagnostics
.iter()
.map(|d| Ok(emit_diagnostic_to_uncolored_text(d)?))
.collect()
}
pub(crate) fn emit_nth_diag_into_string(&self, index: usize) -> Option<Result<String>> {
self.diagnostics
.get(index)
.map(|d| emit_diagnostic_to_uncolored_text(d))
}
pub(crate) fn emit_error_diagnostic(
&mut self,
diag: Diagnostic<DiagnosticStyle>,
) -> Result<()> {
self.emitter.emit_diagnostic(&diag)?;
self.err_count += 1;
Ok(())
}
pub(crate) fn emit_warn_diagnostic(&mut self, diag: Diagnostic<DiagnosticStyle>) -> Result<()> {
self.emitter.emit_diagnostic(&diag)?;
self.warn_count += 1;
Ok(())
}
pub(crate) fn emit_stashed_diagnostics(&mut self) -> Result<()> {
for diag in &self.diagnostics {
self.emitter.emit_diagnostic(diag)?
}
Ok(())
}
#[inline]
pub(crate) fn has_errors(&self) -> bool {
self.err_count > 0
}
#[inline]
pub(crate) fn has_warns(&self) -> bool {
self.warn_count > 0
}
pub(crate) fn abort_if_errors(&mut self) -> Result<()> {
self.emit_stashed_diagnostics()?;
if self.has_errors() {
FatalError.raise();
}
Ok(())
}
pub(crate) fn get_diagnostic_msg(
&self,
index: &str,
sub_index: Option<&str>,
args: &MessageArgs,
) -> Result<String> {
self.template_loader.get_msg_to_str(index, sub_index, args)
}
}