1use crate::io::{OwnedQualifiedName, Span};
2
3#[derive(thiserror::Error, Debug)]
5pub enum EMLErrorKind {
6 #[error("XML error: {0}")]
8 XmlError(#[from] quick_xml::Error),
9
10 #[error("I/O error: {0}")]
12 IoError(#[from] std::io::Error),
13
14 #[error("Escape error: {0}")]
16 EscapeError(#[from] quick_xml::escape::EscapeError),
17
18 #[error("Attribute error: {0}")]
20 AttributeError(#[from] quick_xml::events::attributes::AttrError),
21
22 #[error("Encoding error: {0}")]
24 EncodingError(#[from] quick_xml::encoding::EncodingError),
25
26 #[error("UTF-8 conversion error: {0}")]
28 FromUtf8Error(#[from] std::string::FromUtf8Error),
29
30 #[error("Unexpected end element")]
32 UnexpectedEndElement,
33
34 #[error("Unexpected end of file")]
36 UnexpectedEof,
37
38 #[error("Unexpected parsing event encountered")]
40 UnexpectedEvent,
41
42 #[error("Missing required element: {0}")]
44 MissingElement(OwnedQualifiedName),
45
46 #[error("Missing value for element: {0}")]
48 MissingElementValue(OwnedQualifiedName),
49
50 #[error("Missing any of these elements: {0:?}")]
52 MissingChoiceElements(Vec<OwnedQualifiedName>),
53
54 #[error("Missing required attribute: {0}")]
56 MissingAttribute(OwnedQualifiedName),
57
58 #[error("Unexpected element: {0} inside of {1}")]
60 UnexpectedElement(OwnedQualifiedName, OwnedQualifiedName),
61
62 #[error("Unknown namespace: {0}")]
64 UnknownNamespace(String),
65
66 #[error("Root element must be named EML")]
68 InvalidRootElement,
69
70 #[error("Schema version '{0}' is not supported, only version '5' is supported")]
72 SchemaVersionNotSupported(String),
73
74 #[error("Unknown document type: {0}")]
76 UnknownDocumentType(String),
77
78 #[error("Invalid document type: expected {0}, found {1}")]
80 InvalidDocumentType(&'static str, String),
81
82 #[error("Invalid value for {0}: {1}")]
84 InvalidValue(
85 OwnedQualifiedName,
86 #[source] Box<dyn std::error::Error + Send + Sync + 'static>,
87 ),
88
89 #[error("Error converting value: {0}")]
91 ValueConversionError(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
92
93 #[error("Attributes cannot have the default namespace")]
95 AttributeNamespaceError,
96
97 #[error("Elements cannot be in no namespace when a default namespace is defined")]
99 ElementNamespaceError,
100
101 #[error("Missing the ContestIdentifier element")]
103 MissingContenstIdentifier,
104
105 #[error("Used ElectionDate element without using the kiesraad namespace")]
107 InvalidElectionDateNamespace,
108
109 #[error("The RejectedVotes element with ReasonCode 'blanco' is missing")]
111 MissingRejectedVotesBlank,
112
113 #[error("The RejectedVotes element with ReasonCode 'ongeldig' is missing")]
115 MissingRejectedVotesInvalid,
116
117 #[error(
119 "A Selection is missing a selection type (i.e. Candidate, AffiliationIdentifier or ReferendumOptionIdentifier)"
120 )]
121 MissingSelectionType,
122
123 #[error("A required property '{0}' is missing for building this struct")]
125 MissingBuildProperty(&'static str),
126
127 #[error("The NominationDate is before the ElectionDate, which is not allowed")]
129 NominationDateNotBeforeElectionDate,
130
131 #[error("The ElectionSubcategory is not valid for the ElectionCategory")]
133 InvalidElectionSubcategory,
134
135 #[error("The voting method specified in the document is not supported, only SPV is supported")]
137 UnsupportedVotingMethod,
138
139 #[error("The preference threshold specified does not match the election identifier")]
141 InvalidPreferenceThreshold,
142
143 #[error("The number of seats specified does not match the subcategory")]
145 InvalidNumberOfSeats,
146
147 #[error("A referendum option was found where none was expected")]
149 UnexpectedReferendumOptionSelection,
150
151 #[error("A candidate without affiliation was found")]
153 CandidateWithoutAffiliationFound,
154
155 #[error("Custom error: {0}")]
157 Custom(Box<dyn CustomError>),
158}
159
160pub trait CustomError: std::fmt::Display + std::fmt::Debug + Send + Sync + 'static {}
162
163impl<T> CustomError for T where T: std::fmt::Display + std::fmt::Debug + Send + Sync + 'static {}
164
165impl EMLErrorKind {
166 pub(crate) fn with_span(self, span: Span) -> EMLError {
168 EMLError::Positioned { kind: self, span }
169 }
170
171 pub(crate) fn without_span(self) -> EMLError {
173 EMLError::UnknownPosition { kind: self }
174 }
175}
176
177#[derive(thiserror::Error, Debug)]
182pub enum EMLError {
183 #[error("Error in EML: {kind} at position {span:?}")]
185 Positioned {
186 kind: EMLErrorKind,
188 span: Span,
190 },
191 #[error("Error in EML: {kind}")]
193 UnknownPosition {
194 kind: EMLErrorKind,
196 },
197 #[error("Multiple errors in EML: {0}")]
199 Multiple(MultipleEMLErrors),
200}
201
202#[derive(Debug)]
204pub struct MultipleEMLErrors {
205 pub errors: Vec<EMLError>,
207}
208
209impl std::fmt::Display for MultipleEMLErrors {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 writeln!(
212 f,
213 "{} non-fatal error(s) and then: {}",
214 self.errors.len() - 1,
215 self.errors.last().unwrap()
216 )
217 }
218}
219
220impl EMLError {
221 pub(crate) fn invalid_value(
223 field: OwnedQualifiedName,
224 source: impl std::error::Error + Send + Sync + 'static,
225 span: Option<Span>,
226 ) -> Self {
227 let kind = EMLErrorKind::InvalidValue(field, Box::new(source));
228 if let Some(span) = span {
229 EMLError::Positioned { kind, span }
230 } else {
231 EMLError::UnknownPosition { kind }
232 }
233 }
234
235 pub fn custom(source: impl CustomError) -> Self {
237 EMLErrorKind::Custom(Box::new(source)).without_span()
238 }
239
240 pub(crate) fn from_vec(errors: Vec<EMLError>) -> Self {
242 if errors.len() == 1 {
243 errors
244 .into_iter()
245 .next()
246 .expect("Vec must have one element")
247 } else {
248 EMLError::Multiple(MultipleEMLErrors { errors })
249 }
250 }
251
252 pub(crate) fn from_vec_with_additional(mut errors: Vec<EMLError>, error: EMLError) -> Self {
254 errors.push(error);
255 Self::from_vec(errors)
256 }
257
258 pub fn kind(&self) -> &EMLErrorKind {
264 match self {
265 EMLError::Positioned { kind, .. } => kind,
266 EMLError::UnknownPosition { kind } => kind,
267 EMLError::Multiple(MultipleEMLErrors { errors }) => errors
268 .last()
269 .map(|e| e.kind())
270 .expect("Errors vec cannot be empty"),
271 }
272 }
273
274 pub fn into_kind(self) -> EMLErrorKind {
278 match self {
279 EMLError::Positioned { kind, .. } => kind,
280 EMLError::UnknownPosition { kind } => kind,
281 EMLError::Multiple(MultipleEMLErrors { errors }) => errors
282 .into_iter()
283 .last()
284 .map(|e| e.into_kind())
285 .expect("Errors vec cannot be empty"),
286 }
287 }
288
289 pub fn span(&self) -> Option<Span> {
293 match self {
294 EMLError::Positioned { span, .. } => Some(*span),
295 EMLError::UnknownPosition { .. } => None,
296 EMLError::Multiple(MultipleEMLErrors { errors }) => {
297 errors.last().and_then(|e| e.span())
298 }
299 }
300 }
301}
302
303pub(crate) trait EMLResultExt<T> {
305 fn with_span(self, span: Span) -> Result<T, EMLError>;
307 fn without_span(self) -> Result<T, EMLError>;
309}
310
311impl<T, I> EMLResultExt<T> for Result<T, I>
312where
313 I: Into<EMLErrorKind>,
314{
315 fn with_span(self, span: Span) -> Result<T, EMLError> {
316 self.map_err(|kind| EMLError::Positioned {
317 kind: kind.into(),
318 span,
319 })
320 }
321
322 fn without_span(self) -> Result<T, EMLError> {
323 self.map_err(|kind| EMLError::UnknownPosition { kind: kind.into() })
324 }
325}
326
327pub(crate) trait EMLValueResultExt<T> {
330 fn wrap_field_value_error(
332 self,
333 element_name: impl Into<OwnedQualifiedName>,
334 ) -> Result<T, EMLError>;
335
336 fn wrap_value_error(self) -> Result<T, EMLError>;
338}
339
340impl<T, I> EMLValueResultExt<T> for Result<T, I>
341where
342 I: std::error::Error + Send + Sync + 'static,
343{
344 fn wrap_field_value_error(
345 self,
346 element_name: impl Into<OwnedQualifiedName>,
347 ) -> Result<T, EMLError> {
348 self.map_err(|e| EMLError::invalid_value(element_name.into(), Box::new(e), None))
349 }
350
351 fn wrap_value_error(self) -> Result<T, EMLError> {
352 self.map_err(|e| EMLErrorKind::ValueConversionError(Box::new(e)).without_span())
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359 use crate::NS_EML;
360
361 #[test]
362 fn test_creating_invalid_value_error() {
363 let error = EMLError::invalid_value(
364 OwnedQualifiedName::from_static("Test", Some(NS_EML)),
365 std::io::Error::other("error"),
366 None,
367 );
368
369 assert!(matches!(
370 error,
371 EMLError::UnknownPosition {
372 kind: EMLErrorKind::InvalidValue(_, _)
373 }
374 ));
375
376 let error_with_span = EMLError::invalid_value(
377 OwnedQualifiedName::from_static("Test", Some(NS_EML)),
378 std::io::Error::other("error"),
379 Some(Span { start: 10, end: 20 }),
380 );
381
382 assert!(matches!(
383 error_with_span,
384 EMLError::Positioned {
385 kind: EMLErrorKind::InvalidValue(_, _),
386 span: Span { start: 10, end: 20 }
387 }
388 ));
389 }
390
391 #[test]
392 fn test_creating_multiple_errors() {
393 let err1 = EMLErrorKind::UnexpectedEndElement.with_span(Span { start: 0, end: 5 });
394 let err2 =
395 EMLErrorKind::MissingElement(OwnedQualifiedName::from_static("Test", Some(NS_EML)))
396 .with_span(Span { start: 10, end: 15 });
397
398 let multiple_error = EMLError::from_vec_with_additional(vec![err1], err2);
399 assert!(matches!(multiple_error, EMLError::Multiple(_)));
400
401 let err3 = EMLErrorKind::UnexpectedEof.with_span(Span { start: 0, end: 10 });
402 let multiple_error2 = EMLError::from_vec_with_additional(vec![], err3);
403 assert!(matches!(
404 multiple_error2,
405 EMLError::Positioned {
406 kind: EMLErrorKind::UnexpectedEof,
407 span: Span { start: 0, end: 10 }
408 }
409 ));
410 }
411
412 #[test]
413 fn get_data_from_error() {
414 let err = EMLErrorKind::UnexpectedEof.with_span(Span { start: 0, end: 10 });
415 assert!(matches!(err.kind(), &EMLErrorKind::UnexpectedEof));
416 assert_eq!(err.span(), Some(Span { start: 0, end: 10 }));
417
418 let err2 = EMLError::UnknownPosition {
419 kind: EMLErrorKind::UnexpectedElement(
420 OwnedQualifiedName::from_static("Test", None),
421 OwnedQualifiedName::from_static("Test", None),
422 ),
423 };
424
425 assert!(matches!(
426 err2.kind(),
427 &EMLErrorKind::UnexpectedElement(_, _)
428 ));
429 assert_eq!(err2.span(), None);
430
431 let err3 = EMLError::Multiple(MultipleEMLErrors {
432 errors: vec![
433 EMLError::Positioned {
434 kind: EMLErrorKind::UnexpectedElement(
435 OwnedQualifiedName::from_static("Test", None),
436 OwnedQualifiedName::from_static("Test", None),
437 ),
438 span: Span { start: 0, end: 5 },
439 },
440 EMLError::UnknownPosition {
441 kind: EMLErrorKind::MissingElement(OwnedQualifiedName::from_static(
442 "Test", None,
443 )),
444 },
445 ],
446 });
447 assert!(matches!(err3.kind(), &EMLErrorKind::MissingElement(_)));
448 assert_eq!(err3.span(), None);
449 }
450}