Skip to main content

sif_parser/
error.rs

1use std::fmt;
2
3/// Position within a SIF document for error reporting.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Pos {
6    pub line: usize,
7    pub col: usize,
8}
9
10impl fmt::Display for Pos {
11    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12        write!(f, "{}:{}", self.line, self.col)
13    }
14}
15
16/// A SIF parse error.
17#[derive(Debug, Clone, PartialEq)]
18pub struct Error {
19    pub kind: ErrorKind,
20    pub pos: Option<Pos>,
21    pub message: String,
22}
23
24impl fmt::Display for Error {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        if let Some(pos) = &self.pos {
27            write!(f, "[{}] {}: {}", pos, self.kind, self.message)
28        } else {
29            write!(f, "{}: {}", self.kind, self.message)
30        }
31    }
32}
33
34impl std::error::Error for Error {}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ErrorKind {
38    /// Missing or malformed `#!sif v1` header.
39    InvalidHeader,
40    /// Unsupported SIF version.
41    UnsupportedVersion,
42    /// Malformed `#schema` directive.
43    InvalidSchema,
44    /// Unknown or malformed type in a schema field definition.
45    InvalidType,
46    /// Value does not match its declared type.
47    TypeMismatch,
48    /// Malformed record (wrong field count, bad value, etc.).
49    InvalidRecord,
50    /// Malformed string literal (bad escaping, unterminated quote).
51    InvalidString,
52    /// Malformed array literal.
53    InvalidArray,
54    /// Malformed map literal.
55    InvalidMap,
56    /// Malformed directive.
57    InvalidDirective,
58    /// Malformed block (missing `#/block`, etc.).
59    InvalidBlock,
60    /// Malformed template.
61    InvalidTemplate,
62    /// Duplicate field name in schema.
63    DuplicateField,
64    /// Duplicate section identifier.
65    DuplicateSectionId,
66    /// Record appears before any schema in the section.
67    RecordWithoutSchema,
68    /// Unexpected end of input.
69    UnexpectedEof,
70    /// A BOM was found (§4: warning-level).
71    ByteOrderMark,
72}
73
74impl fmt::Display for ErrorKind {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        match self {
77            ErrorKind::InvalidHeader => write!(f, "invalid header"),
78            ErrorKind::UnsupportedVersion => write!(f, "unsupported version"),
79            ErrorKind::InvalidSchema => write!(f, "invalid schema"),
80            ErrorKind::InvalidType => write!(f, "invalid type"),
81            ErrorKind::TypeMismatch => write!(f, "type mismatch"),
82            ErrorKind::InvalidRecord => write!(f, "invalid record"),
83            ErrorKind::InvalidString => write!(f, "invalid string"),
84            ErrorKind::InvalidArray => write!(f, "invalid array"),
85            ErrorKind::InvalidMap => write!(f, "invalid map"),
86            ErrorKind::InvalidDirective => write!(f, "invalid directive"),
87            ErrorKind::InvalidBlock => write!(f, "invalid block"),
88            ErrorKind::InvalidTemplate => write!(f, "invalid template"),
89            ErrorKind::DuplicateField => write!(f, "duplicate field"),
90            ErrorKind::DuplicateSectionId => write!(f, "duplicate section id"),
91            ErrorKind::RecordWithoutSchema => write!(f, "record without schema"),
92            ErrorKind::UnexpectedEof => write!(f, "unexpected end of input"),
93            ErrorKind::ByteOrderMark => write!(f, "byte order mark"),
94        }
95    }
96}
97
98pub type Result<T> = std::result::Result<T, Error>;
99
100pub(crate) fn err(kind: ErrorKind, line: usize, msg: impl Into<String>) -> Error {
101    Error {
102        kind,
103        pos: Some(Pos { line, col: 0 }),
104        message: msg.into(),
105    }
106}