// Copyright 2026 moonbit-toml contributors
//
// SPDX-License-Identifier: MIT
///|
/// A position (1-based line and column) inside a TOML document.
pub(all) struct Position {
line : Int
column : Int
} derive(Eq, Debug)
///|
/// Structured description of a TOML parse failure.
pub(all) enum ParseErrorData {
/// A character appeared where it is not allowed.
UnexpectedChar(Position, Char)
/// The document ended in the middle of a construct.
UnexpectedEof(Position)
/// A string (or multi-line string) was never closed.
UnterminatedString(Position)
/// An invalid escape sequence such as `\q`.
InvalidEscape(Position, Char)
/// A `\uXXXX` / `\UXXXXXXXX` escape that is out of range or a surrogate.
InvalidUnicodeEscape(Position, String)
/// An integer or float literal that violates TOML syntax or the 64-bit range.
InvalidNumber(Position, String)
/// A date/time literal that violates RFC 3339 style syntax.
InvalidDatetime(Position, String)
/// A value that does not start any known TOML production.
InvalidValue(Position, Char)
/// The same key (or table) was defined twice.
DuplicateKey(Position, String)
/// A `[table]` / `[[array of tables]]` header conflicts with existing data.
TableConflict(Position, String)
/// Anything else, with a human readable description.
InvalidSyntax(Position, String)
} derive(Eq, Debug)
///|
/// The error type raised by `parse` and `parse_value`.
pub suberror ParseError {
ParseError(ParseErrorData)
} derive(Debug)
///|
fn ParseErrorData::message(self : ParseErrorData) -> String {
match self {
UnexpectedChar(pos, c) =>
"line \{pos.line}, column \{pos.column}: unexpected character '\{c}'"
UnexpectedEof(pos) =>
"line \{pos.line}, column \{pos.column}: unexpected end of input"
UnterminatedString(pos) =>
"line \{pos.line}, column \{pos.column}: unterminated string"
InvalidEscape(pos, c) =>
"line \{pos.line}, column \{pos.column}: invalid escape sequence '\\\{c}'"
InvalidUnicodeEscape(pos, text) =>
"line \{pos.line}, column \{pos.column}: invalid unicode escape '\{text}'"
InvalidNumber(pos, text) =>
"line \{pos.line}, column \{pos.column}: invalid number '\{text}'"
InvalidDatetime(pos, text) =>
"line \{pos.line}, column \{pos.column}: invalid datetime '\{text}'"
InvalidValue(pos, c) =>
"line \{pos.line}, column \{pos.column}: invalid value starting with '\{c}'"
DuplicateKey(pos, key) =>
"line \{pos.line}, column \{pos.column}: duplicate key '\{key}'"
TableConflict(pos, key) =>
"line \{pos.line}, column \{pos.column}: conflicting table definition for '\{key}'"
InvalidSyntax(pos, msg) => "line \{pos.line}, column \{pos.column}: \{msg}"
}
}
///|
pub impl Show for ParseError with fn output(self, logger) {
logger.write_object(self.message())
}
///|
/// A short human readable description of the error, e.g.
/// `line 3, column 5: duplicate key 'apple'`.
pub fn ParseError::message(self : ParseError) -> String {
match self {
ParseError(data) => data.message()
}
}