use crate::Span;
use std::fmt::{self, Debug, Display};
#[derive(Debug, Clone)]
pub struct Error {
pub kind: ErrorKind,
pub span: Span,
pub line_info: Option<(usize, usize)>,
}
impl std::error::Error for Error {}
impl From<(ErrorKind, Span)> for Error {
fn from((kind, span): (ErrorKind, Span)) -> Self {
Self {
kind,
span,
line_info: None,
}
}
}
#[derive(Debug, Clone)]
pub enum ErrorKind {
UnexpectedEof,
InvalidCharInString(char),
InvalidEscape(char),
InvalidHexEscape(char),
InvalidEscapeValue(u32),
Unexpected(char),
UnterminatedString,
InvalidNumber,
OutOfRange(&'static str),
Wanted {
expected: &'static str,
found: &'static str,
},
DuplicateTable {
name: String,
first: Span,
},
DuplicateKey {
key: String,
first: Span,
},
RedefineAsArray,
MultilineStringKey,
Custom(std::borrow::Cow<'static, str>),
DottedKeyInvalidType {
first: Span,
},
UnexpectedKeys {
keys: Vec<(String, Span)>,
expected: Vec<String>,
},
UnquotedString,
MissingField(&'static str),
Deprecated {
old: &'static str,
new: &'static str,
},
UnexpectedValue {
expected: &'static [&'static str],
},
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedEof => f.write_str("unexpected-eof"),
Self::Custom(..) => f.write_str("custom"),
Self::DottedKeyInvalidType { .. } => f.write_str("dotted-key-invalid-type"),
Self::DuplicateKey { .. } => f.write_str("duplicate-key"),
Self::DuplicateTable { .. } => f.write_str("duplicate-table"),
Self::UnexpectedKeys { .. } => f.write_str("unexpected-keys"),
Self::UnquotedString => f.write_str("unquoted-string"),
Self::MultilineStringKey => f.write_str("multiline-string-key"),
Self::RedefineAsArray => f.write_str("redefine-as-array"),
Self::InvalidCharInString(..) => f.write_str("invalid-char-in-string"),
Self::InvalidEscape(..) => f.write_str("invalid-escape"),
Self::InvalidEscapeValue(..) => f.write_str("invalid-escape-value"),
Self::InvalidHexEscape(..) => f.write_str("invalid-hex-escape"),
Self::Unexpected(..) => f.write_str("unexpected"),
Self::UnterminatedString => f.write_str("unterminated-string"),
Self::InvalidNumber => f.write_str("invalid-number"),
Self::OutOfRange(_) => f.write_str("out-of-range"),
Self::Wanted { .. } => f.write_str("wanted"),
Self::MissingField(..) => f.write_str("missing-field"),
Self::Deprecated { .. } => f.write_str("deprecated"),
Self::UnexpectedValue { .. } => f.write_str("unexpected-value"),
}
}
}
struct Escape(char);
impl fmt::Display for Escape {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use std::fmt::Write as _;
if self.0.is_whitespace() {
for esc in self.0.escape_default() {
f.write_char(esc)?;
}
Ok(())
} else {
f.write_char(self.0)
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
ErrorKind::UnexpectedEof => f.write_str("unexpected eof encountered")?,
ErrorKind::InvalidCharInString(c) => {
write!(f, "invalid character in string: `{}`", Escape(*c))?;
}
ErrorKind::InvalidEscape(c) => {
write!(f, "invalid escape character in string: `{}`", Escape(*c))?;
}
ErrorKind::InvalidHexEscape(c) => write!(
f,
"invalid hex escape character in string: `{}`",
Escape(*c)
)?,
ErrorKind::InvalidEscapeValue(c) => write!(f, "invalid escape value: `{c}`")?,
ErrorKind::Unexpected(c) => write!(f, "unexpected character found: `{}`", Escape(*c))?,
ErrorKind::UnterminatedString => f.write_str("unterminated string")?,
ErrorKind::Wanted { expected, found } => {
write!(f, "expected {expected}, found {found}")?;
}
ErrorKind::InvalidNumber => f.write_str("invalid number")?,
ErrorKind::OutOfRange(kind) => write!(f, "out of range of '{kind}'")?,
ErrorKind::DuplicateTable { name, .. } => {
write!(f, "redefinition of table `{name}`")?;
}
ErrorKind::DuplicateKey { key, .. } => {
write!(f, "duplicate key: `{key}`")?;
}
ErrorKind::RedefineAsArray => f.write_str("table redefined as array")?,
ErrorKind::MultilineStringKey => {
f.write_str("multiline strings are not allowed for key")?;
}
ErrorKind::Custom(message) => f.write_str(message)?,
ErrorKind::DottedKeyInvalidType { .. } => {
f.write_str("dotted key attempted to extend non-table type")?;
}
ErrorKind::UnexpectedKeys { keys, expected } => write!(
f,
"unexpected keys in table: `{keys:?}`\nexpected: {expected:?}"
)?,
ErrorKind::UnquotedString => {
f.write_str("invalid TOML value, did you mean to use a quoted string?")?;
}
ErrorKind::MissingField(field) => write!(f, "missing field '{field}' in table")?,
ErrorKind::Deprecated { old, new } => {
write!(f, "field '{old}' is deprecated, '{new}' has replaced it")?;
}
ErrorKind::UnexpectedValue { expected } => write!(f, "expected '{expected:?}'")?,
}
Ok(())
}
}
#[cfg(feature = "reporting")]
#[cfg_attr(docsrs, doc(cfg(feature = "reporting")))]
impl Error {
pub fn to_diagnostic<FileId: Copy + PartialEq>(
&self,
fid: FileId,
) -> codespan_reporting::diagnostic::Diagnostic<FileId> {
let diag =
codespan_reporting::diagnostic::Diagnostic::error().with_code(self.kind.to_string());
use codespan_reporting::diagnostic::Label;
let diag = match &self.kind {
ErrorKind::DuplicateKey { first, .. } => diag.with_labels(vec![
Label::secondary(fid, *first).with_message("first key instance"),
Label::primary(fid, self.span).with_message("duplicate key"),
]),
ErrorKind::Unexpected(c) => diag.with_labels(vec![Label::primary(fid, self.span)
.with_message(format!("unexpected character '{}'", Escape(*c)))]),
ErrorKind::InvalidCharInString(c) => {
diag.with_labels(vec![Label::primary(fid, self.span)
.with_message(format!("invalid character '{}' in string", Escape(*c)))])
}
ErrorKind::InvalidEscape(c) => diag.with_labels(vec![Label::primary(fid, self.span)
.with_message(format!(
"invalid escape character '{}' in string",
Escape(*c)
))]),
ErrorKind::InvalidEscapeValue(_) => diag
.with_labels(vec![
Label::primary(fid, self.span).with_message("invalid escape value")
]),
ErrorKind::InvalidNumber => diag.with_labels(vec![
Label::primary(fid, self.span).with_message("unable to parse number")
]),
ErrorKind::OutOfRange(kind) => diag
.with_message(format!("number is out of range of '{kind}'"))
.with_labels(vec![Label::primary(fid, self.span)]),
ErrorKind::Wanted { expected, .. } => diag
.with_labels(vec![
Label::primary(fid, self.span).with_message(format!("expected {expected}"))
]),
ErrorKind::MultilineStringKey => diag.with_labels(vec![
Label::primary(fid, self.span).with_message("multiline keys are not allowed")
]),
ErrorKind::UnterminatedString => diag
.with_labels(vec![Label::primary(fid, self.span)
.with_message("eof reached before string terminator")]),
ErrorKind::DuplicateTable { first, .. } => diag.with_labels(vec![
Label::secondary(fid, *first).with_message("first table instance"),
Label::primary(fid, self.span).with_message("duplicate table"),
]),
ErrorKind::InvalidHexEscape(c) => diag
.with_labels(vec![Label::primary(fid, self.span)
.with_message(format!("invalid hex escape '{}'", Escape(*c)))]),
ErrorKind::UnquotedString => diag.with_labels(vec![
Label::primary(fid, self.span).with_message("string is not quoted")
]),
ErrorKind::UnexpectedKeys { keys, expected } => diag
.with_message(format!(
"found {} unexpected keys, expected: {expected:?}",
keys.len()
))
.with_labels(
keys.iter()
.map(|(_name, span)| Label::secondary(fid, *span))
.collect(),
),
ErrorKind::MissingField(field) => diag
.with_message(format!("missing field '{field}'"))
.with_labels(vec![
Label::primary(fid, self.span).with_message("table with missing field")
]),
ErrorKind::Deprecated { new, .. } => diag
.with_message(format!(
"deprecated field enountered, '{new}' should be used instead"
))
.with_labels(vec![
Label::primary(fid, self.span).with_message("deprecated field")
]),
ErrorKind::UnexpectedValue { expected } => diag
.with_message(format!("expected '{expected:?}'"))
.with_labels(vec![
Label::primary(fid, self.span).with_message("unexpected value")
]),
ErrorKind::UnexpectedEof => diag
.with_message("unexpected end of file")
.with_labels(vec![Label::primary(fid, self.span)]),
ErrorKind::DottedKeyInvalidType { first } => {
diag.with_message(self.to_string()).with_labels(vec![
Label::primary(fid, self.span).with_message("attempted to extend table here"),
Label::secondary(fid, *first).with_message("non-table"),
])
}
ErrorKind::RedefineAsArray => diag
.with_message(self.to_string())
.with_labels(vec![Label::primary(fid, self.span)]),
ErrorKind::Custom(msg) => diag
.with_message(msg.to_string())
.with_labels(vec![Label::primary(fid, self.span)]),
};
diag
}
}
#[derive(Debug)]
pub struct DeserError {
pub errors: Vec<Error>,
}
impl DeserError {
#[inline]
pub fn merge(&mut self, mut other: Self) {
self.errors.append(&mut other.errors);
}
}
impl std::error::Error for DeserError {}
impl From<Error> for DeserError {
fn from(value: Error) -> Self {
Self {
errors: vec![value],
}
}
}
impl fmt::Display for DeserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for err in &self.errors {
writeln!(f, "{err}")?;
}
Ok(())
}
}