#![allow(unused_assignments)]
use crate::file::display_path;
use miette::{Diagnostic, NamedSource, SourceSpan};
use std::fmt;
use std::path::Path;
use thiserror::Error;
#[derive(Debug, Error, Diagnostic)]
#[error("Invalid TOML in config file")]
#[diagnostic(code(mise::config::parse_error))]
pub(crate) struct TomlParseError {
#[source_code]
src: NamedSource<String>,
#[label("{message}")]
span: SourceSpan,
message: String,
#[help]
help: Option<String>,
}
fn backslash_help(source: &str, span: &SourceSpan, message: &str) -> Option<String> {
if !(message.contains("escape") || message.contains("unicode")) {
return None;
}
if !failing_line(source, span.offset())?.contains('\\') {
return None;
}
Some(
"a backslash starts an escape inside a double-quoted TOML string. \
Write a Windows path as a literal string -- 'C:\\Users\\you' -- \
or double the backslashes: \"C:\\\\Users\\\\you\"."
.to_string(),
)
}
fn failing_line(source: &str, offset: usize) -> Option<&str> {
let offset = offset.min(source.len());
let before = source.get(..offset)?;
let start = before.rfind('\n').map_or(0, |i| i + 1);
let end = source
.get(offset..)?
.find('\n')
.map_or(source.len(), |i| offset + i);
source.get(start..end)
}
#[derive(Debug)]
pub(crate) struct MiseDiagnostic {
message: String,
rendered: String,
}
impl fmt::Display for MiseDiagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for MiseDiagnostic {}
impl MiseDiagnostic {
pub(crate) fn new<D: Diagnostic + Send + Sync + 'static>(diagnostic: D) -> Self {
let message = diagnostic.to_string();
let rendered = format!("{:?}", miette::Report::new(diagnostic));
MiseDiagnostic { message, rendered }
}
pub(crate) fn render(&self) -> &str {
&self.rendered
}
}
pub(crate) fn toml_parse_error(err: &toml::de::Error, source: &str, path: &Path) -> eyre::Report {
let message = err.message().to_string();
let span = err
.span()
.map(|r| SourceSpan::from((r.start, r.end.saturating_sub(r.start))))
.unwrap_or_else(|| SourceSpan::from((0, 0)));
let help = backslash_help(source, &span, &message);
let diagnostic = TomlParseError {
src: NamedSource::new(display_path(path), source.to_string()),
span,
message,
help,
};
eyre::Report::new(MiseDiagnostic::new(diagnostic))
}