1use core::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum IcuErrorKind {
10 SyntaxError,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct IcuPosition {
17 pub offset: usize,
19 pub line: usize,
21 pub column: usize,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27#[non_exhaustive]
28pub struct IcuParseError {
29 pub kind: IcuErrorKind,
31 pub message: String,
33 pub position: IcuPosition,
35}
36
37impl IcuParseError {
38 #[must_use]
40 pub fn syntax(message: impl Into<String>, input: &str, offset: usize) -> Self {
41 Self {
42 kind: IcuErrorKind::SyntaxError,
43 message: message.into(),
44 position: position_for_offset(input, offset),
45 }
46 }
47}
48
49impl fmt::Display for IcuParseError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(
52 f,
53 "{} at line {}, column {}",
54 self.message, self.position.line, self.position.column
55 )
56 }
57}
58
59impl std::error::Error for IcuParseError {}
60
61fn position_for_offset(input: &str, offset: usize) -> IcuPosition {
62 let clamped = offset.min(input.len());
63 let mut line = 1usize;
64 let mut column = 1usize;
65
66 for ch in input[..clamped].chars() {
67 if ch == '\n' {
68 line += 1;
69 column = 1;
70 } else {
71 column += 1;
72 }
73 }
74
75 IcuPosition {
76 offset: clamped,
77 line,
78 column,
79 }
80}