use crate::source_tracking::filename::FileName;
use crate::source_tracking::immutable_string::ImmutableString;
use crate::source_tracking::SourceMap;
use crate::source_tracking::{fragment::Fragment, source::SourceId};
use codespan_reporting::diagnostic::Diagnostic as CRDiagnostic;
use codespan_reporting::diagnostic::Label;
use codespan_reporting::files::{Error as CRError, Files};
use codespan_reporting::term::Config;
use std::sync::Mutex;
use termcolor::{ColorChoice, StandardStream, WriteColor};
#[cfg(doc)]
use crate::source_tracking::source::Source;
pub use codespan_reporting::diagnostic::Severity;
pub use codespan_reporting::diagnostic::LabelStyle;
static STDOUT_COLOR_CHOICE: Mutex<ColorChoice> = Mutex::new(ColorChoice::Auto);
pub fn set_stdout_color(c: ColorChoice) {
*STDOUT_COLOR_CHOICE.lock().unwrap() = c;
}
pub fn get_stdout_color() -> ColorChoice {
*STDOUT_COLOR_CHOICE.lock().unwrap()
}
#[derive(Debug)]
pub struct Diagnostic(pub CRDiagnostic<SourceId>);
impl Diagnostic {
pub fn new(severity: Severity) -> Self {
Diagnostic(CRDiagnostic::new(severity))
}
#[inline]
pub fn bug() -> Self {
Self::new(Severity::Bug)
}
#[inline]
pub fn error() -> Self {
Self::new(Severity::Error)
}
#[inline]
pub fn warning() -> Self {
Self::new(Severity::Warning)
}
#[inline]
pub fn note() -> Self {
Self::new(Severity::Note)
}
#[inline]
pub fn help() -> Self {
Self::new(Severity::Note)
}
pub fn with_code(self, code: impl Into<String>) -> Self {
Diagnostic(self.0.with_code(code))
}
pub fn with_message(self, message: impl Into<String>) -> Self {
Diagnostic(self.0.with_message(message))
}
pub fn with_notes<I: Into<String>>(mut self, notes: impl IntoIterator<Item = I>) -> Self {
self.0.notes.extend(notes.into_iter().map(Into::into));
self
}
pub fn with_highlights(mut self, highlights: impl IntoIterator<Item = Highlight>) -> Self {
self.0.labels.extend(highlights.into_iter().map(Into::into));
self
}
pub fn write(
&self,
map: &SourceMap,
writer: &mut dyn WriteColor,
config: &Config,
) -> Result<(), CRError> {
codespan_reporting::term::emit(writer, config, map, &self.0)
}
pub fn print(&self, map: &SourceMap) -> Result<(), codespan_reporting::files::Error> {
let stream = StandardStream::stdout(get_stdout_color());
let mut lock = stream.lock();
self.write(map, &mut lock, &Config::default())
}
}
#[derive(Clone, Debug)]
pub struct Highlight {
pub style: LabelStyle,
pub fragment: Fragment,
pub message: String,
}
impl Highlight {
pub fn primary(fragment: Fragment, message: impl Into<String>) -> Self {
Highlight {
style: LabelStyle::Primary,
fragment,
message: message.into(),
}
}
pub fn secondary(fragment: Fragment, message: impl Into<String>) -> Self {
Highlight {
style: LabelStyle::Secondary,
fragment,
message: message.into(),
}
}
pub fn with_message(mut self, message: impl Into<String>) -> Self {
self.message = message.into();
self
}
}
impl From<Highlight> for Label<SourceId> {
fn from(value: Highlight) -> Self {
Label {
style: value.style,
file_id: value.fragment.source.id,
range: value.fragment.range,
message: value.message,
}
}
}
impl<'f> Files<'f> for SourceMap {
type FileId = SourceId;
type Name = FileName;
type Source = ImmutableString;
fn name(&'f self, id: Self::FileId) -> Result<Self::Name, codespan_reporting::files::Error> {
self.get(id)
.map(|source| source.name().clone())
.ok_or(CRError::FileMissing)
}
fn source(
&'f self,
id: Self::FileId,
) -> Result<Self::Source, codespan_reporting::files::Error> {
self.get(id)
.map(|source| source.source().clone())
.ok_or(CRError::FileMissing)
}
fn line_index(
&'f self,
id: Self::FileId,
byte_index: usize,
) -> Result<usize, codespan_reporting::files::Error> {
Ok(self
.get(id)
.ok_or(CRError::FileMissing)?
.line_index(byte_index))
}
fn line_range(
&'f self,
id: Self::FileId,
line_index: usize,
) -> Result<std::ops::Range<usize>, codespan_reporting::files::Error> {
Ok(self
.get(id)
.ok_or(CRError::FileMissing)?
.get_line(line_index)
.range)
}
}