Skip to main content

kashida/
error.rs

1//! Pattern-compilation errors.
2
3use core::fmt;
4
5/// An error while compiling pattern text.
6#[derive(Debug, Clone, PartialEq, Eq)]
7#[non_exhaustive]
8pub struct CompileError {
9    /// What went wrong.
10    pub kind: CompileErrorKind,
11    /// 1-based line number into the compiled pattern text.
12    pub line_number: usize,
13}
14
15/// What went wrong on a pattern line.
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum CompileErrorKind {
19    /// A `[…]` length guard that is malformed or that no run can satisfy.
20    InvalidLengthGuard(String),
21    /// A `[` length guard with no closing `]`.
22    UnterminatedLengthGuard,
23    /// A `{` group set with no closing `}`.
24    UnterminatedGroupSet,
25    /// A `{}` group set with no group names.
26    EmptyGroupSet,
27    /// A group name that is not a Unicode Joining_Group long name.
28    UnknownGroupName(String),
29    /// A token after the trailing `.` run boundary.
30    TokenAfterTrailingBoundary,
31    /// A `\` in a two-digit priority not followed by a digit.
32    ExpectedDigitAfterBackslash,
33    /// A two-digit priority whose second digit is greater than its first.
34    IncreasingPriority {
35        /// The starting priority.
36        base: u8,
37        /// The end priority it drops to.
38        min: u8,
39    },
40    /// A `\` that does not follow a priority digit.
41    BackslashWithoutDigit,
42    /// A `^` not followed by `{`, `@`, or `=`.
43    CaretNotFollowed,
44    /// An `@` or `=` with no group name after it.
45    EmptyGroupName,
46    /// A pattern line with no letter tokens.
47    NoLetters,
48    /// A character that is neither pattern syntax nor a joining letter.
49    StrayCharacter(char),
50    /// Two weights (digits or `!`) in the same inter-token gap.
51    ConflictingWeights,
52    /// A weight in the gap between a token and a `.`, where no junction
53    /// exists.
54    WeightOutsideRun,
55}
56
57impl fmt::Display for CompileErrorKind {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        match self {
60            CompileErrorKind::InvalidLengthGuard(body) => {
61                write!(f, "Invalid length guard “[{body}]”")
62            }
63            CompileErrorKind::UnterminatedLengthGuard => write!(f, "Unterminated length guard"),
64            CompileErrorKind::UnterminatedGroupSet => write!(f, "Unterminated “{{” group set"),
65            CompileErrorKind::EmptyGroupSet => write!(f, "Empty “{{}}” group set"),
66            CompileErrorKind::UnknownGroupName(name) => {
67                write!(f, "Unknown Unicode Joining_Group name “{name}”")
68            }
69            CompileErrorKind::TokenAfterTrailingBoundary => {
70                write!(f, "Token after a trailing “.” boundary")
71            }
72            CompileErrorKind::ExpectedDigitAfterBackslash => {
73                write!(f, "Expected a digit after “\\”")
74            }
75            CompileErrorKind::IncreasingPriority { base, min } => {
76                write!(f, "Priority must not increase ({base}\\{min})")
77            }
78            CompileErrorKind::BackslashWithoutDigit => {
79                write!(f, "“\\” must follow a priority digit")
80            }
81            CompileErrorKind::CaretNotFollowed => {
82                write!(f, "“^” must be followed by “{{”, “@”, or “=”")
83            }
84            CompileErrorKind::EmptyGroupName => write!(f, "Empty group name"),
85            CompileErrorKind::NoLetters => write!(f, "Pattern has no letters"),
86            CompileErrorKind::StrayCharacter(ch) => write!(f, "Stray character {ch:?}"),
87            CompileErrorKind::ConflictingWeights => {
88                write!(f, "Conflicting weights at one junction")
89            }
90            CompileErrorKind::WeightOutsideRun => {
91                write!(f, "Weight outside the run at a “.” boundary")
92            }
93        }
94    }
95}
96
97impl fmt::Display for CompileError {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        write!(f, "line {}: {}", self.line_number, self.kind)
100    }
101}
102
103impl std::error::Error for CompileError {}