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 #[deprecated(since = "TBD", note = "not suitable for use with UTF-8 strings")]
164 pub fn invalid_char(&self) -> u8 { self.invalid }
165 #[inline]
167 pub fn pos(&self) -> usize { self.pos }
168
169 #[must_use]
187 #[inline]
188 pub fn offset(mut self, by_bytes: usize) -> Self {
189 self.pos += by_bytes;
190 self
191 }
192}
193
194impl From<Infallible> for InvalidCharError {
195 #[inline]
196 fn from(never: Infallible) -> Self { match never {} }
197}
198
199impl fmt::Display for InvalidCharError {
202 #[inline]
203 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204 struct Format<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(F);
210 impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Display for Format<F> {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0(f) }
212 }
213
214 let which;
216 let which: &dyn fmt::Display = match self.pos() {
217 0 => &"1st",
218 1 => &"2nd",
219 2 => &"3rd",
220 pos => {
221 which = Format(move |f| write!(f, "{}th", pos + 1));
222 &which
223 }
224 };
225
226 let chr_ascii;
228 let chr_non_ascii;
229
230 let invalid_char = self.invalid_char();
231 let chr: &dyn fmt::Display = if self.invalid_char().is_ascii() {
234 chr_ascii = Format(move |f| write!(f, "{:?}", invalid_char as char));
239 &chr_ascii
240 } else {
241 chr_non_ascii = Format(move |f| write!(f, "{:#02x}", invalid_char));
242 &chr_non_ascii
243 };
244
245 write!(f, "the {} character, {}, is not a valid hex digit", which, chr)
246 }
247}
248
249if_std_error! {{
250 impl StdError for InvalidCharError {
251 #[inline]
252 fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
253 }
254}}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub struct OddLengthStringError {
259 pub(crate) len: usize,
260}
261
262impl OddLengthStringError {
263 #[inline]
265 pub fn length(&self) -> usize { self.len }
266}
267
268impl From<Infallible> for OddLengthStringError {
269 #[inline]
270 fn from(never: Infallible) -> Self { match never {} }
271}
272
273impl fmt::Display for OddLengthStringError {
274 #[inline]
275 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
276 if self.length() == 1 {
277 write!(f, "the hex string is 1 byte long which is not an even number")
278 } else {
279 write!(f, "the hex string is {} bytes long which is not an even number", self.length())
280 }
281 }
282}
283
284if_std_error! {{
285 impl StdError for OddLengthStringError {
286 #[inline]
287 fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
288 }
289}}
290
291#[derive(Debug, Clone, PartialEq, Eq)]
296pub enum DecodeFixedLengthBytesError {
297 InvalidChar(InvalidCharError),
299 InvalidLength(InvalidLengthError),
301}
302
303impl DecodeFixedLengthBytesError {
304 #[must_use]
318 #[inline]
319 pub fn offset(self, by_bytes: usize) -> Self {
320 use DecodeFixedLengthBytesError as E;
321
322 match self {
323 E::InvalidChar(e) => E::InvalidChar(e.offset(by_bytes)),
324 E::InvalidLength(e) => E::InvalidLength(e),
325 }
326 }
327}
328
329impl From<Infallible> for DecodeFixedLengthBytesError {
330 #[inline]
331 fn from(never: Infallible) -> Self { match never {} }
332}
333
334impl fmt::Display for DecodeFixedLengthBytesError {
335 #[inline]
336 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337 use DecodeFixedLengthBytesError as E;
338
339 match *self {
340 E::InvalidChar(ref e) => write_err!(f, "failed to parse hex"; e),
341 E::InvalidLength(ref e) => write_err!(f, "failed to parse hex"; e),
342 }
343 }
344}
345
346if_std_error! {{
347 impl StdError for DecodeFixedLengthBytesError {
348 #[inline]
349 fn source(&self) -> Option<&(dyn StdError + 'static)> {
350 use DecodeFixedLengthBytesError as E;
351
352 match *self {
353 E::InvalidChar(ref e) => Some(e),
354 E::InvalidLength(ref e) => Some(e),
355 }
356 }
357 }
358}}
359
360impl From<InvalidCharError> for DecodeFixedLengthBytesError {
361 #[inline]
362 fn from(e: InvalidCharError) -> Self { Self::InvalidChar(e) }
363}
364
365impl From<InvalidLengthError> for DecodeFixedLengthBytesError {
366 #[inline]
367 fn from(e: InvalidLengthError) -> Self { Self::InvalidLength(e) }
368}
369
370#[derive(Debug, Clone, PartialEq, Eq)]
372#[non_exhaustive]
373pub struct InvalidLengthError {
374 pub expected: usize,
376 pub invalid: usize,
378}
379
380impl InvalidLengthError {
381 #[inline]
386 pub fn expected_length(&self) -> usize { self.expected }
387
388 #[inline]
393 pub fn invalid_length(&self) -> usize { self.invalid }
394}
395
396impl From<Infallible> for InvalidLengthError {
397 #[inline]
398 fn from(never: Infallible) -> Self { match never {} }
399}
400
401impl fmt::Display for InvalidLengthError {
402 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
403 write!(
404 f,
405 "the hex string is {} bytes long but exactly {} bytes were required",
407 self.invalid_length(),
408 self.expected_length()
409 )
410 }
411}
412
413if_std_error! {{
414 impl StdError for InvalidLengthError {
415 #[inline]
416 fn source(&self) -> Option<&(dyn StdError + 'static)> { None }
417 }
418}}
419
420#[cfg(test)]
421#[cfg(feature = "std")]
422mod tests {
423 use super::*;
424 #[cfg(feature = "alloc")]
425 use crate::decode_to_vec;
426 use crate::error::{InvalidCharError, OddLengthStringError};
427 use crate::{decode_to_array, HexToBytesIter, InvalidLengthError};
428
429 fn check_source<T: std::error::Error>(error: &T) {
430 assert!(error.source().is_some());
431 }
432
433 #[cfg(feature = "alloc")]
434 #[test]
435 fn invalid_char_error() {
436 let result = decode_to_vec("12G4");
437 let error = result.unwrap_err();
438 if let DecodeVariableLengthBytesError::InvalidChar(e) = error {
439 assert!(!format!("{}", e).is_empty());
440 assert_eq!(e.invalid_char(), b'G');
441 assert_eq!(e.pos(), 2);
442 } else {
443 panic!("Expected InvalidCharError");
444 }
445 }
446
447 #[cfg(feature = "alloc")]
448 #[test]
449 fn odd_length_string_error() {
450 let result = decode_to_vec("123");
451 let error = result.unwrap_err();
452 assert!(!format!("{}", error).is_empty());
453 check_source(&error);
454 if let DecodeVariableLengthBytesError::OddLengthString(e) = error {
455 assert!(!format!("{}", e).is_empty());
456 assert_eq!(e.length(), 3);
457 } else {
458 panic!("Expected OddLengthStringError");
459 }
460 }
461
462 #[test]
463 fn invalid_length_error() {
464 let result = decode_to_array::<4>("123");
465 let error = result.unwrap_err();
466 assert!(!format!("{}", error).is_empty());
467 check_source(&error);
468 if let DecodeFixedLengthBytesError::InvalidLength(e) = error {
469 assert!(!format!("{}", e).is_empty());
470 assert_eq!(e.expected_length(), 8);
471 assert_eq!(e.invalid_length(), 3);
472 } else {
473 panic!("Expected InvalidLengthError");
474 }
475 }
476
477 #[test]
478 fn to_bytes_error() {
479 let error =
480 DecodeVariableLengthBytesError::OddLengthString(OddLengthStringError { len: 7 });
481 assert!(!format!("{}", error).is_empty());
482 check_source(&error);
483 }
484
485 #[test]
486 fn to_array_error() {
487 let error = DecodeFixedLengthBytesError::InvalidLength(InvalidLengthError {
488 expected: 8,
489 invalid: 7,
490 });
491 assert!(!format!("{}", error).is_empty());
492 check_source(&error);
493 }
494
495 #[test]
496 #[cfg(feature = "alloc")]
497 fn hex_error() {
498 let oddlen = "0123456789abcdef0";
499 let badchar1 = "Z123456789abcdef";
500 let badchar2 = "012Y456789abcdeb";
501 let badchar3 = "«23456789abcdef";
502
503 assert_eq!(decode_to_vec(oddlen).unwrap_err(), OddLengthStringError { len: 17 }.into());
504 assert_eq!(
505 decode_to_array::<4>(oddlen).unwrap_err(),
506 InvalidLengthError { invalid: 17, expected: 8 }.into()
507 );
508 assert_eq!(
509 decode_to_vec(badchar1).unwrap_err(),
510 InvalidCharError { pos: 0, invalid: b'Z' }.into()
511 );
512 assert_eq!(
513 decode_to_vec(badchar2).unwrap_err(),
514 InvalidCharError { pos: 3, invalid: b'Y' }.into()
515 );
516 assert_eq!(
517 decode_to_vec(badchar3).unwrap_err(),
518 InvalidCharError { pos: 0, invalid: 194 }.into()
519 );
520 }
521
522 #[test]
523 fn hex_error_position() {
524 let badpos1 = "Z123456789abcdef";
525 let badpos2 = "012Y456789abcdeb";
526 let badpos3 = "0123456789abcdeZ";
527 let badpos4 = "0123456789abYdef";
528
529 assert_eq!(
530 HexToBytesIter::new(badpos1).unwrap().next().unwrap().unwrap_err(),
531 InvalidCharError { pos: 0, invalid: b'Z' }
532 );
533 assert_eq!(
534 HexToBytesIter::new(badpos2).unwrap().nth(1).unwrap().unwrap_err(),
535 InvalidCharError { pos: 3, invalid: b'Y' }
536 );
537 assert_eq!(
538 HexToBytesIter::new(badpos3).unwrap().next_back().unwrap().unwrap_err(),
539 InvalidCharError { pos: 15, invalid: b'Z' }
540 );
541 assert_eq!(
542 HexToBytesIter::new(badpos4).unwrap().nth_back(1).unwrap().unwrap_err(),
543 InvalidCharError { pos: 12, invalid: b'Y' }
544 );
545 }
546
547 #[test]
548 fn hex_to_array() {
549 let len_sixteen = "0123456789abcdef";
550 assert!(decode_to_array::<8>(len_sixteen).is_ok());
551 }
552
553 #[test]
554 fn hex_to_array_error() {
555 let len_sixteen = "0123456789abcdef";
556 assert_eq!(
557 decode_to_array::<4>(len_sixteen).unwrap_err(),
558 InvalidLengthError { invalid: 16, expected: 8 }.into()
559 );
560 }
561
562 #[test]
563 #[cfg(feature = "alloc")]
564 fn mixed_case() {
565 use crate::display::DisplayHex as _;
566
567 let s = "DEADbeef0123";
568 let want_lower = "deadbeef0123";
569 let want_upper = "DEADBEEF0123";
570
571 let v = decode_to_vec(s).expect("valid hex");
572 assert_eq!(format!("{:x}", v.as_hex()), want_lower);
573 assert_eq!(format!("{:X}", v.as_hex()), want_upper);
574 }
575}