1use std::fmt;
2use std::io;
3
4#[derive(Debug)]
6pub struct Error {
7 kind: ErrorKind,
8 line: usize,
9 column: usize,
10}
11
12#[derive(Debug)]
13enum ErrorKind {
14 Message(String),
15 Io(io::Error),
16}
17
18impl Error {
19 pub(crate) fn syntax(message: impl Into<String>, input: &[u8], index: usize) -> Self {
20 let end = index.min(input.len());
21 let line = 1 + memchr::memchr_iter(b'\n', &input[..end]).count();
22 let column = input[..end]
23 .iter()
24 .rposition(|&byte| byte == b'\n')
25 .map_or(end + 1, |position| end - position);
26 Self {
27 kind: ErrorKind::Message(message.into()),
28 line,
29 column,
30 }
31 }
32
33 pub(crate) fn message(message: impl Into<String>) -> Self {
34 Self {
35 kind: ErrorKind::Message(message.into()),
36 line: 0,
37 column: 0,
38 }
39 }
40
41 #[must_use]
43 pub const fn line(&self) -> usize {
44 self.line
45 }
46
47 #[must_use]
49 pub const fn column(&self) -> usize {
50 self.column
51 }
52
53 #[must_use]
55 pub const fn is_io(&self) -> bool {
56 matches!(self.kind, ErrorKind::Io(_))
57 }
58}
59
60impl fmt::Display for Error {
61 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match &self.kind {
63 ErrorKind::Message(message) if self.line != 0 => {
64 write!(
65 formatter,
66 "{message} at line {} column {}",
67 self.line, self.column
68 )
69 }
70 ErrorKind::Message(message) => formatter.write_str(message),
71 ErrorKind::Io(error) => error.fmt(formatter),
72 }
73 }
74}
75
76impl std::error::Error for Error {
77 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
78 match &self.kind {
79 ErrorKind::Io(error) => Some(error),
80 ErrorKind::Message(_) => None,
81 }
82 }
83}
84
85impl serde::de::Error for Error {
86 fn custom<T: fmt::Display>(message: T) -> Self {
87 Self::message(message.to_string())
88 }
89}
90
91impl serde::ser::Error for Error {
92 fn custom<T: fmt::Display>(message: T) -> Self {
93 Self::message(message.to_string())
94 }
95}
96
97impl From<io::Error> for Error {
98 fn from(error: io::Error) -> Self {
99 Self {
100 kind: ErrorKind::Io(error),
101 line: 0,
102 column: 0,
103 }
104 }
105}
106
107impl From<Error> for io::Error {
108 fn from(error: Error) -> Self {
109 match error.kind {
110 ErrorKind::Io(error) => error,
111 ErrorKind::Message(message) => Self::new(io::ErrorKind::InvalidData, message),
112 }
113 }
114}
115
116pub type Result<T> = std::result::Result<T, Error>;