libyaml_safer/
error.rs

1pub type Result<T, E = Error> = core::result::Result<T, E>;
2
3/// The pointer position.
4#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
5#[non_exhaustive]
6pub struct Mark {
7    /// The position index.
8    pub index: u64,
9    /// The position line.
10    pub line: u64,
11    /// The position column.
12    pub column: u64,
13}
14
15impl std::fmt::Display for Mark {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "line {} column {}", self.line + 1, self.column + 1)
18    }
19}
20
21#[derive(Debug)]
22#[allow(clippy::struct_field_names)]
23struct Problem {
24    pub problem: &'static str,
25    pub problem_mark: Mark,
26    pub context: &'static str,
27    pub context_mark: Mark,
28}
29
30#[derive(Debug)]
31enum ErrorImpl {
32    Reader {
33        problem: &'static str,
34        offset: usize,
35        value: i32,
36    },
37    Scanner(Problem),
38    Parser(Problem),
39    Composer(Problem),
40    Emitter(&'static str),
41    Io(std::io::Error),
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum ErrorKind {
46    Reader,
47    Scanner,
48    Parser,
49    Composer,
50    Emitter,
51    Io,
52}
53
54#[derive(Debug)]
55pub struct Error(Box<ErrorImpl>);
56
57impl From<std::io::Error> for Error {
58    fn from(value: std::io::Error) -> Self {
59        Self(Box::new(ErrorImpl::Io(value)))
60    }
61}
62
63impl Error {
64    pub(crate) fn reader(problem: &'static str, offset: usize, value: i32) -> Self {
65        Self(Box::new(ErrorImpl::Reader {
66            problem,
67            offset,
68            value,
69        }))
70    }
71
72    pub(crate) fn scanner(
73        context: &'static str,
74        context_mark: Mark,
75        problem: &'static str,
76        problem_mark: Mark,
77    ) -> Self {
78        Self(Box::new(ErrorImpl::Scanner(Problem {
79            problem,
80            problem_mark,
81            context,
82            context_mark,
83        })))
84    }
85
86    pub(crate) fn parser(
87        context: &'static str,
88        context_mark: Mark,
89        problem: &'static str,
90        problem_mark: Mark,
91    ) -> Self {
92        Self(Box::new(ErrorImpl::Parser(Problem {
93            problem,
94            problem_mark,
95            context,
96            context_mark,
97        })))
98    }
99
100    pub(crate) fn composer(
101        context: &'static str,
102        context_mark: Mark,
103        problem: &'static str,
104        problem_mark: Mark,
105    ) -> Self {
106        Self(Box::new(ErrorImpl::Composer(Problem {
107            problem,
108            problem_mark,
109            context,
110            context_mark,
111        })))
112    }
113
114    pub(crate) fn emitter(problem: &'static str) -> Self {
115        Self(Box::new(ErrorImpl::Emitter(problem)))
116    }
117
118    pub fn kind(&self) -> ErrorKind {
119        match &*self.0 {
120            ErrorImpl::Reader { .. } => ErrorKind::Reader,
121            ErrorImpl::Scanner(_) => ErrorKind::Scanner,
122            ErrorImpl::Parser(_) => ErrorKind::Parser,
123            ErrorImpl::Composer(_) => ErrorKind::Composer,
124            ErrorImpl::Emitter(_) => ErrorKind::Emitter,
125            ErrorImpl::Io(_) => ErrorKind::Io,
126        }
127    }
128
129    pub fn problem_mark(&self) -> Option<Mark> {
130        match &*self.0 {
131            ErrorImpl::Reader { .. } | ErrorImpl::Emitter(_) | ErrorImpl::Io(_) => None,
132            ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => {
133                Some(p.problem_mark)
134            }
135        }
136    }
137
138    pub fn context_mark(&self) -> Option<Mark> {
139        match &*self.0 {
140            ErrorImpl::Reader { .. } | ErrorImpl::Emitter(..) | ErrorImpl::Io(_) => None,
141            ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => {
142                if p.context.is_empty() {
143                    None
144                } else {
145                    Some(p.context_mark)
146                }
147            }
148        }
149    }
150
151    pub fn problem(&self) -> &'static str {
152        match &*self.0 {
153            ErrorImpl::Reader { problem, .. } | ErrorImpl::Emitter(problem) => problem,
154            ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => p.problem,
155            ErrorImpl::Io(_) => "I/O error",
156        }
157    }
158
159    pub fn context(&self) -> Option<&'static str> {
160        match &*self.0 {
161            ErrorImpl::Reader { .. } | ErrorImpl::Emitter(..) | ErrorImpl::Io(_) => None,
162            ErrorImpl::Scanner(p) | ErrorImpl::Parser(p) | ErrorImpl::Composer(p) => {
163                if p.context.is_empty() {
164                    None
165                } else {
166                    Some(p.context)
167                }
168            }
169        }
170    }
171}
172
173impl std::error::Error for Error {
174    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
175        if let ErrorImpl::Io(err) = &*self.0 {
176            Some(err)
177        } else {
178            None
179        }
180    }
181}
182
183impl TryFrom<Error> for std::io::Error {
184    type Error = Error;
185
186    fn try_from(value: Error) -> Result<Self, Self::Error> {
187        if value.kind() == ErrorKind::Io {
188            if let ErrorImpl::Io(err) = *value.0 {
189                Ok(err)
190            } else {
191                unreachable!()
192            }
193        } else {
194            Err(value)
195        }
196    }
197}
198
199impl core::fmt::Display for ErrorKind {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        f.write_str(match self {
202            ErrorKind::Reader => "Reader",
203            ErrorKind::Scanner => "Scanner",
204            ErrorKind::Parser => "Parser",
205            ErrorKind::Composer => "Composer",
206            ErrorKind::Emitter => "Emitter",
207            ErrorKind::Io => "I/O",
208        })
209    }
210}
211
212impl core::fmt::Display for Problem {
213    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214        let Self {
215            problem,
216            problem_mark,
217            context,
218            context_mark,
219        } = self;
220
221        if self.context.is_empty() {
222            write!(f, "{problem_mark}: {problem}")
223        } else {
224            write!(f, "{problem_mark}: {problem} {context} ({context_mark})")
225        }
226    }
227}
228
229impl core::fmt::Display for Error {
230    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231        write!(f, "{} error: ", self.kind())?;
232        match *self.0 {
233            ErrorImpl::Reader {
234                problem,
235                offset,
236                value,
237            } => write!(f, "{problem} (offset {offset}, value {value})"),
238            ErrorImpl::Scanner(ref p) | ErrorImpl::Parser(ref p) | ErrorImpl::Composer(ref p) => {
239                write!(f, "{p}")
240            }
241            ErrorImpl::Emitter(problem) => write!(f, "{problem}"),
242            ErrorImpl::Io(ref err) => write!(f, "{err}"),
243        }
244    }
245}