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