1use std::{
2 error::Error as StdError,
3 fmt::{self, Debug, Display},
4 io, result, string,
5 sync::Arc,
6};
7
8use serde::{de, ser};
9
10use crate::{
11 libyaml::{emitter, error as libyaml},
12 path::Path,
13};
14
15pub struct Error(Box<ErrorImpl>);
17
18pub type Result<T> = result::Result<T, Error>;
20
21#[derive(Debug)]
22pub(crate) enum ErrorImpl {
23 Message(String, Option<Pos>),
24
25 Libyaml(libyaml::Error),
26 Io(io::Error),
27 FromUtf8(string::FromUtf8Error),
28
29 EndOfStream,
30 MoreThanOneDocument,
31 RecursionLimitExceeded(libyaml::Mark),
32 RepetitionLimitExceeded,
33 BytesUnsupported,
34 UnknownAnchor(libyaml::Mark),
35 SerializeNestedEnum,
36 ScalarInMerge,
37 TaggedInMerge,
38 ScalarInMergeElement,
39 SequenceInMergeElement,
40 EmptyTag,
41 FailedToParseNumber,
42
43 Shared(Arc<ErrorImpl>),
44}
45
46#[derive(Debug)]
47pub(crate) struct Pos {
48 mark: libyaml::Mark,
49 path: String,
50}
51
52#[derive(Debug)]
54pub struct Location {
55 index: usize,
56 line: usize,
57 column: usize,
58}
59
60impl Location {
61 pub fn index(&self) -> usize {
63 self.index
64 }
65
66 pub fn line(&self) -> usize {
68 self.line
69 }
70
71 pub fn column(&self) -> usize {
73 self.column
74 }
75
76 #[doc(hidden)]
78 fn from_mark(mark: libyaml::Mark) -> Self {
79 Location {
80 index: mark.index() as usize,
81 line: mark.line() as usize + 1,
83 column: mark.column() as usize + 1,
84 }
85 }
86}
87
88impl Error {
89 pub fn location(&self) -> Option<Location> {
107 self.0.location()
108 }
109}
110
111pub(crate) fn new(inner: ErrorImpl) -> Error {
112 Error(Box::new(inner))
113}
114
115pub(crate) fn shared(shared: Arc<ErrorImpl>) -> Error {
116 Error(Box::new(ErrorImpl::Shared(shared)))
117}
118
119pub(crate) fn fix_mark(mut error: Error, mark: libyaml::Mark, path: Path) -> Error {
120 if let ErrorImpl::Message(_, none @ None) = error.0.as_mut() {
121 *none = Some(Pos {
122 mark,
123 path: path.to_string(),
124 });
125 }
126 error
127}
128
129impl Error {
130 pub(crate) fn shared(self) -> Arc<ErrorImpl> {
131 if let ErrorImpl::Shared(err) = *self.0 {
132 err
133 } else {
134 Arc::from(self.0)
135 }
136 }
137}
138
139impl From<libyaml::Error> for Error {
140 fn from(err: libyaml::Error) -> Self {
141 Error(Box::new(ErrorImpl::Libyaml(err)))
142 }
143}
144
145impl From<emitter::Error> for Error {
146 fn from(err: emitter::Error) -> Self {
147 match err {
148 emitter::Error::Libyaml(err) => Self::from(err),
149 emitter::Error::Io(err) => new(ErrorImpl::Io(err)),
150 }
151 }
152}
153
154impl StdError for Error {
155 fn source(&self) -> Option<&(dyn StdError + 'static)> {
156 self.0.source()
157 }
158}
159
160impl Display for Error {
161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162 self.0.display(f)
163 }
164}
165
166impl Debug for Error {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 self.0.debug(f)
171 }
172}
173
174impl ser::Error for Error {
175 fn custom<T: Display>(msg: T) -> Self {
176 Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
177 }
178}
179
180impl de::Error for Error {
181 fn custom<T: Display>(msg: T) -> Self {
182 Error(Box::new(ErrorImpl::Message(msg.to_string(), None)))
183 }
184}
185
186impl ErrorImpl {
187 fn location(&self) -> Option<Location> {
188 self.mark().map(Location::from_mark)
189 }
190
191 fn source(&self) -> Option<&(dyn StdError + 'static)> {
192 match self {
193 ErrorImpl::Io(err) => err.source(),
194 ErrorImpl::FromUtf8(err) => err.source(),
195 ErrorImpl::Shared(err) => err.source(),
196 _ => None,
197 }
198 }
199
200 fn mark(&self) -> Option<libyaml::Mark> {
201 match self {
202 ErrorImpl::Message(
203 _,
204 Some(Pos {
205 mark,
206 path: _,
207 }),
208 )
209 | ErrorImpl::RecursionLimitExceeded(mark)
210 | ErrorImpl::UnknownAnchor(mark) => Some(*mark),
211 ErrorImpl::Libyaml(err) => Some(err.mark()),
212 ErrorImpl::Shared(err) => err.mark(),
213 _ => None,
214 }
215 }
216
217 fn message_no_mark(&self, f: &mut fmt::Formatter) -> fmt::Result {
218 match self {
219 ErrorImpl::Message(msg, None) => f.write_str(msg),
220 ErrorImpl::Message(
221 msg,
222 Some(Pos {
223 mark: _,
224 path,
225 }),
226 ) => {
227 if path != "." {
228 write!(f, "{}: ", path)?;
229 }
230 f.write_str(msg)
231 }
232 ErrorImpl::Libyaml(_) => unreachable!(),
233 ErrorImpl::Io(err) => Display::fmt(err, f),
234 ErrorImpl::FromUtf8(err) => Display::fmt(err, f),
235 ErrorImpl::EndOfStream => f.write_str("EOF while parsing a value"),
236 ErrorImpl::MoreThanOneDocument => {
237 f.write_str("deserializing from YAML containing more than one document is not supported")
238 }
239 ErrorImpl::RecursionLimitExceeded(_mark) => f.write_str("recursion limit exceeded"),
240 ErrorImpl::RepetitionLimitExceeded => f.write_str("repetition limit exceeded"),
241 ErrorImpl::BytesUnsupported => {
242 f.write_str("serialization and deserialization of bytes in YAML is not implemented")
243 }
244 ErrorImpl::UnknownAnchor(_mark) => f.write_str("unknown anchor"),
245 ErrorImpl::SerializeNestedEnum => f.write_str("serializing nested enums in YAML is not supported yet"),
246 ErrorImpl::ScalarInMerge => {
247 f.write_str("expected a mapping or list of mappings for merging, but found scalar")
248 }
249 ErrorImpl::TaggedInMerge => f.write_str("unexpected tagged value in merge"),
250 ErrorImpl::ScalarInMergeElement => f.write_str("expected a mapping for merging, but found scalar"),
251 ErrorImpl::SequenceInMergeElement => f.write_str("expected a mapping for merging, but found sequence"),
252 ErrorImpl::EmptyTag => f.write_str("empty YAML tag is not allowed"),
253 ErrorImpl::FailedToParseNumber => f.write_str("failed to parse YAML number"),
254 ErrorImpl::Shared(_) => unreachable!(),
255 }
256 }
257
258 fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
259 match self {
260 ErrorImpl::Libyaml(err) => Display::fmt(err, f),
261 ErrorImpl::Shared(err) => err.display(f),
262 _ => {
263 self.message_no_mark(f)?;
264 if let Some(mark) = self.mark() {
265 if mark.line() != 0 || mark.column() != 0 {
266 write!(f, " at {}", mark)?;
267 }
268 }
269 Ok(())
270 }
271 }
272 }
273
274 fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
275 match self {
276 ErrorImpl::Libyaml(err) => Debug::fmt(err, f),
277 ErrorImpl::Shared(err) => err.debug(f),
278 _ => {
279 f.write_str("Error(")?;
280 struct MessageNoMark<'a>(&'a ErrorImpl);
281 impl<'a> Display for MessageNoMark<'a> {
282 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 self.0.message_no_mark(f)
284 }
285 }
286 let msg = MessageNoMark(self).to_string();
287 Debug::fmt(&msg, f)?;
288 if let Some(mark) = self.mark() {
289 write!(f, ", line: {}, column: {}", mark.line() + 1, mark.column() + 1,)?;
290 }
291 f.write_str(")")
292 }
293 }
294 }
295}