use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct LocationDetails {
pub offset: usize,
pub line: usize,
pub column: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct Location {
pub start: LocationDetails,
pub end: LocationDetails,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ParserError {
pub kind: ErrorKind,
pub message: String,
pub location: Location,
}
impl ParserError {
pub fn new(kind: ErrorKind, message: String, location: Location) -> Self {
Self {
kind,
message,
location,
}
}
}
impl std::fmt::Display for ParserError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} at line {}, column {}: {}",
self.kind, self.location.start.line, self.location.start.column, self.message
)
}
}
impl std::error::Error for ParserError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ErrorKind {
ExpectArgumentClosingBrace = 1,
EmptyArgument = 2,
MalformedArgument = 3,
ExpectArgumentType = 4,
InvalidArgumentType = 5,
ExpectArgumentStyle = 6,
InvalidNumberSkeleton = 7,
InvalidDateTimeSkeleton = 8,
ExpectNumberSkeleton = 9,
ExpectDateTimeSkeleton = 10,
UnclosedQuoteInArgumentStyle = 11,
ExpectSelectArgumentOptions = 12,
ExpectPluralArgumentOffsetValue = 13,
InvalidPluralArgumentOffsetValue = 14,
ExpectSelectArgumentSelector = 15,
ExpectPluralArgumentSelector = 16,
ExpectSelectArgumentSelectorFragment = 17,
ExpectPluralArgumentSelectorFragment = 18,
InvalidPluralArgumentSelector = 19,
DuplicatePluralArgumentSelector = 20,
DuplicateSelectArgumentSelector = 21,
MissingOtherClause = 22,
InvalidTag = 23,
InvalidTagName = 25,
UnmatchedClosingTag = 26,
UnclosedTag = 27,
}
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::ExpectArgumentClosingBrace => write!(f, "EXPECT_ARGUMENT_CLOSING_BRACE"),
ErrorKind::EmptyArgument => write!(f, "EMPTY_ARGUMENT"),
ErrorKind::MalformedArgument => write!(f, "MALFORMED_ARGUMENT"),
ErrorKind::ExpectArgumentType => write!(f, "EXPECT_ARGUMENT_TYPE"),
ErrorKind::InvalidArgumentType => write!(f, "INVALID_ARGUMENT_TYPE"),
ErrorKind::ExpectArgumentStyle => write!(f, "EXPECT_ARGUMENT_STYLE"),
ErrorKind::InvalidNumberSkeleton => write!(f, "INVALID_NUMBER_SKELETON"),
ErrorKind::InvalidDateTimeSkeleton => write!(f, "INVALID_DATE_TIME_SKELETON"),
ErrorKind::ExpectNumberSkeleton => write!(f, "EXPECT_NUMBER_SKELETON"),
ErrorKind::ExpectDateTimeSkeleton => write!(f, "EXPECT_DATE_TIME_SKELETON"),
ErrorKind::UnclosedQuoteInArgumentStyle => {
write!(f, "UNCLOSED_QUOTE_IN_ARGUMENT_STYLE")
}
ErrorKind::ExpectSelectArgumentOptions => write!(f, "EXPECT_SELECT_ARGUMENT_OPTIONS"),
ErrorKind::ExpectPluralArgumentOffsetValue => {
write!(f, "EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE")
}
ErrorKind::InvalidPluralArgumentOffsetValue => {
write!(f, "INVALID_PLURAL_ARGUMENT_OFFSET_VALUE")
}
ErrorKind::ExpectSelectArgumentSelector => write!(f, "EXPECT_SELECT_ARGUMENT_SELECTOR"),
ErrorKind::ExpectPluralArgumentSelector => write!(f, "EXPECT_PLURAL_ARGUMENT_SELECTOR"),
ErrorKind::ExpectSelectArgumentSelectorFragment => {
write!(f, "EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT")
}
ErrorKind::ExpectPluralArgumentSelectorFragment => {
write!(f, "EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT")
}
ErrorKind::InvalidPluralArgumentSelector => {
write!(f, "INVALID_PLURAL_ARGUMENT_SELECTOR")
}
ErrorKind::DuplicatePluralArgumentSelector => {
write!(f, "DUPLICATE_PLURAL_ARGUMENT_SELECTOR")
}
ErrorKind::DuplicateSelectArgumentSelector => {
write!(f, "DUPLICATE_SELECT_ARGUMENT_SELECTOR")
}
ErrorKind::MissingOtherClause => write!(f, "MISSING_OTHER_CLAUSE"),
ErrorKind::InvalidTag => write!(f, "INVALID_TAG"),
ErrorKind::InvalidTagName => write!(f, "INVALID_TAG_NAME"),
ErrorKind::UnmatchedClosingTag => write!(f, "UNMATCHED_CLOSING_TAG"),
ErrorKind::UnclosedTag => write!(f, "UNCLOSED_TAG"),
}
}
}
impl Serialize for ErrorKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u8(*self as u8)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_kind_discriminants() {
assert_eq!(ErrorKind::ExpectArgumentClosingBrace as u8, 1);
assert_eq!(ErrorKind::EmptyArgument as u8, 2);
assert_eq!(ErrorKind::MalformedArgument as u8, 3);
assert_eq!(ErrorKind::InvalidTagName as u8, 25);
assert_eq!(ErrorKind::UnmatchedClosingTag as u8, 26);
assert_eq!(ErrorKind::UnclosedTag as u8, 27);
}
#[test]
fn test_parser_error_display() {
let location = Location {
start: LocationDetails {
offset: 0,
line: 1,
column: 5,
},
end: LocationDetails {
offset: 10,
line: 1,
column: 15,
},
};
let error = ParserError::new(
ErrorKind::EmptyArgument,
"Argument is empty".to_string(),
location,
);
let display = format!("{}", error);
assert!(display.contains("EMPTY_ARGUMENT"));
assert!(display.contains("line 1"));
assert!(display.contains("column 5"));
assert!(display.contains("Argument is empty"));
}
#[test]
fn test_location_details() {
let details = LocationDetails {
offset: 10,
line: 2,
column: 5,
};
assert_eq!(details.offset, 10);
assert_eq!(details.line, 2);
assert_eq!(details.column, 5);
}
}