use std::fmt;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position {
pub line: usize,
pub column: usize,
pub offset: usize,
}
impl Position {
#[inline]
pub const fn new(line: usize, column: usize, offset: usize) -> Self {
Self {
line,
column,
offset,
}
}
#[inline]
pub const fn start() -> Self {
Self {
line: 1,
column: 1,
offset: 0,
}
}
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ErrorKind {
#[error("unexpected end of input")]
UnexpectedEof,
#[error("invalid indentation: expected at least {expected} spaces, found {found}")]
InvalidIndentation { expected: usize, found: usize },
#[error("tab characters are not allowed for indentation; use spaces")]
TabInIndentation,
#[error("unexpected character '{0}'")]
UnexpectedCharacter(char),
#[error("expected ':' separating mapping key and value")]
ExpectedColon,
#[error("expected mapping key")]
ExpectedKey,
#[error("expected value")]
ExpectedValue,
#[error("unclosed delimiter '{0}'")]
UnclosedDelimiter(char),
#[error("invalid flow syntax: {0}")]
InvalidFlowSyntax(&'static str),
#[error("duplicate mapping key '{0}'")]
DuplicateKey(String),
#[error("{0}")]
Custom(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("yaml parse error at {position}: {kind}")]
pub struct YamlError {
pub kind: ErrorKind,
pub position: Position,
}
impl YamlError {
#[inline]
pub const fn new(kind: ErrorKind, position: Position) -> Self {
Self { kind, position }
}
pub fn custom(msg: impl Into<String>, position: Position) -> Self {
Self {
kind: ErrorKind::Custom(msg.into()),
position,
}
}
}
pub type Result<T> = std::result::Result<T, YamlError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display_formatting() {
let pos = Position::new(4, 12, 45);
let err = YamlError::new(ErrorKind::TabInIndentation, pos);
assert_eq!(
err.to_string(),
"yaml parse error at 4:12: tab characters are not allowed for indentation; use spaces"
);
}
#[test]
fn test_error_kinds_display() {
let pos = Position::start();
let eof_err = YamlError::new(ErrorKind::UnexpectedEof, pos);
assert_eq!(
eof_err.to_string(),
"yaml parse error at 1:1: unexpected end of input"
);
let indent_err = YamlError::new(
ErrorKind::InvalidIndentation {
expected: 4,
found: 2,
},
pos,
);
assert_eq!(
indent_err.to_string(),
"yaml parse error at 1:1: invalid indentation: expected at least 4 spaces, found 2"
);
}
}