use std::fmt::{self, Display};
use serde::{de, ser};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
line: Option<usize>,
column: Option<usize>,
}
impl Error {
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn line(&self) -> Option<usize> {
self.line
}
pub fn column(&self) -> Option<usize> {
self.column
}
pub(crate) fn with_location(mut self, line: usize, column: usize) -> Self {
if self.line.is_none() {
self.line = Some(line);
self.column = Some(column);
}
self
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Error {
kind,
line: None,
column: None,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorKind {
Message(String),
Io(String),
Eof,
Syntax,
ExpectedBoolean,
ExpectedInteger,
ExpectedFloat,
ExpectedString,
ExpectedArray,
ExpectedArrayComma,
ExpectedArrayEnd,
ExpectedArraySemicolon,
ExpectedList,
ExpectedListComma,
ExpectedListEnd,
ExpectedGroup,
ExpectedEquals,
ExpectedSemicolon,
ExpectedCommaOrSemicolon,
ExpectedGroupEnd,
ExpectedIdentifier,
TrailingCharacters,
HeterogeneousArray,
InvalidEscape,
InvalidNumber,
KeyMustBeAString,
UnitNotSupported,
TypeNotSupported(String),
IntegerOutOfRange,
FloatOutOfRange,
NonFiniteFloat,
InvalidSettingName(String),
InvalidPath(String),
PathTraversal(String),
ArrayElementNotScalar,
TopLevelBraces,
UnterminatedComment,
Include(String),
}
impl ser::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
ErrorKind::Message(msg.to_string()).into()
}
}
impl de::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
ErrorKind::Message(msg.to_string()).into()
}
}
impl Display for ErrorKind {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
ErrorKind::Message(msg) => formatter.write_str(msg),
ErrorKind::Io(msg) => write!(formatter, "i/o error: {}", msg),
ErrorKind::Eof => formatter.write_str("unexpected end of input"),
ErrorKind::Syntax => formatter.write_str("syntax error"),
ErrorKind::ExpectedBoolean => formatter.write_str("expected boolean"),
ErrorKind::ExpectedInteger => formatter.write_str("expected integer"),
ErrorKind::ExpectedFloat => formatter.write_str("expected float"),
ErrorKind::ExpectedString => formatter.write_str("expected string"),
ErrorKind::ExpectedArray => formatter.write_str("expected array"),
ErrorKind::ExpectedArrayComma => formatter.write_str("expected comma in array"),
ErrorKind::ExpectedArrayEnd => formatter.write_str("expected end of array"),
ErrorKind::ExpectedArraySemicolon => {
formatter.write_str("expected semicolon after array")
}
ErrorKind::ExpectedList => formatter.write_str("expected list"),
ErrorKind::ExpectedListComma => formatter.write_str("expected comma in list"),
ErrorKind::ExpectedListEnd => formatter.write_str("expected end of list"),
ErrorKind::ExpectedGroup => formatter.write_str("expected group"),
ErrorKind::ExpectedEquals => formatter.write_str("expected '=' or ':'"),
ErrorKind::ExpectedSemicolon => formatter.write_str("expected semicolon"),
ErrorKind::ExpectedCommaOrSemicolon => {
formatter.write_str("expected comma or semicolon")
}
ErrorKind::ExpectedGroupEnd => formatter.write_str("expected end of group"),
ErrorKind::ExpectedIdentifier => formatter.write_str("expected identifier"),
ErrorKind::TrailingCharacters => formatter.write_str("trailing characters"),
ErrorKind::HeterogeneousArray => {
formatter.write_str("heterogeneous arrays not supported")
}
ErrorKind::InvalidEscape => formatter.write_str("invalid escape sequence"),
ErrorKind::InvalidNumber => formatter.write_str("invalid number"),
ErrorKind::KeyMustBeAString => formatter.write_str("map keys must be strings"),
ErrorKind::UnitNotSupported => formatter.write_str("unit type not supported"),
ErrorKind::TypeNotSupported(ty) => write!(formatter, "type not supported: {}", ty),
ErrorKind::IntegerOutOfRange => {
formatter.write_str("integer value out of range for target type")
}
ErrorKind::FloatOutOfRange => {
formatter.write_str("float value out of range for target type")
}
ErrorKind::NonFiniteFloat => {
formatter.write_str("non-finite floats (inf, NaN) cannot be represented")
}
ErrorKind::InvalidSettingName(name) => write!(
formatter,
"invalid setting name '{}': names must match [A-Za-z*][-A-Za-z0-9_*]*",
name
),
ErrorKind::InvalidPath(path) => write!(formatter, "invalid path '{}'", path),
ErrorKind::PathTraversal(msg) => write!(formatter, "path traversal failed: {}", msg),
ErrorKind::ArrayElementNotScalar => {
formatter.write_str("arrays may only contain scalar values")
}
ErrorKind::TopLevelBraces => formatter.write_str(
"braces are not allowed around the top level of a configuration; \
write settings directly (name = value;)",
),
ErrorKind::UnterminatedComment => {
formatter.write_str("unterminated block comment: missing '*/'")
}
ErrorKind::Include(msg) => write!(formatter, "include error: {}", msg),
}
}
}
impl Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match (self.line, self.column) {
(Some(line), Some(column)) => {
write!(formatter, "{} at line {}, column {}", self.kind, line, column)
}
_ => self.kind.fmt(formatter),
}
}
}
impl std::error::Error for Error {}