use std::fmt;
use std::io;
use std::panic;
use std::panic::Location;
use std::process;
use std::sync::Arc;
use crate::file::Context;
#[cfg(doc)]
use crate::file::SpanId;
use crate::spec::Spec;
mod builtin;
mod diagnostic;
mod render;
pub use builtin::Builtins;
pub use builtin::Expected;
pub use diagnostic::Diagnostic;
use diagnostic::Kind;
pub struct Report {
ctx: Context,
state: Arc<render::State>,
}
pub struct Options {
pub color: bool,
pub show_report_locations: bool,
}
impl Default for Options {
fn default() -> Self {
Self {
color: true,
show_report_locations: cfg!(debug_assertions),
}
}
}
impl Report {
pub(crate) fn copy(&self) -> Report {
Self {
ctx: self.ctx.copy(),
state: self.state.clone(),
}
}
pub fn builtins<'a>(&'a self, spec: &'a Spec) -> Builtins {
Builtins { report: self, spec }
}
#[track_caller]
pub fn error(&self, message: impl fmt::Display) -> Diagnostic {
self.new_diagnostic(Kind::Error, message.to_string())
}
#[track_caller]
pub fn warn(&self, message: impl fmt::Display) -> Diagnostic {
self.new_diagnostic(Kind::Warning, message.to_string())
}
#[track_caller]
pub fn note(&self, message: impl fmt::Display) -> Diagnostic {
self.new_diagnostic(Kind::Note, message.to_string())
}
#[track_caller]
fn new_diagnostic(&self, kind: Kind, message: String) -> Diagnostic {
Diagnostic::new(self.copy(), kind, message).reported_at(Location::caller())
}
pub fn fatal<T>(&self) -> Result<T, Fatal> {
Err(Fatal(self.copy()))
}
pub fn fatal_or<T>(&self, ok: T) -> Result<T, Fatal> {
if !self.state.has_error() {
return Ok(ok);
}
self.fatal()
}
pub fn collate(&self) {
self.state.collate()
}
pub fn write_out(&self, sink: impl io::Write) -> io::Result<()> {
render::finish(self, sink)
}
pub(crate) fn write_out_for_test(&self) -> String {
eprintln!("{}", self.fatal::<()>().unwrap_err());
let mut sink = String::new();
render::render_fmt(
self,
&Options {
color: false,
show_report_locations: false,
},
&mut sink,
)
.unwrap();
sink
}
pub(crate) fn new(ctx: &Context, opts: Options) -> Self {
Self {
ctx: ctx.copy(),
state: Arc::new(render::State::new(opts)),
}
}
}
pub struct Fatal(Report);
impl Fatal {
pub fn terminate(self) -> ! {
eprintln!("{self}");
process::exit(1);
}
pub fn panic(self) -> ! {
panic::panic_any(self.to_string())
}
}
impl fmt::Debug for Fatal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
render::render_fmt(&self.0, &self.0.state.opts, f)
}
}
impl fmt::Display for Fatal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}