1use core::convert::Infallible;
10use core::fmt;
11#[cfg(feature = "std")]
12use std::error::Error as StdError;
13#[cfg(all(not(feature = "std"), feature = "newer-rust-version"))]
14if_rust_version::if_rust_version! {
15 >= 1.81 {
16 use core::error::Error as StdError;
17 }
18}
19
20#[cfg(feature = "std")]
21macro_rules! if_std_error {
22 ({ $($if_yes:tt)* } $(else { $($if_not:tt)* })?) => {
23 #[cfg_attr(docsrs, doc(cfg(any(feature = "std", all(feature = "newer-rust-version", rust_version = ">= 1.81.0")))))]
24 $($if_yes)*
25 }
26}
27
28#[cfg(all(not(feature = "std"), feature = "newer-rust-version"))]
29macro_rules! if_std_error {
30 ({ $($if_yes:tt)* } $(else { $($if_not:tt)* })?) => {
31 if_rust_version::if_rust_version! {
32 >= 1.81 {
33 #[cfg_attr(docsrs, doc(cfg(any(feature = "std", all(feature = "newer-rust-version", rust_version = ">= 1.81.0")))))]
34 $($if_yes)*
35 } $(else { $($if_not)* })?
36 }
37 }
38}
39
40#[cfg(all(not(feature = "std"), not(feature = "newer-rust-version")))]
41macro_rules! if_std_error {
42 ({ $($if_yes:tt)* } $(else { $($if_not:tt)* })?) => {
43 $($($if_not)*)?
44 }
45}
46
47macro_rules! write_err {
53 ($writer:expr, $string:literal $(, $args:expr)*; $source:expr) => {
54 {
55 if_std_error! {
56 {
57 {
58 let _ = &$source; write!($writer, $string $(, $args)*)
60 }
61 } else {
62 {
63 write!($writer, concat!($string, ": {}") $(, $args)*, $source)
64 }
65 }
66 }
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum DecodeVariableLengthBytesError {
80 InvalidChar(InvalidCharError),
82 OddLengthString(OddLengthStringError),
84}
85
86impl DecodeVariableLengthBytesError {
87 #[must_use]
101 #[inline]
102 pub fn offset(self, by_bytes: usize) -> Self {
103 use DecodeVariableLengthBytesError as E;
104
105 match self {
106 E::InvalidChar(e) => E::InvalidChar(e.offset(by_bytes)),
107 E::OddLengthString(e) => E::OddLengthString(e),
108 }
109 }
110}
111
112impl From<Infallible> for DecodeVariableLengthBytesError {
113 #[inline]
114 fn from(never: Infallible) -> Self { match never {} }
115}
116
117impl fmt::Display for DecodeVariableLengthBytesError {
118 #[inline]
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 use DecodeVariableLengthBytesError as E;
121
122 match *self {
123 E::InvalidChar(ref e) => write_err!(f, "failed to decode hex"; e),
124 E::OddLengthString(ref e) => write_err!(f, "failed to decode hex"; e),
125 }
126 }
127}
128
129if_std_error! {{
130 impl StdError for DecodeVariableLengthBytesError {
131 #[inline]
132 fn source(&self) -> Option<&(dyn StdError + 'static)> {
133 use DecodeVariableLengthBytesError as E;
134
135 match *self {
136 E::InvalidChar(ref e) => Some(e),
137 E::OddLengthString(ref e) => Some(e),
138 }
139 }
140 }
141}}
142
143impl From<InvalidCharError> for DecodeVariableLengthBytesError {
144 #[inline]
145 fn from(e: InvalidCharError) -> Self { Self::InvalidChar(e) }
146}
147
148impl From<OddLengthStringError> for DecodeVariableLengthBytesError {
149 #[inline]
150 fn from(e: OddLengthStringError) -> Self { Self::OddLengthString(e) }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq)]
155pub struct InvalidCharError {
156 pub(crate) invalid: u8,
157 pub(crate) pos: usize,
158}
159
160impl InvalidCharError {
161 #[inline]
163 pub(crate) fn invalid_char(&self) -> u8 { self.invalid }
164 #[inline]
166 pub fn pos(&self) -> usize { self.pos }
167
168 #[must_use]
186 #[inline]
187 pub fn offset(mut self, by_bytes: usize) -> Self {
188 self.pos += by_bytes;
189 self
190 }
191}
192
193impl From<Infallible> for InvalidCharError {
194 #[inline]
195 fn from(never: Infallible) -> Self { match never {} }
196}
197
198impl fmt::Display for InvalidCharError {
201 #[inline]
202 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203 struct Format<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(F);
209 impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Display for Format<F> {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0(f) }
211 }
212
213 let which;
215 let which: &dyn fmt::Display = match self.pos() {
216 0 => &"1st",
217 1 => &"2nd",
218 2 => &"3rd",
219 pos => {
220 which = Format(move |f| write!(f, "{}th", pos + 1));
221 &which
222 }
223 };
224
225 let chr_ascii;
227 let chr_non_ascii;
228
229 let invalid_char = self.invalid_char();
230 let chr: &dyn fmt::Display = if self.invalid_char().is_ascii() {
233 chr_ascii = Format(move |f| write!(f, "{:?}", invalid_char as char));
238 &chr_ascii
239 } else {
240 chr_non_ascii = Format(move |f| write!(f, "{:#02x}", invalid_char));
241 &chr_non_ascii
242 };
243
244 write!(f, "the {} character, {}, is not a valid hex digit", which, chr)
245 }
246}
247
248if_std_error! {{
249 impl StdError for InvalidCharError {
250 #[inline]
251 fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
252 }
253}}
254
255#[derive(Debug, Clone, PartialEq, Eq)]
257pub struct OddLengthStringError {
258 pub(crate) len: usize,
259}
260
261impl OddLengthStringError {
262 #[inline]
264 pub fn length(&self) -> usize { self.len }
265}
266
267impl From<Infallible> for OddLengthStringError {
268 #[inline]
269 fn from(never: Infallible) -> Self { match never {} }
270}
271
272impl fmt::Display for OddLengthStringError {
273 #[inline]
274 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275 if self.length() == 1 {
276 write!(f, "the hex string is 1 byte long which is not an even number")
277 } else {
278 write!(f, "the hex string is {} bytes long which is not an even number", self.length())
279 }
280 }
281}
282
283if_std_error! {{
284 impl StdError for OddLengthStringError {
285 #[inline]
286 fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
287 }
288}}
289
290#[derive(Debug, Clone, PartialEq, Eq)]
295pub enum DecodeFixedLengthBytesError {
296 InvalidChar(InvalidCharError),
298 InvalidLength(InvalidLengthError),
300}
301
302impl DecodeFixedLengthBytesError {
303 #[must_use]
317 #[inline]
318 pub fn offset(self, by_bytes: usize) -> Self {
319 use DecodeFixedLengthBytesError as E;
320
321 match self {
322 E::InvalidChar(e) => E::InvalidChar(e.offset(by_bytes)),
323 E::InvalidLength(e) => E::InvalidLength(e),
324 }
325 }
326}
327
328impl From<Infallible> for DecodeFixedLengthBytesError {
329 #[inline]
330 fn from(never: Infallible) -> Self { match never {} }
331}
332
333impl fmt::Display for DecodeFixedLengthBytesError {
334 #[inline]
335 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
336 use DecodeFixedLengthBytesError as E;
337
338 match *self {
339 E::InvalidChar(ref e) => write_err!(f, "failed to parse hex"; e),
340 E::InvalidLength(ref e) => write_err!(f, "failed to parse hex"; e),
341 }
342 }
343}
344
345if_std_error! {{
346 impl StdError for DecodeFixedLengthBytesError {
347 #[inline]
348 fn source(&self) -> Option<&(dyn StdError + 'static)> {
349 use DecodeFixedLengthBytesError as E;
350
351 match *self {
352 E::InvalidChar(ref e) => Some(e),
353 E::InvalidLength(ref e) => Some(e),
354 }
355 }
356 }
357}}
358
359impl From<InvalidCharError> for DecodeFixedLengthBytesError {
360 #[inline]
361 fn from(e: InvalidCharError) -> Self { Self::InvalidChar(e) }
362}
363
364impl From<InvalidLengthError> for DecodeFixedLengthBytesError {
365 #[inline]
366 fn from(e: InvalidLengthError) -> Self { Self::InvalidLength(e) }
367}
368
369#[derive(Debug, Clone, PartialEq, Eq)]
371pub struct InvalidLengthError {
372 pub(crate) expected: usize,
374 pub(crate) invalid: usize,
376}
377
378impl InvalidLengthError {
379 #[inline]
384 pub fn expected_length(&self) -> usize { self.expected }
385
386 #[inline]
391 pub fn invalid_length(&self) -> usize { self.invalid }
392}
393
394impl From<Infallible> for InvalidLengthError {
395 #[inline]
396 fn from(never: Infallible) -> Self { match never {} }
397}
398
399impl fmt::Display for InvalidLengthError {
400 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401 write!(
402 f,
403 "the hex string is {} bytes long but exactly {} bytes were required",
405 self.invalid_length(),
406 self.expected_length()
407 )
408 }
409}
410
411if_std_error! {{
412 impl StdError for InvalidLengthError {
413 #[inline]
414 fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
415 }
416}}
417
418#[cfg(test)]
419#[cfg(feature = "std")]
420mod tests {
421 use super::*;
422 #[cfg(feature = "alloc")]
423 use crate::decode_to_vec;
424 use crate::error::{InvalidCharError, OddLengthStringError};
425 use crate::{decode_to_array, HexToBytesIter, InvalidLengthError};
426
427 fn check_source<T: std::error::Error>(error: &T) {
428 assert!(error.source().is_some());
429 }
430
431 #[cfg(feature = "alloc")]
432 #[test]
433 fn invalid_char_error() {
434 let result = decode_to_vec("12G4");
435 let error = result.unwrap_err();
436 if let DecodeVariableLengthBytesError::InvalidChar(e) = error {
437 assert!(!format!("{}", e).is_empty());
438 assert_eq!(e.invalid_char(), b'G');
439 assert_eq!(e.pos(), 2);
440 } else {
441 panic!("Expected InvalidCharError");
442 }
443 }
444
445 #[cfg(feature = "alloc")]
446 #[test]
447 fn odd_length_string_error() {
448 let result = decode_to_vec("123");
449 let error = result.unwrap_err();
450 assert!(!format!("{}", error).is_empty());
451 check_source(&error);
452 if let DecodeVariableLengthBytesError::OddLengthString(e) = error {
453 assert!(!format!("{}", e).is_empty());
454 assert_eq!(e.length(), 3);
455 } else {
456 panic!("Expected OddLengthStringError");
457 }
458 }
459
460 #[test]
461 fn invalid_length_error() {
462 let result = decode_to_array::<4>("123");
463 let error = result.unwrap_err();
464 assert!(!format!("{}", error).is_empty());
465 check_source(&error);
466 if let DecodeFixedLengthBytesError::InvalidLength(e) = error {
467 assert!(!format!("{}", e).is_empty());
468 assert_eq!(e.expected_length(), 8);
469 assert_eq!(e.invalid_length(), 3);
470 } else {
471 panic!("Expected InvalidLengthError");
472 }
473 }
474
475 #[test]
476 fn to_bytes_error() {
477 let error =
478 DecodeVariableLengthBytesError::OddLengthString(OddLengthStringError { len: 7 });
479 assert!(!format!("{}", error).is_empty());
480 check_source(&error);
481 }
482
483 #[test]
484 fn to_array_error() {
485 let error = DecodeFixedLengthBytesError::InvalidLength(InvalidLengthError {
486 expected: 8,
487 invalid: 7,
488 });
489 assert!(!format!("{}", error).is_empty());
490 check_source(&error);
491 }
492
493 #[test]
494 #[cfg(feature = "alloc")]
495 fn hex_error() {
496 let oddlen = "0123456789abcdef0";
497 let badchar1 = "Z123456789abcdef";
498 let badchar2 = "012Y456789abcdeb";
499 let badchar3 = "«23456789abcdef";
500
501 assert_eq!(decode_to_vec(oddlen).unwrap_err(), OddLengthStringError { len: 17 }.into());
502 assert_eq!(
503 decode_to_array::<4>(oddlen).unwrap_err(),
504 InvalidLengthError { invalid: 17, expected: 8 }.into()
505 );
506 assert_eq!(
507 decode_to_vec(badchar1).unwrap_err(),
508 InvalidCharError { pos: 0, invalid: b'Z' }.into()
509 );
510 assert_eq!(
511 decode_to_vec(badchar2).unwrap_err(),
512 InvalidCharError { pos: 3, invalid: b'Y' }.into()
513 );
514 assert_eq!(
515 decode_to_vec(badchar3).unwrap_err(),
516 InvalidCharError { pos: 0, invalid: 194 }.into()
517 );
518 }
519
520 #[test]
521 fn hex_error_position() {
522 let badpos1 = "Z123456789abcdef";
523 let badpos2 = "012Y456789abcdeb";
524 let badpos3 = "0123456789abcdeZ";
525 let badpos4 = "0123456789abYdef";
526
527 assert_eq!(
528 HexToBytesIter::new(badpos1).unwrap().next().unwrap().unwrap_err(),
529 InvalidCharError { pos: 0, invalid: b'Z' }
530 );
531 assert_eq!(
532 HexToBytesIter::new(badpos2).unwrap().nth(1).unwrap().unwrap_err(),
533 InvalidCharError { pos: 3, invalid: b'Y' }
534 );
535 assert_eq!(
536 HexToBytesIter::new(badpos3).unwrap().next_back().unwrap().unwrap_err(),
537 InvalidCharError { pos: 15, invalid: b'Z' }
538 );
539 assert_eq!(
540 HexToBytesIter::new(badpos4).unwrap().nth_back(1).unwrap().unwrap_err(),
541 InvalidCharError { pos: 12, invalid: b'Y' }
542 );
543 }
544
545 #[test]
546 fn hex_to_array() {
547 let len_sixteen = "0123456789abcdef";
548 assert!(decode_to_array::<8>(len_sixteen).is_ok());
549 }
550
551 #[test]
552 fn hex_to_array_error() {
553 let len_sixteen = "0123456789abcdef";
554 assert_eq!(
555 decode_to_array::<4>(len_sixteen).unwrap_err(),
556 InvalidLengthError { invalid: 16, expected: 8 }.into()
557 );
558 }
559
560 #[test]
561 #[cfg(feature = "alloc")]
562 fn mixed_case() {
563 use crate::display::DisplayHex as _;
564
565 let s = "DEADbeef0123";
566 let want_lower = "deadbeef0123";
567 let want_upper = "DEADBEEF0123";
568
569 let v = decode_to_vec(s).expect("valid hex");
570 assert_eq!(format!("{:x}", v.as_hex()), want_lower);
571 assert_eq!(format!("{:X}", v.as_hex()), want_upper);
572 }
573}