use miette::SourceOffset;
use miette::{Diagnostic, Report, SourceSpan};
#[derive(Debug, thiserror::Error, Diagnostic)]
pub struct DiagnosticWithLocationError {
#[source]
pub source_error: Box<dyn std::error::Error + Send + Sync + 'static>,
#[label(primary, "Error")]
pub caller_location: SourceSpan,
pub msg: Option<String>,
#[source_code]
pub source_code: Option<String>,
#[help]
pub help: Option<String>,
#[related]
pub related: Option<Report>,
}
impl DiagnosticWithLocationError {
#[allow(dead_code)]
#[track_caller]
pub fn new(
source_error: Box<dyn std::error::Error + Send + Sync + 'static>,
caller_location: Option<SourceSpan>,
msg: Option<String>,
source_code: Option<String>,
help: Option<String>,
) -> Self {
Self {
source_error,
caller_location: caller_location.unwrap_or_else(|| {
miette::SourceSpan::new(miette::SourceOffset::from_current_location().unwrap().1, 0)
}),
msg,
source_code,
help,
related: None,
}
}
#[track_caller]
pub fn from_error(source: Box<dyn std::error::Error + Send + Sync + 'static>) -> Self {
Self {
caller_location: miette::SourceSpan::new(
miette::SourceOffset::from_current_location().unwrap().1,
0,
),
msg: None,
source_error: source,
source_code: None,
help: None,
related: None,
}
}
}
impl ::std::fmt::Display for DiagnosticWithLocationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.msg {
Some(msg) => write!(f, "{}", msg),
None => write!(f, "{}", self.source_error),
}
}
}
pub trait IntoDiagnosticWithLocation<T, E> {
fn into_diagnostic(self) -> Result<T, Report>;
fn into_diagnostic_with_help(self, help: Option<String>) -> Result<T, Report>;
}
impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnosticWithLocation<T, E>
for Result<T, E>
{
#[track_caller]
fn into_diagnostic(self) -> Result<T, Report> {
self.into_diagnostic_with_help(None)
}
fn into_diagnostic_with_help(self, help: Option<String>) -> Result<T, Report> {
let location: &'static std::panic::Location<'static> = ::std::panic::Location::caller();
let res = self.map_err(|e| Box::new(dbg!(e)));
if res.is_ok() {
return Ok(res.unwrap());
}
let error: Box<dyn std::error::Error + Send + Sync + 'static> = res.err().unwrap();
let mut diagnostic_location = DiagnosticWithLocationError::from_error(error);
diagnostic_location.help = help;
#[cfg(debug_assertions)]
{
let (filepath, offset) =
SourceOffset::from_current_location().expect("Only accessible from dev build.");
let file =
::std::fs::read_to_string(&filepath).expect("All files should be availble in dev.");
diagnostic_location.source_code = Some(file);
diagnostic_location.caller_location = SourceSpan::new(offset, 0);
diagnostic_location.msg = Some(format!("Error occured at {}", location));
}
Err(Report::from(diagnostic_location))
}
}